Skip to content

Commit a32e23f

Browse files
committed
Offline runtime installer (as required by the store)
1 parent c4c2193 commit a32e23f

3 files changed

Lines changed: 9 additions & 272 deletions

File tree

Binary file not shown.

plugin_KinectOne/RuntimeInstaller.cs

Lines changed: 5 additions & 272 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,6 @@ public List<IDependency> ListDependencies()
4848

4949
internal class KinectRuntime : IDependency
5050
{
51-
private const string WixDownloadUrl =
52-
"https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip";
53-
54-
private const string RuntimeDownloadUrl =
55-
"https://download.microsoft.com/download/A/7/4/A74239EB-22C2-45A1-996C-2F8E564B28ED/KinectRuntime-v2.0_1409-Setup.exe";
56-
57-
private string TemporaryFolderName { get; } = Guid.NewGuid().ToString().ToUpper();
58-
5951
public IDependencyInstaller.ILocalizationHost Host { get; set; }
6052

6153
public string Name { get; set; }
@@ -96,275 +88,16 @@ public string InstallerEula
9688
}
9789
}
9890

99-
public async Task<bool> Install(IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
91+
public Task<bool> Install(IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
10092
{
10193
// Amethyst will handle this exception for us anyway
10294
cancellationToken.ThrowIfCancellationRequested();
10395

104-
return
105-
// Download and unpack WiX
106-
await SetupWix("WiXToolset", progress, cancellationToken) &&
107-
108-
// Download, unpack, and install the runtime
109-
await SetupRuntime("WiXToolset", progress, cancellationToken);
110-
}
111-
112-
private async Task<StorageFolder> GetTempDirectory()
113-
{
114-
return await ApplicationData.Current.TemporaryFolder.CreateFolderAsync(
115-
TemporaryFolderName, CreationCollisionOption.OpenIfExists);
116-
}
117-
118-
private async Task<bool> SetupWix(string outputFolder,
119-
IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
120-
{
121-
// Amethyst will handle this exception for us anyway
122-
cancellationToken.ThrowIfCancellationRequested();
123-
124-
try
96+
return Task.FromResult(InstallFiles(new[]
12597
{
126-
using var client = new RestClient();
127-
progress.Report(new InstallationProgress
128-
{
129-
IsIndeterminate = true,
130-
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/WiX") ??
131-
"Downloading WiX Toolset"
132-
});
133-
134-
// Create a stream reader using the received Installer Uri
135-
await using var stream =
136-
await client.ExecuteDownloadStreamAsync(WixDownloadUrl, new RestRequest());
137-
138-
// Replace or create our installer file
139-
var installerFile = await (await GetTempDirectory()).CreateFileAsync(
140-
"wix-binaries.zip", CreationCollisionOption.ReplaceExisting);
141-
142-
// Create an output stream and push all the available data to it
143-
await using var fsInstallerFile = await installerFile.OpenStreamForWriteAsync();
144-
await stream.CopyToWithProgressAsync(fsInstallerFile, cancellationToken,
145-
innerProgress =>
146-
{
147-
progress.Report(new InstallationProgress
148-
{
149-
IsIndeterminate = false,
150-
OverallProgress = innerProgress / 34670186.0,
151-
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/WiX") ??
152-
"Downloading WiX Toolset"
153-
});
154-
}); // The runtime will do the rest for us
155-
156-
// Close the file to unlock it
157-
fsInstallerFile.Close();
158-
159-
var sourceZip = Path.GetFullPath(Path.Combine((await GetTempDirectory()).Path, "wix-binaries.zip"));
160-
var tempDirectory = Path.GetFullPath(Path.Combine((await GetTempDirectory()).Path, outputFolder));
161-
162-
if (File.Exists(sourceZip))
163-
{
164-
if (!Directory.Exists(tempDirectory))
165-
Directory.CreateDirectory(tempDirectory);
166-
167-
try
168-
{
169-
// Extract the toolset
170-
ZipFile.ExtractToDirectory(sourceZip, tempDirectory, true);
171-
}
172-
catch (Exception e)
173-
{
174-
progress.Report(new InstallationProgress
175-
{
176-
IsIndeterminate = true,
177-
StageTitle =
178-
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Exceptions/WiX/Extraction") ??
179-
"Toolset extraction failed! Exception: {0}").Replace("{0}", e.Message)
180-
});
181-
182-
return false;
183-
}
184-
185-
return true;
186-
}
187-
}
188-
catch (Exception e)
189-
{
190-
progress.Report(new InstallationProgress
191-
{
192-
IsIndeterminate = true,
193-
StageTitle = (Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Exceptions/WiX/Installation") ??
194-
"Toolset installation failed! Exception: {0}").Replace("{0}", e.Message)
195-
});
196-
return false;
197-
}
198-
199-
return false;
200-
}
201-
202-
private async Task<bool> SetupRuntime(string wixFolder,
203-
IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
204-
{
205-
// Amethyst will handle this exception for us anyway
206-
cancellationToken.ThrowIfCancellationRequested();
207-
208-
try
209-
{
210-
using var client = new RestClient();
211-
progress.Report(new InstallationProgress
212-
{
213-
IsIndeterminate = true,
214-
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/Runtime") ??
215-
"Downloading Kinect for Xbox One Runtime..."
216-
});
217-
218-
// Create a stream reader using the received Installer Uri
219-
await using var stream =
220-
await client.ExecuteDownloadStreamAsync(RuntimeDownloadUrl, new RestRequest());
221-
222-
// Replace or create our installer file
223-
var installerFile = await (await GetTempDirectory()).CreateFileAsync(
224-
"kinect-setup.exe", CreationCollisionOption.ReplaceExisting);
225-
226-
// Create an output stream and push all the available data to it
227-
await using var fsInstallerFile = await installerFile.OpenStreamForWriteAsync();
228-
await stream.CopyToWithProgressAsync(fsInstallerFile, cancellationToken,
229-
innerProgress =>
230-
{
231-
progress.Report(new InstallationProgress
232-
{
233-
IsIndeterminate = false,
234-
OverallProgress = innerProgress / 93314296.0,
235-
StageTitle = Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Downloading/Runtime") ??
236-
"Downloading Kinect for Xbox One Runtime..."
237-
});
238-
}); // The runtime will do the rest for us
239-
240-
// Close the file to unlock it
241-
fsInstallerFile.Close();
242-
243-
return
244-
// Extract all runtime files for the installation
245-
await ExtractFiles(Path.GetFullPath(Path.Combine((await GetTempDirectory()).Path, wixFolder)),
246-
installerFile.Path, Path.Join((await GetTempDirectory()).Path, "KinectRuntime"),
247-
progress, cancellationToken) &&
248-
249-
// Install the files using msi installers
250-
InstallFiles(Directory.GetFiles(Path.Join((await GetTempDirectory()).Path,
251-
"KinectRuntime", "AttachedContainer"), "*.msi"),
252-
progress, cancellationToken);
253-
}
254-
catch (Exception e)
255-
{
256-
progress.Report(new InstallationProgress
257-
{
258-
IsIndeterminate = true,
259-
StageTitle =
260-
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Exceptions/Runtime/Installation") ??
261-
"Runtime installation failed! Exception: {0}").Replace("{0}", e.Message)
262-
});
263-
return false;
264-
}
265-
}
266-
267-
private async Task<bool> ExtractFiles(string wixPath, string sourceFile, string outputFolder,
268-
IProgress<InstallationProgress> progress, CancellationToken cancellationToken)
269-
{
270-
// Amethyst will handle this exception for us anyway
271-
cancellationToken.ThrowIfCancellationRequested();
272-
273-
try
274-
{
275-
progress.Report(new InstallationProgress
276-
{
277-
IsIndeterminate = true,
278-
StageTitle = (Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Unpacking") ??
279-
"Unpacking {0}...").Replace("{0}", Path.GetFileName(sourceFile))
280-
});
281-
282-
// dark.exe {sourceFile} -x {outDir}
283-
var procStart = new ProcessStartInfo
284-
{
285-
FileName = Path.Combine(wixPath, "dark.exe"),
286-
WorkingDirectory = (await GetTempDirectory()).Path,
287-
Arguments = $"\"{sourceFile}\" -x \"{outputFolder}\"",
288-
CreateNoWindow = true,
289-
WindowStyle = ProcessWindowStyle.Hidden,
290-
291-
// Verbose error handling
292-
RedirectStandardOutput = true,
293-
RedirectStandardError = true,
294-
RedirectStandardInput = false,
295-
UseShellExecute = false,
296-
StandardOutputEncoding = Encoding.UTF8,
297-
StandardErrorEncoding = Encoding.UTF8
298-
};
299-
300-
var proc = Process.Start(procStart);
301-
// Redirecting process output so that we can log what happened
302-
var stdout = new StringBuilder();
303-
var stderr = new StringBuilder();
304-
305-
proc!.OutputDataReceived += (_, args) =>
306-
{
307-
if (args.Data != null)
308-
stdout.AppendLine(args.Data);
309-
};
310-
proc.ErrorDataReceived += (_, args) =>
311-
{
312-
if (args.Data != null)
313-
stderr.AppendLine(args.Data);
314-
};
315-
316-
proc.BeginErrorReadLine();
317-
proc.BeginOutputReadLine();
318-
var hasExited = proc.WaitForExit(60000);
319-
320-
// https://github.com/wixtoolset/wix3/blob/6b461364c40e6d1c487043cd0eae7c1a3d15968c/src/tools/dark/dark.cs#L54
321-
// Exit codes for DARK:
322-
//
323-
// 0 - Success
324-
// 1 - Error
325-
// Just in case
326-
if (!hasExited)
327-
{
328-
// WTF
329-
progress.Report(new InstallationProgress
330-
{
331-
IsIndeterminate = true,
332-
StageTitle =
333-
Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Dark/Error/Timeout") ??
334-
"Failed to execute dark.exe in the allocated time!"
335-
});
336-
337-
proc.Kill();
338-
}
339-
340-
if (proc.ExitCode == 1)
341-
{
342-
// Assume WiX failed
343-
progress.Report(new InstallationProgress
344-
{
345-
IsIndeterminate = true,
346-
StageTitle =
347-
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Dark/Error/Result") ??
348-
"Dark.exe exited with error code: {0}").Replace("{0}", proc.ExitCode.ToString())
349-
});
350-
351-
return false;
352-
}
353-
}
354-
catch (Exception e)
355-
{
356-
progress.Report(new InstallationProgress
357-
{
358-
IsIndeterminate = true,
359-
StageTitle =
360-
(Host?.RequestLocalizedString("/Plugins/KinectOne/Stages/Exceptions/Other") ??
361-
"Exception: {0}").Replace("{0}", e.Message)
362-
});
363-
364-
return false;
365-
}
366-
367-
return true;
98+
Path.Join(Directory.GetParent(Assembly.GetExecutingAssembly().Location)!.FullName,
99+
"Assets", "Resources", "Dependencies", "KinectRuntime-x64.msi")
100+
}, progress, cancellationToken));
368101
}
369102

370103
private bool InstallFiles(IEnumerable<string> files,

plugin_KinectOne/plugin_KinectOne.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@
3131
<TargetPath>Microsoft.Kinect.dll</TargetPath>
3232
</ContentWithTargetPath>
3333
</ItemGroup>
34+
35+
<ItemGroup>
36+
<Folder Include="Assets\Resources\Dependencies\" />
37+
</ItemGroup>
3438
</Project>

0 commit comments

Comments
 (0)