Skip to content

Commit 9a29175

Browse files
authored
Fixes for deploying optional package while running (microsoft#81)
1 parent fb0db2a commit 9a29175

7 files changed

Lines changed: 191 additions & 82 deletions

File tree

src/AppInstallerCLICore/VTSupport.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ namespace AppInstaller::CLI::VirtualTerminal
2424
{
2525
if (!GetConsoleMode(hOut, &m_previousMode))
2626
{
27-
LOG_LAST_ERROR();
27+
// If the user redirects output, the handle will be invalid for this function.
28+
// Don't log it in that case.
29+
LOG_LAST_ERROR_IF(GetLastError() != ERROR_INVALID_HANDLE);
2830
}
2931
else
3032
{

src/AppInstallerCLITests/MsixInfo.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ TEST_CASE("MsixInfo_GetPackageFamilyName", "[msixinfo]")
1717
TestDataFile index(s_MsixFile_1);
1818
Msix::MsixInfo msix(index.GetPath().u8string());
1919

20-
std::string expectedFamilyName = "AppInstallerCLITestsFakeIndex_125rzkzqaqjwj";
21-
std::string actualFamilyName = msix.GetPackageFamilyName();
20+
std::string expectedFullName = "AppInstallerCLITestsFakeIndex_1.0.0.0_neutral__125rzkzqaqjwj";
21+
std::string actualFullName = msix.GetPackageFullName();
2222

23-
REQUIRE(expectedFamilyName == actualFamilyName);
23+
REQUIRE(expectedFullName == actualFullName);
2424
}
2525

2626
TEST_CASE("MsixInfo_WriteManifestAndCompareToSelf", "[msixinfo]")

src/AppInstallerCommonCore/Deployment.cpp

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,89 @@
88

99
namespace AppInstaller::Deployment
1010
{
11+
using namespace winrt::Windows::Foundation;
12+
using namespace winrt::Windows::Management::Deployment;
13+
1114
namespace
1215
{
1316
size_t GetDeploymentOperationId()
1417
{
1518
static std::atomic_size_t s_deploymentId = 0;
1619
return s_deploymentId.fetch_add(1);
1720
}
21+
22+
void WaitForDeployment(
23+
IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress>& deployOperation,
24+
size_t id,
25+
IProgressCallback& callback)
26+
{
27+
AsyncOperationProgressHandler<DeploymentResult, DeploymentProgress> progressCallback(
28+
[&callback](const IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress>&, DeploymentProgress progress)
29+
{
30+
callback.OnProgress(progress.percentage, 100, ProgressType::Percent);
31+
}
32+
);
33+
34+
// Set progress callback.
35+
deployOperation.Progress(progressCallback);
36+
37+
auto removeCancel = callback.SetCancellationFunction([&]() { deployOperation.Cancel(); });
38+
auto deployResult = deployOperation.get();
39+
40+
if (!SUCCEEDED(deployResult.ExtendedErrorCode()))
41+
{
42+
AICLI_LOG(Core, Error, << "Deployment failed #" << id << ": " << Utility::ConvertToUTF8(deployResult.ErrorText()));
43+
44+
// Note that while the format string is char*, it gets converted to wchar before being used and thus %s needs a wchar.
45+
THROW_HR_MSG(deployResult.ExtendedErrorCode(), "Install failed: %s", deployResult.ErrorText().c_str());
46+
}
47+
else
48+
{
49+
AICLI_LOG(Core, Info, << "Successfully deployed #" << id);
50+
}
51+
}
52+
53+
// Type that exists simply to enabled a fire and forget register call as we exit.
54+
struct DelayRegisterStorage
55+
{
56+
DelayRegisterStorage() = default;
57+
58+
~DelayRegisterStorage()
59+
{
60+
PackageManager packageManager;
61+
62+
for (const auto& fn : m_familyNames)
63+
{
64+
size_t id = GetDeploymentOperationId();
65+
AICLI_LOG(Core, Info, << "Starting RegisterPackageByFamilyName operation #" << id << ": " << fn);
66+
67+
winrt::hstring familyName = Utility::ConvertToUTF16(fn).c_str();
68+
(void)packageManager.RegisterPackageByFamilyNameAsync(
69+
familyName,
70+
nullptr,
71+
winrt::Windows::Management::Deployment::DeploymentOptions::None,
72+
nullptr,
73+
nullptr);
74+
}
75+
}
76+
77+
void Add(std::string_view familyName)
78+
{
79+
m_familyNames.emplace_back(familyName);
80+
}
81+
82+
private:
83+
std::vector<std::string> m_familyNames;
84+
};
85+
86+
DelayRegisterStorage s_delayRegisterStorage;
1887
}
1988

2089
void RequestAddPackageAsync(
2190
const winrt::Windows::Foundation::Uri& uri,
2291
winrt::Windows::Management::Deployment::DeploymentOptions options,
2392
IProgressCallback& callback)
2493
{
25-
using namespace winrt::Windows::Foundation;
26-
using namespace winrt::Windows::Management::Deployment;
27-
2894
size_t id = GetDeploymentOperationId();
2995
AICLI_LOG(Core, Info, << "Starting RequestAddPackage operation #" << id << ": " << Utility::ConvertToUTF8(uri.AbsoluteUri().c_str()));
3096

@@ -39,36 +105,39 @@ namespace AppInstaller::Deployment
39105
nullptr, /*optionalAndRelatedPackageFamilyNames*/
40106
nullptr /*relatedPackageUris*/);
41107

42-
AsyncOperationProgressHandler<DeploymentResult, DeploymentProgress> progressCallback(
43-
[&callback](const IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress>&, DeploymentProgress progress)
44-
{
45-
callback.OnProgress(progress.percentage, 100, ProgressType::Percent);
46-
}
47-
);
108+
WaitForDeployment(deployOperation, id, callback);
109+
}
110+
111+
void StageAndDelayRegisterPackageAsync(
112+
std::string_view packageFamilyName,
113+
const winrt::Windows::Foundation::Uri& uri,
114+
winrt::Windows::Management::Deployment::DeploymentOptions stageOptions,
115+
winrt::Windows::Management::Deployment::DeploymentOptions,
116+
IProgressCallback& callback)
117+
{
118+
size_t id = GetDeploymentOperationId();
119+
AICLI_LOG(Core, Info, << "Starting StagePackage operation #" << id << ": " << Utility::ConvertToUTF8(uri.AbsoluteUri().c_str()));
48120

49-
// Set progress callback.
50-
deployOperation.Progress(progressCallback);
121+
PackageManager packageManager;
51122

52-
auto removeCancel = callback.SetCancellationFunction([&]() { deployOperation.Cancel(); });
53-
auto deployResult = deployOperation.get();
123+
// RequestAddPackageAsync will invoke smart screen.
124+
auto deployOperation = packageManager.StagePackageAsync(
125+
uri,
126+
nullptr, /*dependencyPackageUris*/
127+
stageOptions,
128+
nullptr, /*targetVolume*/
129+
nullptr, /*optionalAndRelatedPackageFamilyNames*/
130+
nullptr /*relatedPackageUris*/);
54131

55-
if (!SUCCEEDED(deployResult.ExtendedErrorCode()))
56-
{
57-
AICLI_LOG(Core, Error, << "Deployment failed #" << id << ": " << Utility::ConvertToUTF8(deployResult.ErrorText()));
132+
WaitForDeployment(deployOperation, id, callback);
58133

59-
// Note that while the format string is char*, it gets converted to wchar before being used and thus %s needs a wchar.
60-
THROW_HR_MSG(deployResult.ExtendedErrorCode(), "Install failed: %s", deployResult.ErrorText().c_str());
61-
}
62-
else
63-
{
64-
AICLI_LOG(Core, Info, << "Successfully deployed #" << id);
65-
}
134+
s_delayRegisterStorage.Add(packageFamilyName);
66135
}
67136

68-
void RemovePackageFireAndForget(winrt::hstring packageFullName)
137+
void RemovePackageFireAndForget(std::string_view packageFullName)
69138
{
70-
using namespace winrt::Windows::Management::Deployment;
71139
PackageManager packageManager;
72-
(void)packageManager.RemovePackageAsync(packageFullName, RemovalOptions::None);
140+
winrt::hstring fullName = Utility::ConvertToUTF16(packageFullName).c_str();
141+
(void)packageManager.RemovePackageAsync(fullName, RemovalOptions::None);
73142
}
74143
}

src/AppInstallerCommonCore/MsixInfo.cpp

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,44 @@ namespace AppInstaller::Msix
188188
THROW_IF_FAILED(appxFactory->CreateManifestReader(inputStream, reader));
189189
}
190190

191+
std::string GetPackageFamilyNameFromFullName(std::string_view fullName)
192+
{
193+
std::wstring result;
194+
result.resize(PACKAGE_FAMILY_NAME_MAX_LENGTH + 1);
195+
UINT32 size = static_cast<UINT32>(result.size());
196+
THROW_IF_WIN32_ERROR(PackageFamilyNameFromFullName(Utility::ConvertToUTF16(fullName).c_str(), &size, &result[0]));
197+
result.resize(size - 1);
198+
return Utility::ConvertToUTF8(result);
199+
}
200+
201+
std::optional<std::filesystem::path> GetPackageLocationFromFullName(std::string_view fullName)
202+
{
203+
std::wstring fn = Utility::ConvertToUTF16(fullName);
204+
205+
UINT32 length = 0;
206+
LONG returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, nullptr);
207+
if (returnVal != ERROR_INSUFFICIENT_BUFFER)
208+
{
209+
LOG_WIN32(returnVal);
210+
return {};
211+
}
212+
213+
THROW_HR_IF(E_UNEXPECTED, length == 0);
214+
215+
std::wstring result;
216+
result.resize(length);
217+
218+
returnVal = GetStagedPackagePathByFullName(fn.c_str(), &length, &result[0]);
219+
if (returnVal != ERROR_SUCCESS)
220+
{
221+
LOG_WIN32(returnVal);
222+
return {};
223+
}
224+
225+
result.resize(length - 1);
226+
return { result };
227+
}
228+
191229
MsixInfo::MsixInfo(std::string_view uriStr)
192230
{
193231
if (Utility::IsUrlRemote(uriStr))
@@ -255,7 +293,7 @@ namespace AppInstaller::Msix
255293
return signatureContent;
256294
}
257295

258-
std::string MsixInfo::GetPackageFamilyName()
296+
std::string MsixInfo::GetPackageFullName()
259297
{
260298
ComPtr<IAppxManifestPackageId> packageId;
261299
if (m_isBundle)
@@ -271,10 +309,10 @@ namespace AppInstaller::Msix
271309
THROW_IF_FAILED(manifestReader->GetPackageId(&packageId));
272310
}
273311

274-
wil::unique_cotaskmem_string familyName;
275-
THROW_IF_FAILED(packageId->GetPackageFamilyName(&familyName));
312+
wil::unique_cotaskmem_string fullName;
313+
THROW_IF_FAILED(packageId->GetPackageFullName(&fullName));
276314

277-
return Utility::ConvertToUTF8(familyName.get());
315+
return Utility::ConvertToUTF8(fullName.get());
278316
}
279317

280318
bool MsixInfo::IsNewerThan(const std::filesystem::path& otherManifest)

src/AppInstallerCommonCore/Public/AppInstallerDeployment.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,18 @@ namespace AppInstaller::Deployment
1313
winrt::Windows::Management::Deployment::DeploymentOptions options,
1414
IProgressCallback& callback);
1515

16+
// Stages the package, and then attempts to register it without waiting.
17+
// This enables us to work around the fact that we cannot call SetPackageInUse,
18+
// and thus cannot actually update an optional package while we are running.
19+
void StageAndDelayRegisterPackageAsync(
20+
std::string_view packageFamilyName,
21+
const winrt::Windows::Foundation::Uri& uri,
22+
winrt::Windows::Management::Deployment::DeploymentOptions stageOptions,
23+
winrt::Windows::Management::Deployment::DeploymentOptions registerOptions,
24+
IProgressCallback& callback);
25+
1626
// Calls winrt::Windows::Management::Deployment::PackageManager::RemovePackageAsync,
1727
// but *DOES NOT WAIT FOR A RESULT*. As this is used for removing an optional package
18-
// we will simply complete our actions
19-
void RemovePackageFireAndForget(winrt::hstring packageFullName);
28+
// we will simply complete our actions and exit the process.
29+
void RemovePackageFireAndForget(std::string_view packageFullName);
2030
}

src/AppInstallerCommonCore/Public/AppInstallerMsixInfo.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <AppxPackaging.h>
66
#include <wrl/client.h>
77
#include <filesystem>
8+
#include <optional>
89
#include <string>
910
#include <string_view>
1011
#include <vector>
@@ -28,6 +29,12 @@ namespace AppInstaller::Msix
2829
IStream* inputStream,
2930
IAppxManifestReader** reader);
3031

32+
// Gets the package family name from the given full name.
33+
std::string GetPackageFamilyNameFromFullName(std::string_view fullName);
34+
35+
// Gets the package location from the given full name.
36+
std::optional<std::filesystem::path> GetPackageLocationFromFullName(std::string_view fullName);
37+
3138
// MsixInfo class handles all appx/msix related query.
3239
struct MsixInfo
3340
{
@@ -47,8 +54,8 @@ namespace AppInstaller::Msix
4754
// Full content of AppxSignature.p7x
4855
std::vector<byte> GetSignature();
4956

50-
// Gets the package family name.
51-
std::string GetPackageFamilyName();
57+
// Gets the package full name.
58+
std::string GetPackageFullName();
5259

5360
// Gets a value indicating whether the referenced info is newer than the given manifest.
5461
bool IsNewerThan(const std::filesystem::path& otherManifest);

0 commit comments

Comments
 (0)