From 6f43aac26af3bbbc275240ba73c5835a1c0a80a6 Mon Sep 17 00:00:00 2001
From: Kai Tao <69313318+vanzue@users.noreply.github.com>
Date: Wed, 9 Apr 2025 07:16:08 +0800
Subject: [PATCH] [CmdPal] [Install] do not install dependency if already
satisfied. (#38531)
* do not install dependency if already satisfied.
* self contain winappsdk
---
installer/PowerToysSetup/CmdPal.wxs | 2 -
src/common/utils/package.h | 183 +++++++++++++++++-
.../Microsoft.CmdPal.UI.csproj | 1 +
3 files changed, 180 insertions(+), 6 deletions(-)
diff --git a/installer/PowerToysSetup/CmdPal.wxs b/installer/PowerToysSetup/CmdPal.wxs
index 89a813979b..c74e27d6c1 100644
--- a/installer/PowerToysSetup/CmdPal.wxs
+++ b/installer/PowerToysSetup/CmdPal.wxs
@@ -40,7 +40,6 @@
-
@@ -50,7 +49,6 @@
-
diff --git a/src/common/utils/package.h b/src/common/utils/package.h
index a3ce07db22..60bde7ea53 100644
--- a/src/common/utils/package.h
+++ b/src/common/utils/package.h
@@ -2,11 +2,14 @@
#include
+#include
#include
#include
#include
#include
#include
+#include
+#include
#include
#include
@@ -15,11 +18,12 @@
#include "../logger/logger.h"
#include "../version/version.h"
-namespace package {
-
+namespace package
+{
using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::Management::Deployment;
+ using Microsoft::WRL::ComPtr;
inline BOOL IsWin11OrGreater()
{
@@ -46,6 +50,118 @@ namespace package {
dwlConditionMask);
}
+ struct PACKAGE_VERSION
+ {
+ UINT16 Major;
+ UINT16 Minor;
+ UINT16 Build;
+ UINT16 Revision;
+ };
+
+ class ComInitializer
+ {
+ public:
+ explicit ComInitializer(DWORD coInitFlags = COINIT_MULTITHREADED) :
+ _initialized(false)
+ {
+ const HRESULT hr = CoInitializeEx(nullptr, coInitFlags);
+ _initialized = SUCCEEDED(hr);
+ }
+
+ ~ComInitializer()
+ {
+ if (_initialized)
+ {
+ CoUninitialize();
+ }
+ }
+
+ bool Succeeded() const { return _initialized; }
+
+ private:
+ bool _initialized;
+ };
+
+ inline bool GetPackageNameAndVersionFromAppx(
+ const std::wstring& appxPath,
+ std::wstring& outName,
+ PACKAGE_VERSION& outVersion)
+ {
+ try
+ {
+ ComInitializer comInit;
+ if (!comInit.Succeeded())
+ {
+ Logger::error(L"COM initialization failed.");
+ return false;
+ }
+
+ ComPtr factory;
+ ComPtr stream;
+ ComPtr reader;
+ ComPtr manifest;
+ ComPtr packageId;
+
+ HRESULT hr = CoCreateInstance(__uuidof(AppxFactory), nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory));
+ if (FAILED(hr))
+ return false;
+
+ hr = SHCreateStreamOnFileEx(appxPath.c_str(), STGM_READ | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
+ if (FAILED(hr))
+ return false;
+
+ hr = factory->CreatePackageReader(stream.Get(), &reader);
+ if (FAILED(hr))
+ return false;
+
+ hr = reader->GetManifest(&manifest);
+ if (FAILED(hr))
+ return false;
+
+ hr = manifest->GetPackageId(&packageId);
+ if (FAILED(hr))
+ return false;
+
+ LPWSTR name = nullptr;
+ hr = packageId->GetName(&name);
+ if (FAILED(hr))
+ return false;
+
+ UINT64 version = 0;
+ hr = packageId->GetVersion(&version);
+ if (FAILED(hr))
+ return false;
+
+ outName = std::wstring(name);
+ CoTaskMemFree(name);
+
+ outVersion.Major = static_cast((version >> 48) & 0xFFFF);
+ outVersion.Minor = static_cast((version >> 32) & 0xFFFF);
+ outVersion.Build = static_cast((version >> 16) & 0xFFFF);
+ outVersion.Revision = static_cast(version & 0xFFFF);
+
+ Logger::info(L"Package name: {}, version: {}.{}.{}.{}, appxPath: {}",
+ outName,
+ outVersion.Major,
+ outVersion.Minor,
+ outVersion.Build,
+ outVersion.Revision,
+ appxPath);
+
+ return true;
+ }
+ catch (const std::exception& ex)
+ {
+ Logger::error(L"Standard exception: {}", winrt::to_hstring(ex.what()));
+ return false;
+ }
+ catch (...)
+ {
+ Logger::error(L"Unknown or non-standard exception occurred.");
+ return false;
+ }
+ }
+
inline std::optional GetRegisteredPackage(std::wstring packageDisplayName, bool checkVersion)
{
PackageManager packageManager;
@@ -229,6 +345,59 @@ namespace package {
return matchedFiles;
}
+ inline bool IsPackageSatisfied(const std::wstring& appxPath)
+ {
+ std::wstring targetName;
+ PACKAGE_VERSION targetVersion{};
+
+ if (!GetPackageNameAndVersionFromAppx(appxPath, targetName, targetVersion))
+ {
+ Logger::error(L"Failed to get package name and version from appx: " + appxPath);
+ return false;
+ }
+
+ PackageManager pm;
+
+ for (const auto& package : pm.FindPackagesForUser({}))
+ {
+ const auto& id = package.Id();
+ if (std::wstring(id.Name()) == targetName)
+ {
+ const auto& version = id.Version();
+
+ if (version.Major > targetVersion.Major ||
+ (version.Major == targetVersion.Major && version.Minor > targetVersion.Minor) ||
+ (version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build > targetVersion.Build) ||
+ (version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build == targetVersion.Build && version.Revision >= targetVersion.Revision))
+ {
+ Logger::info(
+ L"Package {} is already satisfied with version {}.{}.{}.{}; target version {}.{}.{}.{}; appxPath: {}",
+ id.Name(),
+ version.Major,
+ version.Minor,
+ version.Build,
+ version.Revision,
+ targetVersion.Major,
+ targetVersion.Minor,
+ targetVersion.Build,
+ targetVersion.Revision,
+ appxPath);
+ return true;
+ }
+ }
+ }
+
+ Logger::info(
+ L"Package {} is not satisfied. Target version: {}.{}.{}.{}; appxPath: {}",
+ targetName,
+ targetVersion.Major,
+ targetVersion.Minor,
+ targetVersion.Build,
+ targetVersion.Revision,
+ appxPath);
+ return false;
+ }
+
inline bool RegisterPackage(std::wstring pkgPath, std::vector dependencies)
{
try
@@ -247,7 +416,14 @@ namespace package {
{
try
{
- uris.Append(Uri(dependency));
+ if (IsPackageSatisfied(dependency))
+ {
+ Logger::info(L"Dependency already satisfied: {}", dependency);
+ }
+ else
+ {
+ uris.Append(Uri(dependency));
+ }
}
catch (const winrt::hresult_error& ex)
{
@@ -282,7 +458,6 @@ namespace package {
{
Logger::debug(L"Register {} package started.", pkgPath);
}
-
}
catch (std::exception& e)
{
diff --git a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj
index 9a4061f6bc..e1f891558d 100644
--- a/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj
+++ b/src/modules/cmdpal/Microsoft.CmdPal.UI/Microsoft.CmdPal.UI.csproj
@@ -13,6 +13,7 @@
true
enable
enable
+ true
$(CmdPalVersion)