Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/AppInstallerCLITests/AppInstallerCLITests.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1105,4 +1105,4 @@
<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'))" />
<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'))" />
</Target>
</Project>
</Project>
38 changes: 38 additions & 0 deletions src/AppInstallerCLITests/InstallFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
#include "pch.h"
#include "WorkflowCommon.h"
#include "TestHooks.h"
#include "AppInstallerRuntime.h"
#include <AppInstallerFileLogger.h>
#include <AppInstallerProgress.h>
#include <winget/MSStore.h>
#include <AppInstallerStrings.h>
#include <AppInstallerSynchronization.h>
#include <Commands/InstallCommand.h>
Expand Down Expand Up @@ -590,6 +593,41 @@ TEST_CASE("MSStoreInstallFlowWithTestManifest", "[InstallFlow][workflow]")
REQUIRE(installResultStr.find("9WZDNCRFJ364") != std::string::npos);
}

TEST_CASE("MSStoreInstallFlow_MachineScopeProvision", "[InstallFlow][MSStore]")
{
if (!AppInstaller::Runtime::IsRunningAsAdmin() || AppInstaller::Runtime::IsRunningAsSystem())
{
WARN("Test requires running as admin but not SYSTEM. Skipped.");
return;
}

TestHook::SetForceProvisionAfterInstall_Override forceProvisionOverride(true);

AppInstaller::ProgressCallback progress;
AppInstaller::MSStore::MSStoreOperation installOperation(
AppInstaller::MSStore::MSStoreOperationType::Install,
L"9NVTPZWRC6KQ",
AppInstaller::Manifest::ScopeEnum::User,
true,
false);

HRESULT hr = installOperation.StartAndWaitForOperation(progress);
REQUIRE(SUCCEEDED(hr));

// Verify the package is now provisioned.
winrt::Windows::Management::Deployment::PackageManager packageManager;
bool isProvisioned = false;
for (auto const& pkg : packageManager.FindProvisionedPackages())
{
if (pkg.Id().FamilyName() == L"Microsoft.DesiredStateConfiguration_8wekyb3d8bbwe")
{
isProvisioned = true;
break;
}
}
REQUIRE(isProvisioned);
}

TEST_CASE("MsixInstallFlow_DownloadFlow", "[InstallFlow][workflow]")
{
TestCommon::TempFile installResultPath("TestMsixInstalled.txt");
Expand Down
18 changes: 18 additions & 0 deletions src/AppInstallerCLITests/TestHooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ namespace AppInstaller
void SetSfsClientAppContents_Override(std::function<std::vector<SFS::AppContent>(std::string_view)>* value);

void SetLicensingHttpPipelineStage_Override(std::shared_ptr<web::http::http_pipeline_stage> value);

void TestHook_SetProvisionAfterInstall(bool* value);
}

namespace Utility::TestHooks
Expand Down Expand Up @@ -320,6 +322,22 @@ namespace TestHook
}
};

struct SetForceProvisionAfterInstall_Override
{
SetForceProvisionAfterInstall_Override(bool value) : m_value(value)
{
AppInstaller::MSStore::TestHooks::TestHook_SetProvisionAfterInstall(&m_value);
}

~SetForceProvisionAfterInstall_Override()
{
AppInstaller::MSStore::TestHooks::TestHook_SetProvisionAfterInstall(nullptr);
}

private:
bool m_value;
};

struct SetDownloadResult_Function_Override
{
SetDownloadResult_Function_Override(std::function<AppInstaller::Utility::DownloadResult(
Expand Down
8 changes: 8 additions & 0 deletions src/AppInstallerCommonCore/Deployment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ namespace AppInstaller::Deployment
return out;
}

HRESULT WaitForDeployment(
IAsyncOperationWithProgress<DeploymentResult, DeploymentProgress>& deployOperation,
IProgressCallback& callback,
bool throwOnError)
{
return WaitForDeployment(deployOperation, GetDeploymentOperationId(), callback, throwOnError);
}

void AddPackage(
const winrt::Windows::Foundation::Uri& uri,
const Options& options,
Expand Down
78 changes: 77 additions & 1 deletion src/AppInstallerCommonCore/MSStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <winget/ManifestCommon.h>
#include <winget/Runtime.h>
#include <AppInstallerFileLogger.h>
#include <AppInstallerDeployment.h>
#include <AppInstallerErrors.h>
#include <winrt/Windows.ApplicationModel.h>

Expand All @@ -14,6 +15,7 @@ namespace AppInstaller::MSStore
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Foundation::Collections;
using namespace winrt::Windows::ApplicationModel::Store::Preview::InstallControl;
using namespace winrt::Windows::Management::Deployment;

namespace
{
Expand Down Expand Up @@ -213,6 +215,50 @@ namespace AppInstaller::MSStore
std::atomic_bool m_isUpdating = false;
};

// After a successful machine-scope Store install, the package may not be provisioned on older
// Windows versions (fixed in Windows 11 26100). This helper ensures the package is provisioned
// as a best-effort mitigation.
void EnsureProvisionedForMachineScope(const IVectorView<AppInstallItem>& installItems, std::wstring_view productId, IProgressCallback& progress) try
{
PackageManager packageManager;
auto provisionedPackages = packageManager.FindProvisionedPackages();

for (auto const& installItem : installItems)
{
if (Utility::CaseInsensitiveEquals(installItem.ProductId(), productId))
{
std::wstring familyName{ installItem.PackageFamilyName() };
if (familyName.empty())
{
continue;
}

bool isProvisioned = false;
for (auto const& provisionedPkg : provisionedPackages)
{
if (provisionedPkg.Id().FamilyName() == familyName)
{
isProvisioned = true;
break;
}
}

if (!isProvisioned)
{
AICLI_LOG(Core, Info, << "Package not provisioned after machine scope install, provisioning: " << Utility::ConvertToUTF8(familyName));
try
{
auto operation = packageManager.ProvisionPackageForAllUsersAsync(familyName);
Deployment::WaitForDeployment(operation, progress);
AICLI_LOG(Core, Info, << "Successfully provisioned package: " << Utility::ConvertToUTF8(familyName));
}
CATCH_LOG();
}
}
}
}
CATCH_LOG();

HRESULT WaitForOperation(const std::wstring& productId, bool isSilentMode, IVectorView<AppInstallItem>& installItems, IProgressCallback& progress, const PackageUpdateMonitor& monitor)
{
auto cancelIfOperationFailed = wil::scope_exit(
Expand Down Expand Up @@ -321,6 +367,18 @@ namespace AppInstaller::MSStore
}
}

#ifndef AICLI_DISABLE_TEST_HOOKS
namespace TestHooks
{
static bool* s_ProvisionAfterInstall = nullptr;

void TestHook_SetProvisionAfterInstall(bool* value)
{
s_ProvisionAfterInstall = value;
}
}
#endif

HRESULT MSStoreOperation::InstallPackage(IProgressCallback& progress)
{
PackageUpdateMonitor monitor;
Expand Down Expand Up @@ -378,7 +436,25 @@ namespace AppInstaller::MSStore
installOptions).get();
}

return WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor);
HRESULT hr = WaitForOperation(m_productId, m_isSilentMode, installItems, progress, monitor);

bool shouldProvision = m_scope == Manifest::ScopeEnum::Machine;
#ifndef AICLI_DISABLE_TEST_HOOKS
if (TestHooks::s_ProvisionAfterInstall)
{
shouldProvision = *TestHooks::s_ProvisionAfterInstall;
}
#endif

if (SUCCEEDED(hr) && shouldProvision)
{
// Mitigation: on older OS versions (fixed in Windows 11 26100) the Store install service
// may not provision the package even when InstallForAllUsers was set.
// Explicitly provision if not already provisioned.
EnsureProvisionedForMachineScope(installItems, m_productId, progress);
}

return hr;
}

HRESULT MSStoreOperation::UpdatePackage(IProgressCallback& progress)
Expand Down
6 changes: 6 additions & 0 deletions src/AppInstallerCommonCore/Public/AppInstallerDeployment.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ namespace AppInstaller::Deployment
std::vector<std::pair<std::string, std::wstring>> ExpectedDigests;
};

// Waits for a deployment operation.
HRESULT WaitForDeployment(
winrt::Windows::Foundation::IAsyncOperationWithProgress<winrt::Windows::Management::Deployment::DeploymentResult, winrt::Windows::Management::Deployment::DeploymentProgress>& deployOperation,
IProgressCallback& callback,
bool throwOnError = true);

// Calls winrt::Windows::Management::Deployment::PackageManager::AddPackageAsync if skipSmartScreen is true,
// Otherwise, calls winrt::Windows::Management::Deployment::PackageManager::RequestAddPackageAsync
void AddPackage(
Expand Down
Loading