-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathAppxPackageRunService.cs
190 lines (162 loc) · 7.59 KB
/
AppxPackageRunService.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// MSIX Hero
// Copyright (C) 2022 Marcin Otorowski
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// Full notice:
// https://github.com/marcinotorowski/msix-hero/blob/develop/LICENSE.md
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Otor.MsixHero.Appx.Diagnostic.Developer;
using Otor.MsixHero.Appx.Exceptions;
using Otor.MsixHero.Appx.Packaging.Manifest;
using Otor.MsixHero.Appx.Packaging.Manifest.FileReaders;
using Dapplo.Log;
using Otor.MsixHero.Infrastructure.Progress;
using Otor.MsixHero.Infrastructure.ThirdParty.PowerShell;
using Otor.MsixHero.Infrastructure.Helpers;
namespace Otor.MsixHero.Appx.Packaging.Services
{
[SuppressMessage("ReSharper", "UnusedVariable")]
public class AppxPackageRunService : IAppxPackageRunService
{
private static readonly LogSource Logger = new(); protected readonly ISideloadingConfigurator SideloadingConfigurator = new SideloadingConfigurator();
public async Task RunToolInContext(PackageEntry packageEntry, string toolPath, string arguments, CancellationToken cancellationToken = default, IProgress<ProgressData> progress = default)
{
if (packageEntry == null)
{
throw new ArgumentNullException(nameof(packageEntry));
}
if (toolPath == null)
{
throw new ArgumentNullException(nameof(toolPath));
}
using IAppxFileReader reader = new FileInfoFileReaderAdapter(packageEntry.ManifestPath);
var maniReader = new AppxManifestReader();
var manifest = await maniReader.Read(reader, cancellationToken).ConfigureAwait(false);
if (!manifest.Applications.Any())
{
throw new InvalidOperationException(Resources.Localization.Packages_Error_NoEntryPointCmd);
}
await RunToolInContext(packageEntry.PackageFamilyName, manifest.Applications[0].Id, toolPath, arguments, cancellationToken, progress).ConfigureAwait(false);
}
public async Task RunToolInContext(string packageFamilyName, string appId, string toolPath, string arguments = null, CancellationToken cancellationToken = default, IProgress<ProgressData> progress = default)
{
var windowsVersion = NdDll.RtlGetVersion();
// Invoke-CommandInDesktopPackage requires the device to be in Developer mode for Windows 10 builds prior to 18922.
if (windowsVersion < new Version(10, 0, 18922))
{
SideloadingConfigurator.AssertDeveloperModeEnabled();
}
Logger.Info().WriteLine("Running tool '{0}' with arguments '{1}' in package '{2}' (AppId = '{3}')…", toolPath, arguments, packageFamilyName, appId);
if (packageFamilyName == null)
{
throw new ArgumentNullException(nameof(packageFamilyName));
}
if (toolPath == null)
{
throw new ArgumentNullException(nameof(toolPath));
}
using var ps = await PowerShellSession.CreateForAppxModule().ConfigureAwait(false);
using var cmd = ps.AddCommand("Invoke-CommandInDesktopPackage");
cmd.AddParameter("Command", toolPath);
cmd.AddParameter("PackageFamilyName", packageFamilyName);
cmd.AddParameter("AppId", appId);
cmd.AddParameter("PreventBreakaway");
if (!string.IsNullOrEmpty(arguments))
{
cmd.AddParameter("Args", arguments);
}
Logger.Debug().WriteLine("Executing Invoke-CommandInDesktopPackage");
try
{
using var result = await ps.InvokeAsync().ConfigureAwait(false);
}
catch (Exception e)
{
if (e.HResult == -2147024891 /* 0x80070005 E_ACCESSDENIED */)
{
throw new DeveloperModeException(Resources.Localization.Packages_Error_DeveloperMode, e);
}
if (e.HResult == -2146233087)
{
throw new AdminRightsRequiredException(Resources.Localization.Packages_Error_Uac, e);
}
throw;
}
}
public Task Run(PackageEntry packageEntry, string appId = null, CancellationToken cancellationToken = default, IProgress<ProgressData> progress = default)
{
return Run(packageEntry.ManifestPath, appId, cancellationToken, progress);
}
public async Task Run(string packageManifestLocation, string appId = null, CancellationToken cancellationToken = default, IProgress<ProgressData> progress = default)
{
if (appId == null)
{
Logger.Info().WriteLine("Running the default entry point from package {0}", packageManifestLocation);
}
if (packageManifestLocation == null || !File.Exists(packageManifestLocation))
{
throw new FileNotFoundException();
}
string entryPoint;
if (string.IsNullOrEmpty(appId))
{
entryPoint = (await GetEntryPoints(packageManifestLocation).ConfigureAwait(false)).FirstOrDefault();
}
else
{
entryPoint = (await GetEntryPoints(packageManifestLocation).ConfigureAwait(false)).FirstOrDefault(e => e.EndsWith("!" + appId, StringComparison.Ordinal));
}
if (entryPoint == null)
{
if (appId == null)
{
Logger.Warn().WriteLine("The package does not contain any entry point that is visible in the start menu. Aborting…");
throw new InvalidOperationException(Resources.Localization.Packages_Error_NoStartMenu);
}
throw new InvalidOperationException(string.Format(Resources.Localization.Packages_Error_NoEntryPoint_Format, appId));
}
var p = new Process();
var startInfo = new ProcessStartInfo
{
UseShellExecute = true,
FileName = entryPoint
};
Logger.Info().WriteLine("Executing " + entryPoint + " (with shell execute)…");
p.StartInfo = startInfo;
p.Start();
}
private static async Task<string[]> GetEntryPoints(string manifestLocation)
{
if (!File.Exists(manifestLocation))
{
return Array.Empty<string>();
}
var reader = new AppxManifestReader();
using IAppxFileReader appxSource = new FileInfoFileReaderAdapter(manifestLocation);
var appxPackage = await reader.Read(appxSource).ConfigureAwait(false);
return appxPackage.Applications.Select(app =>
{
if (string.IsNullOrEmpty(app.Id))
{
return @"shell:appsFolder\" + appxPackage.FamilyName;
}
return @"shell:appsFolder\" + appxPackage.FamilyName + "!" + app.Id;
}).ToArray();
}
}
}