Skip to content

Commit bc7ea0b

Browse files
committed
Dependency install with cancellation support
1 parent 7025fcb commit bc7ea0b

8 files changed

Lines changed: 111 additions & 43 deletions

File tree

plugin_KinectOne/Assets/Strings/de.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
{
4949
"id": "/Plugins/KinectOne/Stages/Dark/Error/Result",
5050
"translation": "Dark.exe exited with error code: {0}"
51+
},
52+
{
53+
"id": "/Plugins/KinectOne/Dependencies/Runtime/Name",
54+
"translation": "Kinect for Xbox One Runtime"
5155
}
5256
]
5357
}

plugin_KinectOne/Assets/Strings/en.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
{
4949
"id": "/Plugins/KinectOne/Stages/Dark/Error/Result",
5050
"translation": "Dark.exe exited with error code: {0}"
51+
},
52+
{
53+
"id": "/Plugins/KinectOne/Dependencies/Runtime/Name",
54+
"translation": "Kinect for Xbox One Runtime"
5155
}
5256
]
5357
}

plugin_KinectOne/Assets/Strings/es.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
{
4949
"id": "/Plugins/KinectOne/Stages/Dark/Error/Result",
5050
"translation": "Dark.exe exited with error code: {0}"
51+
},
52+
{
53+
"id": "/Plugins/KinectOne/Dependencies/Runtime/Name",
54+
"translation": "Kinect for Xbox One Runtime"
5155
}
5256
]
5357
}

plugin_KinectOne/Assets/Strings/fr.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
{
4949
"id": "/Plugins/KinectOne/Stages/Dark/Error/Result",
5050
"translation": "Dark.exe exited with error code: {0}"
51+
},
52+
{
53+
"id": "/Plugins/KinectOne/Dependencies/Runtime/Name",
54+
"translation": "Kinect for Xbox One Runtime"
5155
}
5256
]
5357
}

plugin_KinectOne/Assets/Strings/it.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
{
4949
"id": "/Plugins/KinectOne/Stages/Dark/Error/Result",
5050
"translation": "Dark.exe exited with error code: {0}"
51+
},
52+
{
53+
"id": "/Plugins/KinectOne/Dependencies/Runtime/Name",
54+
"translation": "Kinect for Xbox One Runtime"
5155
}
5256
]
5357
}

plugin_KinectOne/Assets/Strings/ru.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
{
4949
"id": "/Plugins/KinectOne/Stages/Dark/Error/Result",
5050
"translation": "Dark.exe exited with error code: {0}"
51+
},
52+
{
53+
"id": "/Plugins/KinectOne/Dependencies/Runtime/Name",
54+
"translation": "Kinect for Xbox One Runtime"
5155
}
5256
]
5357
}

plugin_KinectOne/RuntimeInstaller.cs

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.IO.Compression;
66
using System.Reflection;
77
using System.Text;
8+
using System.Threading;
89
using System.Threading.Tasks;
910
using Windows.Storage;
1011
using Amethyst.Plugins.Contract;
@@ -27,6 +28,24 @@ internal class SetupData : ICoreSetupData
2728
}
2829

2930
internal class RuntimeInstaller : IDependencyInstaller
31+
{
32+
public IDependencyInstaller.ILocalizationHost Host { get; set; }
33+
34+
public List<IDependency> ListDependencies()
35+
{
36+
return new List<IDependency>
37+
{
38+
new KinectRuntime
39+
{
40+
Host = Host,
41+
Name = Host?.RequestLocalizedString("/Plugins/KinectOne/Dependencies/Runtime/Name") ??
42+
"Kinect for Xbox One Runtime"
43+
}
44+
};
45+
}
46+
}
47+
48+
internal class KinectRuntime : IDependency
3049
{
3150
private const string WixDownloadUrl =
3251
"https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
@@ -36,10 +55,10 @@ internal class RuntimeInstaller : IDependencyInstaller
3655

3756
private string TemporaryFolderName { get; } = Guid.NewGuid().ToString().ToUpper();
3857

39-
public Task<bool> InstallTools(IProgress<InstallationProgress> progress)
40-
{
41-
return Task.FromResult(false); // Not supported (yet?)
42-
}
58+
public IDependencyInstaller.ILocalizationHost Host { get; set; }
59+
60+
public string Name { get; set; }
61+
public bool IsMandatory => true;
4362

4463
public bool IsInstalled
4564
{
@@ -76,18 +95,17 @@ public string InstallerEula
7695
}
7796
}
7897

79-
public bool ProvidesTools => false;
80-
public bool ToolsInstalled => false;
81-
public IDependencyInstaller.ILocalizationHost Host { get; set; }
82-
83-
public async Task<bool> Install(IProgress<InstallationProgress> progress)
98+
public async Task<bool> Install(IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
8499
{
100+
// Amethyst will handle this exception for us anyway
101+
cancellationToken.ThrowIfCancellationRequested();
102+
85103
return
86104
// Download and unpack WiX
87-
await SetupWix("WiXToolset", progress) &&
105+
await SetupWix("WiXToolset", progress, cancellationToken) &&
88106

89107
// Download, unpack, and install the runtime
90-
await SetupRuntime("WiXToolset", progress);
108+
await SetupRuntime("WiXToolset", progress, cancellationToken);
91109
}
92110

93111
private async Task<StorageFolder> GetTempDirectory()
@@ -96,8 +114,12 @@ private async Task<StorageFolder> GetTempDirectory()
96114
TemporaryFolderName, CreationCollisionOption.OpenIfExists);
97115
}
98116

99-
private async Task<bool> SetupWix(string outputFolder, IProgress<InstallationProgress> progress)
117+
private async Task<bool> SetupWix(string outputFolder,
118+
IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
100119
{
120+
// Amethyst will handle this exception for us anyway
121+
cancellationToken.ThrowIfCancellationRequested();
122+
101123
try
102124
{
103125
using var client = new RestClient();
@@ -118,15 +140,17 @@ private async Task<bool> SetupWix(string outputFolder, IProgress<InstallationPro
118140

119141
// Create an output stream and push all the available data to it
120142
await using var fsInstallerFile = await installerFile.OpenStreamForWriteAsync();
121-
await stream.CopyToWithProgressAsync(fsInstallerFile, innerProgress =>
122-
{
123-
progress.Report(new InstallationProgress
143+
await stream.CopyToWithProgressAsync(fsInstallerFile, cancellationToken,
144+
innerProgress =>
124145
{
125-
IsIndeterminate = false, OverallProgress = innerProgress / 34670186.0,
126-
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/WiX") ??
127-
"Downloading WiX Toolset"
128-
});
129-
}); // The runtime will do the rest for us
146+
progress.Report(new InstallationProgress
147+
{
148+
IsIndeterminate = false,
149+
OverallProgress = innerProgress / 34670186.0,
150+
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/WiX") ??
151+
"Downloading WiX Toolset"
152+
});
153+
}); // The runtime will do the rest for us
130154

131155
// Close the file to unlock it
132156
fsInstallerFile.Close();
@@ -174,8 +198,12 @@ await stream.CopyToWithProgressAsync(fsInstallerFile, innerProgress =>
174198
return false;
175199
}
176200

177-
private async Task<bool> SetupRuntime(string wixFolder, IProgress<InstallationProgress> progress)
201+
private async Task<bool> SetupRuntime(string wixFolder,
202+
IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
178203
{
204+
// Amethyst will handle this exception for us anyway
205+
cancellationToken.ThrowIfCancellationRequested();
206+
179207
try
180208
{
181209
using var client = new RestClient();
@@ -196,28 +224,31 @@ private async Task<bool> SetupRuntime(string wixFolder, IProgress<InstallationPr
196224

197225
// Create an output stream and push all the available data to it
198226
await using var fsInstallerFile = await installerFile.OpenStreamForWriteAsync();
199-
await stream.CopyToWithProgressAsync(fsInstallerFile, innerProgress =>
200-
{
201-
progress.Report(new InstallationProgress
227+
await stream.CopyToWithProgressAsync(fsInstallerFile, cancellationToken,
228+
innerProgress =>
202229
{
203-
IsIndeterminate = false,
204-
OverallProgress = innerProgress / 93314296.0,
205-
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/Runtime") ??
206-
"Downloading Kinect for Xbox One Runtime..."
207-
});
208-
}); // The runtime will do the rest for us
230+
progress.Report(new InstallationProgress
231+
{
232+
IsIndeterminate = false,
233+
OverallProgress = innerProgress / 93314296.0,
234+
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/Runtime") ??
235+
"Downloading Kinect for Xbox One Runtime..."
236+
});
237+
}); // The runtime will do the rest for us
209238

210239
// Close the file to unlock it
211240
fsInstallerFile.Close();
212241

213242
return
214243
// Extract all runtime files for the installation
215244
await ExtractFiles(Path.GetFullPath(Path.Combine((await GetTempDirectory()).Path, wixFolder)),
216-
installerFile.Path, Path.Join((await GetTempDirectory()).Path, "KinectRuntime"), progress) &&
245+
installerFile.Path, Path.Join((await GetTempDirectory()).Path, "KinectRuntime"),
246+
progress, cancellationToken) &&
217247

218248
// Install the files using msi installers
219249
InstallFiles(Directory.GetFiles(Path.Join((await GetTempDirectory()).Path,
220-
"KinectRuntime", "AttachedContainer"), "*.msi"), progress);
250+
"KinectRuntime", "AttachedContainer"), "*.msi"),
251+
progress, cancellationToken);
221252
}
222253
catch (Exception e)
223254
{
@@ -233,8 +264,11 @@ await ExtractFiles(Path.GetFullPath(Path.Combine((await GetTempDirectory()).Path
233264
}
234265

235266
private async Task<bool> ExtractFiles(string wixPath, string sourceFile, string outputFolder,
236-
IProgress<InstallationProgress> progress)
267+
IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
237268
{
269+
// Amethyst will handle this exception for us anyway
270+
cancellationToken.ThrowIfCancellationRequested();
271+
238272
try
239273
{
240274
progress.Report(new InstallationProgress
@@ -293,7 +327,8 @@ private async Task<bool> ExtractFiles(string wixPath, string sourceFile, string
293327
// WTF
294328
progress.Report(new InstallationProgress
295329
{
296-
IsIndeterminate = true, StageTitle =
330+
IsIndeterminate = true,
331+
StageTitle =
297332
Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Dark/Error/Timeout") ??
298333
"Failed to execute dark.exe in the allocated time!"
299334
});
@@ -306,7 +341,8 @@ private async Task<bool> ExtractFiles(string wixPath, string sourceFile, string
306341
// Assume WiX failed
307342
progress.Report(new InstallationProgress
308343
{
309-
IsIndeterminate = true, StageTitle =
344+
IsIndeterminate = true,
345+
StageTitle =
310346
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Dark/Error/Result") ??
311347
"Dark.exe exited with error code: {0}").Replace("{0}", proc.ExitCode.ToString())
312348
});
@@ -318,7 +354,8 @@ private async Task<bool> ExtractFiles(string wixPath, string sourceFile, string
318354
{
319355
progress.Report(new InstallationProgress
320356
{
321-
IsIndeterminate = true, StageTitle =
357+
IsIndeterminate = true,
358+
StageTitle =
322359
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Exceptions/Other") ??
323360
"Exception: {0}").Replace("{0}", e.Message)
324361
});
@@ -329,16 +366,21 @@ private async Task<bool> ExtractFiles(string wixPath, string sourceFile, string
329366
return true;
330367
}
331368

332-
private bool InstallFiles(IEnumerable<string> files, IProgress<InstallationProgress> progress)
369+
private bool InstallFiles(IEnumerable<string> files,
370+
IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
333371
{
372+
// Amethyst will handle this exception for us anyway
373+
cancellationToken.ThrowIfCancellationRequested();
374+
334375
// Execute each install
335376
foreach (var installFile in files)
336377
try
337378
{
338379
// msi /qn /norestart
339380
progress.Report(new InstallationProgress
340381
{
341-
IsIndeterminate = true, StageTitle =
382+
IsIndeterminate = true,
383+
StageTitle =
342384
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Installing") ??
343385
"Installing {0}...").Replace("{0}", Path.GetFileName(installFile))
344386
});
@@ -362,7 +404,8 @@ private bool InstallFiles(IEnumerable<string> files, IProgress<InstallationProgr
362404
{
363405
progress.Report(new InstallationProgress
364406
{
365-
IsIndeterminate = true, StageTitle =
407+
IsIndeterminate = true,
408+
StageTitle =
366409
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Exceptions/Other") ??
367410
"Exception: {0}").Replace("{0}", e.Message)
368411
});
@@ -392,7 +435,8 @@ public static Task<Stream> ExecuteDownloadStreamAsync(this RestClient client, st
392435
public static class StreamExtensions
393436
{
394437
public static async Task CopyToWithProgressAsync(this Stream source,
395-
Stream destination, Action<long> progress = null, int bufferSize = 10240)
438+
Stream destination, CancellationToken cancellationToken,
439+
Action<long> progress = null, int bufferSize = 10240)
396440
{
397441
var buffer = new byte[bufferSize];
398442
var total = 0L;
@@ -404,13 +448,13 @@ public static async Task CopyToWithProgressAsync(this Stream source,
404448
while (amtRead < bufferSize)
405449
{
406450
var numBytes = await source.ReadAsync(
407-
buffer, amtRead, bufferSize - amtRead);
451+
buffer, amtRead, bufferSize - amtRead, cancellationToken);
408452
if (numBytes == 0) break;
409453
amtRead += numBytes;
410454
}
411455

412456
total += amtRead;
413-
await destination.WriteAsync(buffer, 0, amtRead);
457+
await destination.WriteAsync(buffer, 0, amtRead, cancellationToken);
414458
progress?.Invoke(total);
415459
} while (amtRead == bufferSize);
416460
}

plugin_KinectOne/plugin_KinectOne.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
</PropertyGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Amethyst.Plugins.Contract" Version="0.2.18" />
16+
<PackageReference Include="Amethyst.Plugins.Contract" Version="0.2.21" />
1717
<PackageReference Include="Microsoft.Kinect" Version="2.0.1410.19000" />
1818
<PackageReference Include="RestSharp" Version="108.0.3" />
1919
<PackageReference Include="System.ComponentModel.Composition" Version="8.0.0-preview.3.23174.8" />

0 commit comments

Comments
 (0)