Skip to content

Commit 56ccab8

Browse files
authored
Store provisioning mitigation (#6194)
## Change On older versions of Windows, when a package was already present but not provisioned, attempting to do a store install of an MSIX package for machine scope would end up as a no-op. Newer versions handle this properly, but to ensure consistency this change checks the provisioning state after a Store install to an attempts to provision if not as expected.
1 parent 5c03e8b commit 56ccab8

6 files changed

Lines changed: 148 additions & 2 deletions

File tree

src/AppInstallerCLITests/AppInstallerCLITests.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1127,4 +1127,4 @@
11271127
<Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
11281128
<Error Condition="!Exists('$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\packages\Microsoft.Windows.CppWinRT.2.0.250303.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
11291129
</Target>
1130-
</Project>
1130+
</Project>

src/AppInstallerCLITests/InstallFlow.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
#include "pch.h"
44
#include "WorkflowCommon.h"
55
#include "TestHooks.h"
6+
#include "AppInstallerRuntime.h"
67
#include <AppInstallerFileLogger.h>
8+
#include <AppInstallerProgress.h>
9+
#include <winget/MSStore.h>
710
#include <AppInstallerStrings.h>
811
#include <AppInstallerSynchronization.h>
912
#include <Commands/InstallCommand.h>
@@ -590,6 +593,41 @@ TEST_CASE("MSStoreInstallFlowWithTestManifest", "[InstallFlow][workflow]")
590593
REQUIRE(installResultStr.find("9WZDNCRFJ364") != std::string::npos);
591594
}
592595

596+
TEST_CASE("MSStoreInstallFlow_MachineScopeProvision", "[InstallFlow][MSStore]")
597+
{
598+
if (!AppInstaller::Runtime::IsRunningAsAdmin() || AppInstaller::Runtime::IsRunningAsSystem())
599+
{
600+
WARN("Test requires running as admin but not SYSTEM. Skipped.");
601+
return;
602+
}
603+
604+
TestHook::SetForceProvisionAfterInstall_Override forceProvisionOverride(true);
605+
606+
AppInstaller::ProgressCallback progress;
607+
AppInstaller::MSStore::MSStoreOperation installOperation(
608+
AppInstaller::MSStore::MSStoreOperationType::Install,
609+
L"9NVTPZWRC6KQ",
610+
AppInstaller::Manifest::ScopeEnum::User,
611+
true,
612+
false);
613+
614+
HRESULT hr = installOperation.StartAndWaitForOperation(progress);
615+
REQUIRE(SUCCEEDED(hr));
616+
617+
// Verify the package is now provisioned.
618+
winrt::Windows::Management::Deployment::PackageManager packageManager;
619+
bool isProvisioned = false;
620+
for (auto const& pkg : packageManager.FindProvisionedPackages())
621+
{
622+
if (pkg.Id().FamilyName() == L"Microsoft.DesiredStateConfiguration_8wekyb3d8bbwe")
623+
{
624+
isProvisioned = true;
625+
break;
626+
}
627+
}
628+
REQUIRE(isProvisioned);
629+
}
630+
593631
TEST_CASE("MsixInstallFlow_DownloadFlow", "[InstallFlow][workflow]")
594632
{
595633
TestCommon::TempFile installResultPath("TestMsixInstalled.txt");

src/AppInstallerCLITests/TestHooks.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ namespace AppInstaller
105105
void SetSfsClientAppContents_Override(std::function<std::vector<SFS::AppContent>(std::string_view)>* value);
106106

107107
void SetLicensingHttpPipelineStage_Override(std::shared_ptr<web::http::http_pipeline_stage> value);
108+
109+
void TestHook_SetProvisionAfterInstall(bool* value);
108110
}
109111

110112
namespace Utility::TestHooks
@@ -325,6 +327,22 @@ namespace TestHook
325327
}
326328
};
327329

330+
struct SetForceProvisionAfterInstall_Override
331+
{
332+
SetForceProvisionAfterInstall_Override(bool value) : m_value(value)
333+
{
334+
AppInstaller::MSStore::TestHooks::TestHook_SetProvisionAfterInstall(&m_value);
335+
}
336+
337+
~SetForceProvisionAfterInstall_Override()
338+
{
339+
AppInstaller::MSStore::TestHooks::TestHook_SetProvisionAfterInstall(nullptr);
340+
}
341+
342+
private:
343+
bool m_value;
344+
};
345+
328346
struct SetDownloadResult_Function_Override
329347
{
330348
SetDownloadResult_Function_Override(std::function<AppInstaller::Utility::DownloadResult(

src/AppInstallerCommonCore/Deployment.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@ namespace AppInstaller::Deployment
150150
return out;
151151
}
152152

153+
HRESULT WaitForDeployment(
154+
IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress>& deployOperation,
155+
IProgressCallback& callback,
156+
bool throwOnError)
157+
{
158+
return WaitForDeployment(deployOperation, GetDeploymentOperationId(), callback, throwOnError);
159+
}
160+
153161
void AddPackage(
154162
const winrt::Windows::Foundation::Uri& uri,
155163
const Options& options,

src/AppInstallerCommonCore/MSStore.cpp

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <winget/ManifestCommon.h>
66
#include <winget/Runtime.h>
77
#include <AppInstallerFileLogger.h>
8+
#include <AppInstallerDeployment.h>
89
#include <AppInstallerErrors.h>
910
#include <winrt/Windows.ApplicationModel.h>
1011

@@ -14,6 +15,7 @@ namespace AppInstaller::MSStore
1415
using namespace winrt::Windows::Foundation;
1516
using namespace winrt::Windows::Foundation::Collections;
1617
using namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl;
18+
using namespace winrt::Windows::Management::Deployment;
1719

1820
namespace
1921
{
@@ -213,6 +215,50 @@ namespace AppInstaller::MSStore
213215
std::atomic_bool m_isUpdating = false;
214216
};
215217

218+
// After a successful machine-scope Store install, the package may not be provisioned on older
219+
// Windows versions (fixed in Windows 11 26100). This helper ensures the package is provisioned
220+
// as a best-effort mitigation.
221+
void EnsureProvisionedForMachineScope(const IVectorView<AppInstallItem>& installItems, std::wstring_view productId, IProgressCallback& progress) try
222+
{
223+
PackageManager packageManager;
224+
auto provisionedPackages = packageManager.FindProvisionedPackages();
225+
226+
for (auto const& installItem : installItems)
227+
{
228+
if (Utility::CaseInsensitiveEquals(installItem.ProductId(), productId))
229+
{
230+
std::wstring familyName{ installItem.PackageFamilyName() };
231+
if (familyName.empty())
232+
{
233+
continue;
234+
}
235+
236+
bool isProvisioned = false;
237+
for (auto const& provisionedPkg : provisionedPackages)
238+
{
239+
if (provisionedPkg.Id().FamilyName() == familyName)
240+
{
241+
isProvisioned = true;
242+
break;
243+
}
244+
}
245+
246+
if (!isProvisioned)
247+
{
248+
AICLI_LOG(Core, Info, << "Package not provisioned after machine scope install, provisioning: " << Utility::ConvertToUTF8(familyName));
249+
try
250+
{
251+
auto operation = packageManager.ProvisionPackageForAllUsersAsync(familyName);
252+
Deployment::WaitForDeployment(operation, progress);
253+
AICLI_LOG(Core, Info, << "Successfully provisioned package: " << Utility::ConvertToUTF8(familyName));
254+
}
255+
CATCH_LOG();
256+
}
257+
}
258+
}
259+
}
260+
CATCH_LOG();
261+
216262
HRESULT WaitForOperation(const std::wstring& productId, bool isSilentMode, IVectorView<AppInstallItem>& installItems, IProgressCallback& progress, const PackageUpdateMonitor& monitor)
217263
{
218264
auto cancelIfOperationFailed = wil::scope_exit(
@@ -321,6 +367,18 @@ namespace AppInstaller::MSStore
321367
}
322368
}
323369

370+
#ifndef AICLI_DISABLE_TEST_HOOKS
371+
namespace TestHooks
372+
{
373+
static bool* s_ProvisionAfterInstall = nullptr;
374+
375+
void TestHook_SetProvisionAfterInstall(bool* value)
376+
{
377+
s_ProvisionAfterInstall = value;
378+
}
379+
}
380+
#endif
381+
324382
HRESULT MSStoreOperation::InstallPackage(IProgressCallback& progress)
325383
{
326384
PackageUpdateMonitor monitor;
@@ -378,7 +436,25 @@ namespace AppInstaller::MSStore
378436
installOptions).get();
379437
}
380438

381-
return WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor);
439+
HRESULT hr = WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor);
440+
441+
bool shouldProvision = m_scope == Manifest::ScopeEnum::Machine;
442+
#ifndef AICLI_DISABLE_TEST_HOOKS
443+
if (TestHooks::s_ProvisionAfterInstall)
444+
{
445+
shouldProvision = *TestHooks::s_ProvisionAfterInstall;
446+
}
447+
#endif
448+
449+
if (SUCCEEDED(hr) && shouldProvision)
450+
{
451+
// Mitigation: on older OS versions (fixed in Windows 11 26100) the Store install service
452+
// may not provision the package even when InstallForAllUsers was set.
453+
// Explicitly provision if not already provisioned.
454+
EnsureProvisionedForMachineScope(installItems, m_productId, progress);
455+
}
456+
457+
return hr;
382458
}
383459

384460
HRESULT MSStoreOperation::UpdatePackage(IProgressCallback& progress)

src/AppInstallerCommonCore/Public/AppInstallerDeployment.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ namespace AppInstaller::Deployment
2020
std::vector<std::pair<std::string, std::wstring>> ExpectedDigests;
2121
};
2222

23+
// Waits for a deployment operation.
24+
HRESULT WaitForDeployment(
25+
winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Windows::Management::Deployment::DeploymentResult, winrt::Windows::Management::Deployment::DeploymentProgress>& deployOperation,
26+
IProgressCallback& callback,
27+
bool throwOnError = true);
28+
2329
// Calls winrt::Windows::Management::Deployment::PackageManager::AddPackageAsync if skipSmartScreen is true,
2430
// Otherwise, calls winrt::Windows::Management::Deployment::PackageManager::RequestAddPackageAsync
2531
void AddPackage(

0 commit comments

Comments
 (0)