commit 6ea13623e5e4b870b81efeea9142d15a98dd4208
parent d148be1e906235cd0beae7651776fc6cc2b73bf7
Author: yao-msft <50888816+yao-msft@users.noreply.github.com>
Date: Mon, 30 Sep 2024 20:17:40 -0700
More messages for common Store package download scenarios (#4841)
- Rename downloaded file to contain platform architecture info, and a
message for the option to filter packages to be downloaded
- Message for "package does not support download"
- Make licensing errors as warning, added a specific message for "Entra
Id account does not have enough access to licensing"
Diffstat:
13 files changed, 228 insertions(+), 82 deletions(-)
diff --git a/doc/windows/package-manager/winget/returnCodes.md b/doc/windows/package-manager/winget/returnCodes.md
@@ -143,6 +143,8 @@ ms.localizationpriority: medium
| 0x8A150081 | -1978335103 | APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED | Failed to get Microsoft Store package download information. |
| 0x8A150082 | -1978335102 | APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE | No applicable Microsoft Store package download information found. |
| 0x8A150083 | -1978335101 | APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED | Failed to retrieve Microsoft Store package license. |
+| 0x8A150084 | -1978335100 | APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED | The Microsoft Store package does not support download command. |
+| 0x8A150085 | -1978335099 | APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN | Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have required privilege. |
## Install errors.
diff --git a/src/AppInstallerCLICore/Resources.h b/src/AppInstallerCLICore/Resources.h
@@ -370,14 +370,17 @@ namespace AppInstaller::CLI::Resource
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetDownloadInfoFailed);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicense);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicenseFailed);
+ WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicenseForbidden);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadGetLicenseSuccess);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadMainPackages);
+ WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadMultiplePackagesNotice);
+ WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadNoApplicablePackageFound);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloaded);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloadFailed);
+ WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloadNotSupported);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageDownloadSuccess);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageHashMismatch);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageHashVerified);
- WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadPackageNotFound);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreDownloadRenameNotSupported);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallOrUpdateFailed);
WINGET_DEFINE_RESOURCE_STRINGID(MSStoreInstallTryGetEntitlement);
diff --git a/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp b/src/AppInstallerCLICore/Workflows/MSStoreInstallerHandler.cpp
@@ -271,6 +271,7 @@ namespace AppInstaller::CLI::Workflow
// Authentication notice
context.Reporter.Warn() << Resource::String::MSStoreDownloadAuthenticationNotice << std::endl;
+ context.Reporter.Warn() << Resource::String::MSStoreDownloadMultiplePackagesNotice << std::endl;
const auto& installer = context.Get<Execution::Data::Installer>().value();
@@ -307,13 +308,16 @@ namespace AppInstaller::CLI::Workflow
{
case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE:
case APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE:
- context.Reporter.Error() << Resource::String::MSStoreDownloadPackageNotFound << std::endl;
+ context.Reporter.Error() << Resource::String::MSStoreDownloadNoApplicablePackageFound << std::endl;
+ break;
+ case APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED:
+ context.Reporter.Error() << Resource::String::MSStoreDownloadPackageDownloadNotSupported << std::endl;
break;
default:
context.Reporter.Error() << Resource::String::MSStoreDownloadGetDownloadInfoFailed << std::endl;
}
- throw;
+ AICLI_TERMINATE_CONTEXT(re.GetErrorCode());
}
bool skipDependencies = context.Args.Contains(Execution::Args::Type::SkipDependencies);
@@ -368,9 +372,18 @@ namespace AppInstaller::CLI::Workflow
}
catch (const wil::ResultException& re)
{
- AICLI_LOG(CLI, Error, << "Getting MSStore package license failed. Error code: " << re.GetErrorCode());
- context.Reporter.Error() << Resource::String::MSStoreDownloadGetLicenseFailed << std::endl;
- throw;
+ if (re.GetErrorCode() == APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN)
+ {
+ AICLI_LOG(CLI, Warning, << "Getting MSStore package license failed. The Microsoft Entra Id account does not have privilege.");
+ context.Reporter.Warn() << Resource::String::MSStoreDownloadGetLicenseForbidden << std::endl;
+ }
+ else
+ {
+ AICLI_LOG(CLI, Warning, << "Getting MSStore package license failed. Error code: " << re.GetErrorCode());
+ context.Reporter.Warn() << Resource::String::MSStoreDownloadGetLicenseFailed << std::endl;
+ }
+
+ AICLI_TERMINATE_CONTEXT(re.GetErrorCode());
}
std::filesystem::path licenseFilePath = downloadDirectory / Utility::ConvertToUTF16(installer.ProductId + "_License.xml");
diff --git a/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw b/src/AppInstallerCLIPackage/Shared/Strings/en-us/winget.resw
@@ -2896,13 +2896,13 @@ Please specify one of them using the --source option to proceed.</value>
<value>Failed to get Microsoft Store package download information.</value>
</data>
<data name="APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE" xml:space="preserve">
- <value>No applicable Microsoft Store package download information found.</value>
+ <value>No applicable Microsoft Store package found for download.</value>
</data>
<data name="APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED" xml:space="preserve">
<value>Failed to retrieve Microsoft Store package license.</value>
</data>
- <data name="MSStoreDownloadPackageNotFound" xml:space="preserve">
- <value>The Microsoft Store package could not be found.</value>
+ <data name="MSStoreDownloadNoApplicablePackageFound" xml:space="preserve">
+ <value>No applicable Microsoft Store package found.</value>
</data>
<data name="MSStoreDownloadPackageHashVerified" xml:space="preserve">
<value>Successfully verified Microsoft Store package hash</value>
@@ -2948,7 +2948,8 @@ Please specify one of them using the --source option to proceed.</value>
<comment>{Locked="--rename"}</comment>
</data>
<data name="MSStoreDownloadAuthenticationNotice" xml:space="preserve">
- <value>Microsoft Store package download requires Microsoft Entra Id authentication. Authentication prompt may appear when necessary. Authenticated information will be shared with Microsoft services for access authorization. For Microsoft Store package licensing, the Microsoft Entra Id account needs to have administrator access to the Microsoft Entra Id tenant.</value>
+ <value>Microsoft Store package download requires Microsoft Entra Id authentication. Authentication prompt may appear when necessary. Authenticated information will be shared with Microsoft services for access authorization. For Microsoft Store package licensing, the Microsoft Entra Id account needs to be a member of Global Administrator, User Administrator, or License Administrator.</value>
+ <comment>{Locked="Global Administrator,User Administrator,License Administrator"}</comment>
</data>
<data name="SkipMicrosoftStorePackageLicenseArgumentDescription" xml:space="preserve">
<value>Skips retrieving Microsoft Store package offline license</value>
@@ -3112,4 +3113,21 @@ Please specify one of them using the --source option to proceed.</value>
<data name="ConfigurationSuppressPrologueArgumentDescription" xml:space="preserve">
<value>Suppress showing initial configuration details when possible</value>
</data>
-</root>-
\ No newline at end of file
+ <data name="MSStoreDownloadMultiplePackagesNotice" xml:space="preserve">
+ <value>Multiple Microsoft Store packages may be downloaded targeting different platforms and architectures. --platform, --architecture can be used to filter the packages.</value>
+ <comment>{Locked="--platform--architecture"}</comment>
+ </data>
+ <data name="MSStoreDownloadGetLicenseForbidden" xml:space="preserve">
+ <value>Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account is not a member of Global Administrator, User Administrator, or License Administrator. Use --skip-license to skip retrieving Microsoft Store package license.</value>
+ <comment>{Locked="Global Administrator,User Administrator,License Administrator,--skip-license"}</comment>
+ </data>
+ <data name="MSStoreDownloadPackageDownloadNotSupported" xml:space="preserve">
+ <value>The Microsoft Store package does not support download.</value>
+ </data>
+ <data name="APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED" xml:space="preserve">
+ <value>The Microsoft Store package does not support download.</value>
+ </data>
+ <data name="APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN" xml:space="preserve">
+ <value>Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have required privilege.</value>
+ </data>
+</root>
diff --git a/src/AppInstallerCLITests/MSStoreDownloadFlow.cpp b/src/AppInstallerCLITests/MSStoreDownloadFlow.cpp
@@ -212,7 +212,7 @@ std::vector<SFS::AppContent> GetSfsAppContentsOverrideFunction(std::string_view
"https://NotUsed/" + wuCategoryIdStr + "/Xbox/arm",
100,
{ { SFS::HashType::Sha256, base64EncodedSha256 } },
- { SFS::Architecture::Amd64 },
+ { SFS::Architecture::Arm },
{ "Xbox=10.0.0.0" },
wuCategoryIdStr + ".Xbox_1.0.0.0_arm__8wekyb3d8bbwe",
packageXbox);
@@ -225,7 +225,7 @@ std::vector<SFS::AppContent> GetSfsAppContentsOverrideFunction(std::string_view
"https://NotUsed/" + wuCategoryIdStr + "/cab",
100,
{ { SFS::HashType::Sha256, base64EncodedSha256 } },
- { SFS::Architecture::Amd64 },
+ { SFS::Architecture::Arm },
{ "Desktop=10.0.0.0" },
wuCategoryIdStr + ".Data_1.0.0.0_arm__8wekyb3d8bbwe",
packageData);
@@ -263,11 +263,11 @@ TEST_CASE("MSStoreDownloadFlow_Success", "[MSStoreDownloadFlow][workflow]")
// Verify downloaded files
REQUIRE(std::filesystem::exists(tempDirectory.GetPath()));
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_arm__8wekyb3d8bbwe.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx"));
// Verify license
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"));
@@ -278,9 +278,9 @@ TEST_CASE("MSStoreDownloadFlow_Success", "[MSStoreDownloadFlow][workflow]")
REQUIRE(licenseFileStr == LicenseContent);
// Verify unsupported packages filtered out
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Xbox_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Data_1.0.0.0_arm__8wekyb3d8bbwe.cab"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_1.0.0.0_IoT_Arm.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Xbox_1.0.0.0_Xbox_Arm.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.Data_1.0.0.0_Desktop_Arm.cab"));
}
TEST_CASE("MSStoreDownloadFlow_Success_SkipDependencies", "[MSStoreDownloadFlow][workflow]")
@@ -307,11 +307,11 @@ TEST_CASE("MSStoreDownloadFlow_Success_SkipDependencies", "[MSStoreDownloadFlow]
// Verify downloaded files
REQUIRE(std::filesystem::exists(tempDirectory.GetPath()));
REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_x64__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_arm__8wekyb3d8bbwe.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx"));
// Verify license
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"));
@@ -346,11 +346,11 @@ TEST_CASE("MSStoreDownloadFlow_Success_SkipLicense", "[MSStoreDownloadFlow][work
// Verify downloaded files
REQUIRE(std::filesystem::exists(tempDirectory.GetPath()));
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_arm__8wekyb3d8bbwe.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx"));
// Verify license
REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"));
@@ -379,11 +379,11 @@ TEST_CASE("MSStoreDownloadFlow_Success_SpecificLocale", "[MSStoreDownloadFlow][w
// Verify downloaded files
REQUIRE(std::filesystem::exists(tempDirectory.GetPath()));
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench.IoT_2.0.0.0_arm__8wekyb3d8bbwe.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_Universal_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdFrench.Dependency_1.2.3.4_Universal_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_Desktop_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench_1.0.0.0_Desktop_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdFrench.IoT_2.0.0.0_IoT_Arm.appx"));
// Verify license
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"));
@@ -418,11 +418,11 @@ TEST_CASE("MSStoreDownloadFlow_Success_SpecificArchitecture", "[MSStoreDownloadF
// Verify downloaded files
REQUIRE(std::filesystem::exists(tempDirectory.GetPath()));
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_x64__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_x64__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_arm__8wekyb3d8bbwe.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_x64.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx"));
// Verify license
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"));
@@ -457,11 +457,11 @@ TEST_CASE("MSStoreDownloadFlow_Success_SpecificPlatform", "[MSStoreDownloadFlow]
// Verify downloaded files
REQUIRE(std::filesystem::exists(tempDirectory.GetPath()));
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_x64__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_arm__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_x64__8wekyb3d8bbwe.appx"));
- REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_arm__8wekyb3d8bbwe.appx"));
- REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_arm__8wekyb3d8bbwe.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_X64.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"Dependencies" / L"TestCategoryIdEnglish.Dependency_1.2.3.4_Universal_Arm.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_X64.appx"));
+ REQUIRE_FALSE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish_1.0.0.0_Desktop_Arm.appx"));
+ REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"TestCategoryIdEnglish.IoT_2.0.0.0_IoT_Arm.appx"));
// Verify license
REQUIRE(std::filesystem::exists(tempDirectory.GetPath() / L"9WZDNCRFJ364_License.xml"));
@@ -487,7 +487,8 @@ TEST_CASE("MSStoreDownloadFlow_Fail_TargetSkuNotFound", "[MSStoreDownloadFlow][w
context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv);
DownloadCommand download({});
- REQUIRE_THROWS_HR(download.Execute(context), APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
+ download.Execute(context);
+ REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
INFO(downloadOutput.str());
}
@@ -506,7 +507,8 @@ TEST_CASE("MSStoreDownloadFlow_Fail_LocaleNotApplicable", "[MSStoreDownloadFlow]
context.Args.AddArg(Execution::Args::Type::Locale, "ja-JP"sv);
DownloadCommand download({});
- REQUIRE_THROWS_HR(download.Execute(context), APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
+ download.Execute(context);
+ REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
INFO(downloadOutput.str());
}
@@ -526,7 +528,8 @@ TEST_CASE("MSStoreDownloadFlow_Fail_ArchitectureNotApplicable", "[MSStoreDownloa
context.Args.AddArg(Execution::Args::Type::InstallArchitecture, "arm64"sv);
DownloadCommand download({});
- REQUIRE_THROWS_HR(download.Execute(context), APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
+ download.Execute(context);
+ REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_DISPLAYCATALOG_PACKAGE);
INFO(downloadOutput.str());
}
@@ -546,7 +549,8 @@ TEST_CASE("MSStoreDownloadFlow_Fail_PlatformNotApplicable", "[MSStoreDownloadFlo
context.Args.AddArg(Execution::Args::Type::Platform, "Windows.Holographic"sv);
DownloadCommand download({});
- REQUIRE_THROWS_HR(download.Execute(context), APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE);
+ download.Execute(context);
+ REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE);
INFO(downloadOutput.str());
}
@@ -560,12 +564,34 @@ TEST_CASE("MSStoreDownloadFlow_Fail_Licensing", "[MSStoreDownloadFlow][workflow]
OverrideDownloadInstallerFileForMSStoreDownload(context);
TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse));
TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction });
+ TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::InternalError));
+ context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string());
+ context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory);
+ context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv);
+
+ DownloadCommand download({});
+ download.Execute(context);
+ REQUIRE_TERMINATED_WITH(context, MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, web::http::status_codes::InternalError));
+ INFO(downloadOutput.str());
+}
+
+TEST_CASE("MSStoreDownloadFlow_Fail_Licensing_Forbidden", "[MSStoreDownloadFlow][workflow]")
+{
+ TestCommon::TempDirectory tempDirectory("TestDownloadDirectory", false);
+
+ std::ostringstream downloadOutput;
+ TestContext context{ downloadOutput, std::cin };
+ auto previousThreadGlobals = context.SetForCurrentThread();
+ OverrideDownloadInstallerFileForMSStoreDownload(context);
+ TestHook::SetDisplayCatalogHttpPipelineStage_Override displayCatalogOverride(GetTestRestRequestHandler(web::http::status_codes::OK, TestDisplayCatalogResponse));
+ TestHook::SetSfsClientAppContents_Override sfsClientOverride({ &GetSfsAppContentsOverrideFunction });
TestHook::SetLicensingHttpPipelineStage_Override licensingOverride(GetTestRestRequestHandler(web::http::status_codes::Forbidden));
context.Args.AddArg(Execution::Args::Type::Manifest, TestDataFile("DownloadFlowTest_MSStore.yaml").GetPath().u8string());
context.Args.AddArg(Execution::Args::Type::DownloadDirectory, tempDirectory);
context.Args.AddArg(Execution::Args::Type::Locale, "en-US"sv);
DownloadCommand download({});
- REQUIRE_THROWS_HR(download.Execute(context), MAKE_HRESULT(SEVERITY_ERROR, FACILITY_HTTP, web::http::status_codes::Forbidden));
+ download.Execute(context);
+ REQUIRE_TERMINATED_WITH(context, APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN);
INFO(downloadOutput.str());
}
diff --git a/src/AppInstallerCLITests/MsixInfo.cpp b/src/AppInstallerCLITests/MsixInfo.cpp
@@ -95,8 +95,13 @@ TEST_CASE("MsixInfo_ValidateMsixTrustInfo", "[msixinfo]")
}
}
-TEST_CASE("MsixInfo_GetPackageVersionFromFullName", "[msixinfo]")
+TEST_CASE("MsixInfo_GetPackageIdInfoFromFullName", "[msixinfo]")
{
- REQUIRE(Msix::GetPackageVersionFromFullName("Microsoft.NET.Native.Framework.2.2_2.2.29512.0_arm64__8wekyb3d8bbwe") == Utility::UInt64Version{ "2.2.29512.0" });
- REQUIRE(Msix::GetPackageVersionFromFullName("Microsoft.DoesNotExist_1.2.3.4_neutral_~_8wekyb3d8bbwe") == Utility::UInt64Version{ "1.2.3.4" });
+ auto testPackageIdInfo = Msix::GetPackageIdInfoFromFullName("Microsoft.NET.Native.Framework.2.2_2.2.29512.0_arm64__8wekyb3d8bbwe");
+ REQUIRE(testPackageIdInfo.Name == "Microsoft.NET.Native.Framework.2.2");
+ REQUIRE(testPackageIdInfo.Version == Utility::UInt64Version{ "2.2.29512.0" });
+
+ auto testPackageIdInfo2 = Msix::GetPackageIdInfoFromFullName("Microsoft.DoesNotExist_1.2.3.4_neutral_~_8wekyb3d8bbwe");
+ REQUIRE(testPackageIdInfo2.Name == "Microsoft.DoesNotExist");
+ REQUIRE(testPackageIdInfo2.Version == Utility::UInt64Version{ "1.2.3.4" });
}
diff --git a/src/AppInstallerCommonCore/MSStoreDownload.cpp b/src/AppInstallerCommonCore/MSStoreDownload.cpp
@@ -669,9 +669,9 @@ namespace AppInstaller::MSStore
return Utility::Architecture::Unknown;
}
- std::vector<std::string> GetSfsPackageFileSupportedPlatforms(const SFS::AppFile& appFile, Manifest::PlatformEnum requiredPlatform)
+ std::vector<Manifest::PlatformEnum> GetSfsPackageFileSupportedPlatforms(const SFS::AppFile& appFile, Manifest::PlatformEnum requiredPlatform)
{
- std::vector<std::string> supportedPlatforms;
+ std::vector<Manifest::PlatformEnum> supportedPlatforms;
for (auto const& applicability : appFile.GetApplicabilityDetails().GetPlatformApplicabilityForPackage())
{
@@ -679,7 +679,7 @@ namespace AppInstaller::MSStore
if (platform != Manifest::PlatformEnum::Unknown &&
(platform == requiredPlatform || requiredPlatform == Manifest::PlatformEnum::Unknown))
{
- supportedPlatforms.emplace_back(applicability);
+ supportedPlatforms.emplace_back(platform);
}
}
@@ -709,7 +709,7 @@ namespace AppInstaller::MSStore
}
// This also checks if the file type is supported. If not supported, the return is empty string.
- std::string GetSfsPackageFileName(const SFS::AppFile& appFile)
+ std::string GetSfsPackageFileExtension(const SFS::AppFile& appFile)
{
std::string fileExtension = std::filesystem::path{ appFile.GetFileId() }.extension().u8string();
@@ -728,7 +728,48 @@ namespace AppInstaller::MSStore
return {};
}
- return appFile.GetFileMoniker() + fileExtension;
+ return fileExtension;
+ }
+
+ // The file name will be {Name}_{Version}_{Platform list}_{Arch list}.{File Extension}
+ // If the file name is longer than 256, file moniker will be used.
+ std::string GetSfsPackageFileNameForDownload(
+ const std::string& packageName,
+ const Utility::UInt64Version& packageVersion,
+ const std::vector<Manifest::PlatformEnum>& supportedPlatforms,
+ const std::vector<Utility::Architecture>& supportedArchitectures,
+ const std::string& fileExtension,
+ const std::string& fileMoniker)
+ {
+ std::string platformString;
+ for (auto platform : supportedPlatforms)
+ {
+ platformString += std::string{ Manifest::PlatformToString(platform, true) } + '.';
+ }
+ platformString.resize(platformString.size() - 1);
+
+ std::string architectureString;
+ for (auto architecture : supportedArchitectures)
+ {
+ architectureString += std::string{ Utility::ToString(architecture) } + '.';
+ }
+ architectureString.resize(architectureString.size() - 1);
+
+ std::string fileName =
+ packageName + '_' +
+ packageVersion.ToString() + '_' +
+ platformString + '_' +
+ architectureString +
+ fileExtension;
+
+ if (fileName.length() < 256)
+ {
+ return fileName;
+ }
+ else
+ {
+ return fileMoniker + fileExtension;
+ }
}
void SfsClientLoggingCallback(const SFS::LogData& logData)
@@ -784,7 +825,7 @@ namespace AppInstaller::MSStore
Utility::Architecture requiredArchitecture = Utility::Architecture::Unknown,
Manifest::PlatformEnum requiredPlatform = Manifest::PlatformEnum::Unknown)
{
- using PlatformAndArchitectureKey = std::pair<std::string, Utility::Architecture>;
+ using PlatformAndArchitectureKey = std::pair<Manifest::PlatformEnum, Utility::Architecture>;
// Since the server may return multiple versions of the same package, we'll use this map to record the one with latest version
// for each Platform|Architecture pair.
@@ -805,8 +846,8 @@ namespace AppInstaller::MSStore
AICLI_LOG(Core, Info, << "Package skipped due to unsupported architecture. FileId:" << appFile.GetFileId());
continue;
}
- std::string fileName = GetSfsPackageFileName(appFile);
- if (fileName.empty())
+ std::string fileExtension = GetSfsPackageFileExtension(appFile);
+ if (fileExtension.empty())
{
AICLI_LOG(Core, Info, << "Package skipped due to unsupported file type. FileId:" << appFile.GetFileId());
continue;
@@ -814,10 +855,13 @@ namespace AppInstaller::MSStore
MSStoreDownloadFile downloadFile;
downloadFile.Url = appFile.GetUrl();
- downloadFile.FileName = fileName;
// The sha256 hash was base64 encoded
downloadFile.Sha256 = JSON::Base64Decode(appFile.GetHashes().at(SFS::HashType::Sha256));
- downloadFile.Version = Msix::GetPackageVersionFromFullName(appFile.GetFileMoniker());
+ auto packageInfo = Msix::GetPackageIdInfoFromFullName(appFile.GetFileMoniker());
+ downloadFile.Version = packageInfo.Version;
+ downloadFile.FileName = GetSfsPackageFileNameForDownload(
+ packageInfo.Name, packageInfo.Version, supportedPlatforms,
+ supportedArchitectures, fileExtension, appFile.GetFileMoniker());
// Update the platform architecture map with latest package if applicable
for (auto supportedPlatform : supportedPlatforms)
@@ -870,8 +914,16 @@ namespace AppInstaller::MSStore
auto requestResult = GetSfsClientInstance()->GetLatestAppDownloadInfo(sfsClientRequest, appContents);
if (!requestResult)
{
- AICLI_LOG(Core, Error, << "Failed to call SfsClient GetLatestAppDownloadInfo. Error code: " << requestResult.GetCode() << " Message: " << requestResult.GetMsg());
- THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to call SfsClient GetLatestAppDownloadInfo. ErrorCode: %lu Message: %hs", requestResult.GetCode(), requestResult.GetMsg().c_str());
+ if (requestResult.GetCode() == SFS::Result::Code::HttpNotFound)
+ {
+ AICLI_LOG(Core, Error, << "Failed to call SfsClient GetLatestAppDownloadInfo. Package not found.");
+ THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED, "Failed to call SfsClient GetLatestAppDownloadInfo. Package download not supported.");
+ }
+ else
+ {
+ AICLI_LOG(Core, Error, << "Failed to call SfsClient GetLatestAppDownloadInfo. Error code: " << requestResult.GetCode() << " Message: " << requestResult.GetMsg());
+ THROW_HR_MSG(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to call SfsClient GetLatestAppDownloadInfo. ErrorCode: %lu Message: %hs", requestResult.GetCode(), requestResult.GetMsg().c_str());
+ }
}
}
@@ -943,8 +995,25 @@ namespace AppInstaller::MSStore
Http::HttpClientHelper::HttpRequestHeaders requestHeaders;
requestHeaders.insert_or_assign(JSON::GetUtilityString(From), L"winget-cli");
- std::optional<web::json::value> licensingResponseObject = httpClientHelper.HandlePost(
- JSON::GetUtilityString(LicensingRestEndpoint), requestBody, requestHeaders, authHeaders);
+ std::optional<web::json::value> licensingResponseObject = std::nullopt;
+ try
+ {
+ licensingResponseObject = httpClientHelper.HandlePost(
+ JSON::GetUtilityString(LicensingRestEndpoint), requestBody, requestHeaders, authHeaders);
+ }
+ catch (const wil::ResultException& re)
+ {
+ if (re.GetErrorCode() == HTTP_E_STATUS_FORBIDDEN)
+ {
+ AICLI_LOG(CLI, Error, << "Getting MSStore package license failed. The Microsoft Entra Id account does not have privilege.");
+ THROW_HR(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN);
+ }
+ else
+ {
+ AICLI_LOG(CLI, Error, << "Getting MSStore package license failed. Error code: " << re.GetErrorCode());
+ THROW_HR(re.GetErrorCode());
+ }
+ }
if (!licensingResponseObject || licensingResponseObject->is_null())
{
diff --git a/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp b/src/AppInstallerCommonCore/Manifest/ManifestCommon.cpp
@@ -658,20 +658,20 @@ namespace AppInstaller::Manifest
return "unknown"sv;
}
- std::string_view PlatformToString(PlatformEnum platform)
+ std::string_view PlatformToString(PlatformEnum platform, bool shortString)
{
switch (platform)
{
case PlatformEnum::Desktop:
- return "Windows.Desktop"sv;
+ return shortString ? "Desktop" : "Windows.Desktop"sv;
case PlatformEnum::Universal:
- return "Windows.Universal"sv;
+ return shortString ? "Universal" : "Windows.Universal"sv;
case PlatformEnum::IoT:
- return "Windows.IoT"sv;
+ return shortString ? "IoT" : "Windows.IoT"sv;
case PlatformEnum::Holographic:
- return "Windows.Holographic"sv;
+ return shortString ? "Holographic" : "Windows.Holographic"sv;
case PlatformEnum::Team:
- return "Windows.Team"sv;
+ return shortString ? "Team" : "Windows.Team"sv;
}
return "Unknown"sv;
diff --git a/src/AppInstallerCommonCore/MsixInfo.cpp b/src/AppInstallerCommonCore/MsixInfo.cpp
@@ -465,7 +465,7 @@ namespace AppInstaller::Msix
return { result };
}
- Utility::UInt64Version GetPackageVersionFromFullName(std::string_view fullName)
+ Msix::PackageIdInfo GetPackageIdInfoFromFullName(std::string_view fullName)
{
std::wstring fullNameWide = Utility::ConvertToUTF16(fullName);
@@ -474,7 +474,7 @@ namespace AppInstaller::Msix
if (returnVal != ERROR_INSUFFICIENT_BUFFER)
{
LOG_WIN32(returnVal);
- return 0;
+ return {};
}
THROW_HR_IF(E_UNEXPECTED, length == 0);
@@ -485,12 +485,12 @@ namespace AppInstaller::Msix
if (returnVal != ERROR_SUCCESS)
{
LOG_WIN32(returnVal);
- return 0;
+ return {};
}
PACKAGE_ID* packageId = (PACKAGE_ID*)packageIdContent.get();
- return packageId->version.Version;
+ return { Utility::ConvertToUTF8(packageId->name), packageId->version.Version };
}
GetCertContextResult GetCertContextFromMsix(const std::filesystem::path& msixPath)
diff --git a/src/AppInstallerCommonCore/Public/AppInstallerMsixInfo.h b/src/AppInstallerCommonCore/Public/AppInstallerMsixInfo.h
@@ -47,8 +47,14 @@ namespace AppInstaller::Msix
// Gets the package location from the given full name.
std::optional<std::filesystem::path> GetPackageLocationFromFullName(std::string_view fullName);
- // Gets the package version from the given full name.
- AppInstaller::Utility::UInt64Version GetPackageVersionFromFullName(std::string_view fullName);
+ struct PackageIdInfo
+ {
+ std::string Name;
+ AppInstaller::Utility::UInt64Version Version;
+ };
+
+ // Gets the package id info from the given full name.
+ PackageIdInfo GetPackageIdInfoFromFullName(std::string_view fullName);
// MsixInfo class handles all appx/msix related query.
struct MsixInfo
diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h
@@ -406,7 +406,8 @@ namespace AppInstaller::Manifest
std::string_view RepairBehaviorToString(RepairBehaviorEnum repairBehavior);
- std::string_view PlatformToString(PlatformEnum platform);
+ // Short string representation does not contain "Windows."
+ std::string_view PlatformToString(PlatformEnum platform, bool shortString = false);
std::string_view ScopeToString(ScopeEnum scope);
diff --git a/src/AppInstallerSharedLib/Errors.cpp b/src/AppInstallerSharedLib/Errors.cpp
@@ -219,6 +219,8 @@ namespace AppInstaller
WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED, "Failed to get Microsoft Store package download information."),
WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE, "No applicable Microsoft Store package download information found."),
WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED, "Failed to retrieve Microsoft Store package license."),
+ WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED, "The Microsoft Store package does not support download."),
+ WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN, "Failed to retrieve Microsoft Store package license. The Microsoft Entra Id account does not have the required privilege."),
// Install errors.
WINGET_HRESULT_INFO(APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE, "Application is currently running. Exit the application then try again."),
diff --git a/src/AppInstallerSharedLib/Public/AppInstallerErrors.h b/src/AppInstallerSharedLib/Public/AppInstallerErrors.h
@@ -149,6 +149,8 @@
#define APPINSTALLER_CLI_ERROR_SFSCLIENT_API_FAILED ((HRESULT)0x8A150081)
#define APPINSTALLER_CLI_ERROR_NO_APPLICABLE_SFSCLIENT_PACKAGE ((HRESULT)0x8A150082)
#define APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED ((HRESULT)0x8A150083)
+#define APPINSTALLER_CLI_ERROR_SFSCLIENT_PACKAGE_NOT_SUPPORTED ((HRESULT)0x8A150084)
+#define APPINSTALLER_CLI_ERROR_LICENSING_API_FAILED_FORBIDDEN ((HRESULT)0x8A150085)
// Install errors.
#define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE ((HRESULT)0x8A150101)