22
33#include < Windows.h>
44
5+ #include < appxpackaging.h>
56#include < exception>
67#include < filesystem>
78#include < regex>
89#include < string>
910#include < optional>
11+ #include < Shlwapi.h>
12+ #include < wrl/client.h>
1013
1114#include < winrt/Windows.ApplicationModel.h>
1215#include < winrt/Windows.Foundation.h>
1518#include " ../logger/logger.h"
1619#include " ../version/version.h"
1720
18- namespace package {
19-
21+ namespace package
22+ {
2023 using namespace winrt ::Windows::Foundation;
2124 using namespace winrt ::Windows::ApplicationModel;
2225 using namespace winrt ::Windows::Management::Deployment;
26+ using Microsoft::WRL::ComPtr;
2327
2428 inline BOOL IsWin11OrGreater ()
2529 {
@@ -46,6 +50,118 @@ namespace package {
4650 dwlConditionMask);
4751 }
4852
53+ struct PACKAGE_VERSION
54+ {
55+ UINT16 Major;
56+ UINT16 Minor;
57+ UINT16 Build;
58+ UINT16 Revision;
59+ };
60+
61+ class ComInitializer
62+ {
63+ public:
64+ explicit ComInitializer (DWORD coInitFlags = COINIT_MULTITHREADED) :
65+ _initialized(false )
66+ {
67+ const HRESULT hr = CoInitializeEx (nullptr , coInitFlags);
68+ _initialized = SUCCEEDED (hr);
69+ }
70+
71+ ~ComInitializer ()
72+ {
73+ if (_initialized)
74+ {
75+ CoUninitialize ();
76+ }
77+ }
78+
79+ bool Succeeded () const { return _initialized; }
80+
81+ private:
82+ bool _initialized;
83+ };
84+
85+ inline bool GetPackageNameAndVersionFromAppx (
86+ const std::wstring& appxPath,
87+ std::wstring& outName,
88+ PACKAGE_VERSION& outVersion)
89+ {
90+ try
91+ {
92+ ComInitializer comInit;
93+ if (!comInit.Succeeded ())
94+ {
95+ Logger::error (L" COM initialization failed." );
96+ return false ;
97+ }
98+
99+ ComPtr<IAppxFactory> factory;
100+ ComPtr<IStream> stream;
101+ ComPtr<IAppxPackageReader> reader;
102+ ComPtr<IAppxManifestReader> manifest;
103+ ComPtr<IAppxManifestPackageId> packageId;
104+
105+ HRESULT hr = CoCreateInstance (__uuidof (AppxFactory), nullptr , CLSCTX_INPROC_SERVER, IID_PPV_ARGS (&factory));
106+ if (FAILED (hr))
107+ return false ;
108+
109+ hr = SHCreateStreamOnFileEx (appxPath.c_str (), STGM_READ | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_NORMAL, FALSE , nullptr , &stream);
110+ if (FAILED (hr))
111+ return false ;
112+
113+ hr = factory->CreatePackageReader (stream.Get (), &reader);
114+ if (FAILED (hr))
115+ return false ;
116+
117+ hr = reader->GetManifest (&manifest);
118+ if (FAILED (hr))
119+ return false ;
120+
121+ hr = manifest->GetPackageId (&packageId);
122+ if (FAILED (hr))
123+ return false ;
124+
125+ LPWSTR name = nullptr ;
126+ hr = packageId->GetName (&name);
127+ if (FAILED (hr))
128+ return false ;
129+
130+ UINT64 version = 0 ;
131+ hr = packageId->GetVersion (&version);
132+ if (FAILED (hr))
133+ return false ;
134+
135+ outName = std::wstring (name);
136+ CoTaskMemFree (name);
137+
138+ outVersion.Major = static_cast <UINT16>((version >> 48 ) & 0xFFFF );
139+ outVersion.Minor = static_cast <UINT16>((version >> 32 ) & 0xFFFF );
140+ outVersion.Build = static_cast <UINT16>((version >> 16 ) & 0xFFFF );
141+ outVersion.Revision = static_cast <UINT16>(version & 0xFFFF );
142+
143+ Logger::info (L" Package name: {}, version: {}.{}.{}.{}, appxPath: {}" ,
144+ outName,
145+ outVersion.Major ,
146+ outVersion.Minor ,
147+ outVersion.Build ,
148+ outVersion.Revision ,
149+ appxPath);
150+
151+ return true ;
152+ }
153+ catch (const std::exception& ex)
154+ {
155+ Logger::error (L" Standard exception: {}" , winrt::to_hstring (ex.what ()));
156+ return false ;
157+ }
158+ catch (...)
159+ {
160+ Logger::error (L" Unknown or non-standard exception occurred." );
161+ return false ;
162+ }
163+ }
164+
49165 inline std::optional<Package> GetRegisteredPackage (std::wstring packageDisplayName, bool checkVersion)
50166 {
51167 PackageManager packageManager;
@@ -229,6 +345,59 @@ namespace package {
229345 return matchedFiles;
230346 }
231347
348+ inline bool IsPackageSatisfied (const std::wstring& appxPath)
349+ {
350+ std::wstring targetName;
351+ PACKAGE_VERSION targetVersion{};
352+
353+ if (!GetPackageNameAndVersionFromAppx (appxPath, targetName, targetVersion))
354+ {
355+ Logger::error (L" Failed to get package name and version from appx: " + appxPath);
356+ return false ;
357+ }
358+
359+ PackageManager pm;
360+
361+ for (const auto & package : pm.FindPackagesForUser ({}))
362+ {
363+ const auto & id = package.Id ();
364+ if (std::wstring (id.Name ()) == targetName)
365+ {
366+ const auto & version = id.Version ();
367+
368+ if (version.Major > targetVersion.Major ||
369+ (version.Major == targetVersion.Major && version.Minor > targetVersion.Minor ) ||
370+ (version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build > targetVersion.Build ) ||
371+ (version.Major == targetVersion.Major && version.Minor == targetVersion.Minor && version.Build == targetVersion.Build && version.Revision >= targetVersion.Revision ))
372+ {
373+ Logger::info (
374+ L" Package {} is already satisfied with version {}.{}.{}.{}; target version {}.{}.{}.{}; appxPath: {}" ,
375+ id.Name (),
376+ version.Major ,
377+ version.Minor ,
378+ version.Build ,
379+ version.Revision ,
380+ targetVersion.Major ,
381+ targetVersion.Minor ,
382+ targetVersion.Build ,
383+ targetVersion.Revision ,
384+ appxPath);
385+ return true ;
386+ }
387+ }
388+ }
389+
390+ Logger::info (
391+ L" Package {} is not satisfied. Target version: {}.{}.{}.{}; appxPath: {}" ,
392+ targetName,
393+ targetVersion.Major ,
394+ targetVersion.Minor ,
395+ targetVersion.Build ,
396+ targetVersion.Revision ,
397+ appxPath);
398+ return false ;
399+ }
400+
232401 inline bool RegisterPackage (std::wstring pkgPath, std::vector<std::wstring> dependencies)
233402 {
234403 try
@@ -247,7 +416,14 @@ namespace package {
247416 {
248417 try
249418 {
250- uris.Append (Uri (dependency));
419+ if (IsPackageSatisfied (dependency))
420+ {
421+ Logger::info (L" Dependency already satisfied: {}" , dependency);
422+ }
423+ else
424+ {
425+ uris.Append (Uri (dependency));
426+ }
251427 }
252428 catch (const winrt::hresult_error& ex)
253429 {
@@ -282,7 +458,6 @@ namespace package {
282458 {
283459 Logger::debug (L" Register {} package started." , pkgPath);
284460 }
285-
286461 }
287462 catch (std::exception& e)
288463 {
0 commit comments