From 83f8f26f18aa362813c3e284d8d7d1a0ab51b17e Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Fri, 22 Jul 2022 15:59:56 -0700 Subject: [PATCH 01/17] interface --- src/AppInstallerCommonCore/Filesystem.cpp | 8 ++ .../Manifest/ManifestValidation.cpp | 8 ++ .../Public/winget/Filesystem.h | 4 +- .../PackageManager.idl | 133 ++++++++++++++++-- 4 files changed, 141 insertions(+), 12 deletions(-) diff --git a/src/AppInstallerCommonCore/Filesystem.cpp b/src/AppInstallerCommonCore/Filesystem.cpp index 0d69524704..b7423e65e5 100644 --- a/src/AppInstallerCommonCore/Filesystem.cpp +++ b/src/AppInstallerCommonCore/Filesystem.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerStrings.h" +#include "public/winget/FileSystem.h" namespace AppInstaller::Filesystem { @@ -131,4 +132,11 @@ namespace AppInstaller::Filesystem // but it is better to succeed the operation and leave a file around than to fail. std::filesystem::copy_file(from, to, std::filesystem::copy_options::overwrite_existing); } + + std::filesystem::path GetExpandedPath(const std::string& path) + { + std::wstring widePath = Utility::ConvertToUTF16(path); + WCHAR buffer[MAX_PATH]; + return PathUnExpandEnvStrings(widePath.c_str(), buffer, ARRAYSIZE(buffer)) ? std::filesystem::path{ buffer } : std::filesystem::path{ path }; + } } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp index 1abd30fe4e..625adb56a7 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -46,6 +46,14 @@ namespace AppInstaller::Manifest { return in1.InstallerType < in2.InstallerType; } + else if (IsArchiveType(in1.InstallerType)) + { + // Compare nested installer type if installer type is archive. + if (in1.NestedInstallerType != in2.NestedInstallerType) + { + return in1.NestedInstallerType != in2.NestedInstallerType; + } + } if (in1.Arch != in2.Arch) { diff --git a/src/AppInstallerCommonCore/Public/winget/Filesystem.h b/src/AppInstallerCommonCore/Public/winget/Filesystem.h index 6a142e7bd5..4f2ad19b77 100644 --- a/src/AppInstallerCommonCore/Public/winget/Filesystem.h +++ b/src/AppInstallerCommonCore/Public/winget/Filesystem.h @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #pragma once -#include "pch.h" +#include namespace AppInstaller::Filesystem { @@ -19,4 +19,6 @@ namespace AppInstaller::Filesystem // Renames the file to a new path. void RenameFile(const std::filesystem::path& from, const std::filesystem::path& to); + + std::filesystem::path GetExpandedPath(const std::string& path); } \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index 8afa0b19cd..e46728dd2e 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -70,20 +70,20 @@ namespace Microsoft.Management.Deployment runtimeclass InstallResult { /// Used by a caller to correlate the install with a caller's data. - String CorrelationData{ get; }; + String CorrelationData { get; }; /// Whether a restart is required to complete the install. - Boolean RebootRequired{ get; }; + Boolean RebootRequired { get; }; /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED - InstallResultStatus Status{ get; }; + InstallResultStatus Status { get; }; /// The error code of the overall operation. - HRESULT ExtendedErrorCode{ get; }; + HRESULT ExtendedErrorCode { get; }; [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 4)] { /// The error code from the install attempt. Only valid if the Status is InstallError. /// This value's meaning will require knowledge of the specific installer or install technology. - UInt32 InstallerErrorCode{ get; }; + UInt32 InstallerErrorCode { get; }; } } @@ -133,18 +133,18 @@ namespace Microsoft.Management.Deployment runtimeclass UninstallResult { /// Used by a caller to correlate the install with a caller's data. - String CorrelationData{ get; }; + String CorrelationData { get; }; /// Whether a restart is required to complete the install. - Boolean RebootRequired{ get; }; + Boolean RebootRequired { get; }; /// Batched error code, example APPINSTALLER_CLI_ERROR_SHELLEXEC_INSTALL_FAILED - UninstallResultStatus Status{ get; }; + UninstallResultStatus Status { get; }; /// The error code of the overall operation. - HRESULT ExtendedErrorCode{ get; }; + HRESULT ExtendedErrorCode { get; }; /// The error code from the uninstall attempt. Only valid if the Status is UninstallError. /// This value's meaning will require knowledge of the specific uninstaller or install technology. - UInt32 UninstallerErrorCode{ get; }; + UInt32 UninstallerErrorCode { get; }; } /// IMPLEMENTATION NOTE: SourceOrigin from winget/RepositorySource.h @@ -295,6 +295,111 @@ namespace Microsoft.Management.Deployment String Channel { get; }; }; + /// The package installer type. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + enum PackageInstallerType + { + /// Unknown type. + Unknown, + /// Inno type. + Inno, + /// Wix type. + Wix, + /// Msi type. + Msi, + /// Nullsoft type. + Nullsoft, + /// Zip type. + Zip, + /// Msix or Appx type. + Msix, + /// Exe type. + Exe, + /// Burn type. + Burn, + /// MSStore type. + MSStore, + /// Portable type. + Portable, + }; + + /// The package installer scope. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + enum PackageInstallerScope + { + /// Scope not declared. + Unknown, + /// User scope. + User, + /// System scope. + System, + }; + + /// Interface for retrieving information about a package installer. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + runtimeclass PackageInstallerInfo + { + /// The package installer type. + PackageInstallerType InstallerType { get; }; + /// The nested package installer type for archives. + PackageInstallerType NestedInstallerType { get; }; + /// The package installer architecture. + Windows.System.ProcessorArchitecture Architecture { get; }; + /// The package installer scope. + PackageInstallerScope Scope { get; }; + /// The package installer locale. + String Locale{ get; }; + }; + + /// The installed status type. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + [flags] + enum InstalledStatusType + { + /// None is checked. + None = 0x0, + /// Check Apps and Features entry. + AppsAndFeaturesEntry = 0x0001, + /// Check Apps and Features entry install location if applicable. + AppsAndFeaturesEntryInstallLocation = 0x0002, + /// Check Apps and Features entry install location with installed files if applicable. + AppsAndFeaturesEntryInstallLocationFile = 0x0004, + /// Check default install location if applicable. + DefaultInstallLocation = 0x0008, + /// Check default install location with installed files if applicable. + DefaultInstallLocationFile = 0x0010, + }; + + /// Interface representing an individual installed status. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + runtimeclass InstalledStatus + { + /// The installed status type. + InstalledStatusType Type { get; }; + /// The installed status path. + String Path { get; }; + /// The installed status result. + HRESULT Status { get; }; + }; + + /// Interface for retrieving information about a package installer installed status. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + runtimeclass PackageInstallerInstalledStatus + { + /// The package installer info. + PackageInstallerInfo InstallerInfo { get; }; + /// A list of various types of installed status of the package installer. + Windows.Foundation.Collections.IVectorView InstalledStatus { get; }; + }; + + /// Interface for retrieving information about a package installer installed status. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + runtimeclass PackageInstalledStatus + { + /// A list of package installer installed status. + Windows.Foundation.Collections.IVectorView InstalledStatus { get; }; + }; + /// IMPLEMENTATION NOTE: IPackage from winget/RepositorySearch.h /// A package, potentially containing information about it's local state and the available versions. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 1)] @@ -306,7 +411,7 @@ namespace Microsoft.Management.Deployment String Name { get; }; /// Gets the installed package information if the package is installed. - PackageVersionInfo InstalledVersion{ get; }; + PackageVersionInfo InstalledVersion { get; }; /// Gets all available versions of this package. Ordering is not guaranteed. Windows.Foundation.Collections.IVectorView AvailableVersions { get; }; @@ -320,6 +425,12 @@ namespace Microsoft.Management.Deployment /// Gets a value indicating whether an available version is newer than the installed version. Boolean IsUpdateAvailable { get; }; + /// Check the installed status of the package. It's recommended to call this method from a composite package + /// with installed source info for more accurate and complete installed status. + /// This may require downloading information from a server. + Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(); + PackageInstalledStatus CheckInstalledStatus(); + /// DESIGN NOTE: /// IsSame from IPackage in winget/RepositorySearch is not implemented in V1. /// Determines if the given IPackage refers to the same package as this one. From a0f421990cc1bc4c83e3c0c4e581560c2052c409 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Fri, 22 Jul 2022 18:03:35 -0700 Subject: [PATCH 02/17] build --- .../Public/winget/Filesystem.h | 1 + .../CatalogPackage.cpp | 10 ++++++++++ src/Microsoft.Management.Deployment/CatalogPackage.h | 5 +++++ .../PackageManager.idl | 12 ++++++++++-- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCommonCore/Public/winget/Filesystem.h b/src/AppInstallerCommonCore/Public/winget/Filesystem.h index 4f2ad19b77..7283165d9c 100644 --- a/src/AppInstallerCommonCore/Public/winget/Filesystem.h +++ b/src/AppInstallerCommonCore/Public/winget/Filesystem.h @@ -20,5 +20,6 @@ namespace AppInstaller::Filesystem // Renames the file to a new path. void RenameFile(const std::filesystem::path& from, const std::filesystem::path& to); + // Get expanded file system path. std::filesystem::path GetExpandedPath(const std::string& path); } \ No newline at end of file diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp index 56b1c4404e..e9214b464b 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.cpp +++ b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -96,6 +96,16 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_package->IsUpdateAvailable(); } + Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync( + Microsoft::Management::Deployment::InstalledStatusType types) + { + co_return CheckInstalledStatus(types); + } + Microsoft::Management::Deployment::PackageInstalledStatus CatalogPackage::CheckInstalledStatus( + Microsoft::Management::Deployment::InstalledStatusType) + { + throw hresult_not_implemented(); + } std::shared_ptr<::AppInstaller::Repository::IPackage> CatalogPackage::GetRepositoryPackage() { return m_package; diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h index 87a51fe254..451bbe366b 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.h +++ b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -23,6 +23,11 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Microsoft::Management::Deployment::PackageVersionInfo DefaultInstallVersion(); winrt::Microsoft::Management::Deployment::PackageVersionInfo GetPackageVersionInfo(winrt::Microsoft::Management::Deployment::PackageVersionId const& versionKey); bool IsUpdateAvailable(); + // Contract 5.0 + winrt::Windows::Foundation::IAsyncOperation CheckInstalledStatusAsync( + winrt::Microsoft::Management::Deployment::InstalledStatusType types); + winrt::Microsoft::Management::Deployment::PackageInstalledStatus CheckInstalledStatus( + winrt::Microsoft::Management::Deployment::InstalledStatusType types); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index e46728dd2e..6ee655ca2c 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -428,8 +428,8 @@ namespace Microsoft.Management.Deployment /// Check the installed status of the package. It's recommended to call this method from a composite package /// with installed source info for more accurate and complete installed status. /// This may require downloading information from a server. - Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(); - PackageInstalledStatus CheckInstalledStatus(); + Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType types); + PackageInstalledStatus CheckInstalledStatus(InstalledStatusType types); /// DESIGN NOTE: /// IsSame from IPackage in winget/RepositorySearch is not implemented in V1. @@ -864,5 +864,13 @@ namespace Microsoft.Management.Deployment interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; } } From f5a20702de9589eb5e182c2617dc4ea84d8957e0 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Fri, 22 Jul 2022 20:52:52 -0700 Subject: [PATCH 03/17] repo interface --- src/AppInstallerCLITests/TestSource.cpp | 5 +++ src/AppInstallerCLITests/TestSource.h | 1 + .../CompositeSource.cpp | 5 +++ .../Microsoft/SQLiteIndexSource.cpp | 10 +++++ .../Public/winget/RepositorySearch.h | 44 +++++++++++++++++++ .../Rest/RestSource.cpp | 5 +++ .../PackageManager.idl | 4 +- 7 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index b25e867a27..0af7f043e0 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -223,6 +223,11 @@ namespace TestCommon return false; } + std::vector TestPackage::CheckInstalledStatus(InstalledStatusType) const + { + return {}; + } + bool TestPackage::IsSame(const IPackage* other) const { if (IsSameOverride) diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h index f99bab03a4..a28de9418c 100644 --- a/src/AppInstallerCLITests/TestSource.h +++ b/src/AppInstallerCLITests/TestSource.h @@ -67,6 +67,7 @@ namespace TestCommon std::shared_ptr GetLatestAvailableVersion() const override; std::shared_ptr GetAvailableVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; bool IsUpdateAvailable() const override; + std::vector CheckInstalledStatus(AppInstaller::Repository::InstalledStatusType types) const override; bool IsSame(const IPackage* other) const override; std::shared_ptr InstalledVersion; diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 50471d360d..52da03ebcc 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -400,6 +400,11 @@ namespace AppInstaller::Repository return (latest && (GetVACFromVersion(installed.get()).IsUpdatedBy(GetVACFromVersion(latest.get())))); } + std::vector CheckInstalledStatus(InstalledStatusType) const override + { + return {}; + } + bool IsSame(const IPackage* other) const override { const CompositePackage* otherComposite = dynamic_cast(other); diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index bb74dda174..c354ed6fce 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -282,6 +282,11 @@ namespace AppInstaller::Repository::Microsoft return false; } + std::vector CheckInstalledStatus(InstalledStatusType) const override + { + return {}; + } + bool IsSame(const IPackage* other) const override { const AvailablePackage* otherAvailable = dynamic_cast(other); @@ -331,6 +336,11 @@ namespace AppInstaller::Repository::Microsoft return false; } + std::vector CheckInstalledStatus(InstalledStatusType) const override + { + return {}; + } + bool IsSame(const IPackage* other) const override { const InstalledPackage* otherInstalled = dynamic_cast(other); diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index 9dd72d70e5..6babb11c92 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -245,6 +245,47 @@ namespace AppInstaller::Repository Name, }; + // Defines the installed status check type. + enum class InstalledStatusType : uint32_t + { + // None is checked. + None = 0x0, + // Check Apps and Features entry. + AppsAndFeaturesEntry = 0x0001, + // Check Apps and Features entry install location if applicable. + AppsAndFeaturesEntryInstallLocation = 0x0002, + // Check Apps and Features entry install location with installed files if applicable. + AppsAndFeaturesEntryInstallLocationFile = 0x0004, + // Check default install location if applicable. + DefaultInstallLocation = 0x0008, + // Check default install location with installed files if applicable. + DefaultInstallLocationFile = 0x0010, + + // All + All = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile | + DefaultInstallLocation | DefaultInstallLocationFile, + }; + + DEFINE_ENUM_FLAG_OPERATORS(InstalledStatusType); + + // Struct representing an individual installed status. + struct InstalledStatus + { + // The installed status type. + InstalledStatusType Type = InstalledStatusType::None; + // The installed status path. + Utility::NormalizedString Path; + // The installed status result. + HRESULT Status; + }; + + // Struct representing installed status from an installer. + struct InstallerInstalledStatus + { + Manifest::ManifestInstaller Installer; + std::vector InstalledStatus; + }; + // A package, potentially containing information about it's local state and the available versions. struct IPackage { @@ -270,6 +311,9 @@ namespace AppInstaller::Repository // Gets a value indicating whether an available version is newer than the installed version. virtual bool IsUpdateAvailable() const = 0; + // Checks installed status of the package. + virtual std::vector CheckInstalledStatus(InstalledStatusType types = InstalledStatusType::All) const = 0; + // Determines if the given IPackage refers to the same package as this one. virtual bool IsSame(const IPackage*) const = 0; }; diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index e5a7ba0d78..f747462cdb 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -85,6 +85,11 @@ namespace AppInstaller::Repository::Rest return false; } + std::vector CheckInstalledStatus(InstalledStatusType) const override + { + return {}; + } + bool IsSame(const IPackage* other) const override { const AvailablePackage* otherAvailablePackage = dynamic_cast(other); diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index 6ee655ca2c..4c5a0be657 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -425,8 +425,8 @@ namespace Microsoft.Management.Deployment /// Gets a value indicating whether an available version is newer than the installed version. Boolean IsUpdateAvailable { get; }; - /// Check the installed status of the package. It's recommended to call this method from a composite package - /// with installed source info for more accurate and complete installed status. + /// Check the installed status of the package. It's required to call this method from a composite package + /// with installed info for more accurate and complete installed status. /// This may require downloading information from a server. Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType types); PackageInstalledStatus CheckInstalledStatus(InstalledStatusType types); From 41b53ba8d18c7d60be791810201e8f2b6351820d Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Sat, 23 Jul 2022 23:37:28 -0700 Subject: [PATCH 04/17] ResultStatus to follow convention --- .../CatalogPackage.cpp | 4 ++-- .../CatalogPackage.h | 4 ++-- .../PackageManager.idl | 21 ++++++++++++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp index e9214b464b..4465625fdf 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.cpp +++ b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -96,12 +96,12 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_package->IsUpdateAvailable(); } - Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync( + Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync( Microsoft::Management::Deployment::InstalledStatusType types) { co_return CheckInstalledStatus(types); } - Microsoft::Management::Deployment::PackageInstalledStatus CatalogPackage::CheckInstalledStatus( + Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus( Microsoft::Management::Deployment::InstalledStatusType) { throw hresult_not_implemented(); diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h index 451bbe366b..7325272fa6 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.h +++ b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -24,9 +24,9 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Microsoft::Management::Deployment::PackageVersionInfo GetPackageVersionInfo(winrt::Microsoft::Management::Deployment::PackageVersionId const& versionKey); bool IsUpdateAvailable(); // Contract 5.0 - winrt::Windows::Foundation::IAsyncOperation CheckInstalledStatusAsync( + winrt::Windows::Foundation::IAsyncOperation CheckInstalledStatusAsync( winrt::Microsoft::Management::Deployment::InstalledStatusType types); - winrt::Microsoft::Management::Deployment::PackageInstalledStatus CheckInstalledStatus( + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CheckInstalledStatus( winrt::Microsoft::Management::Deployment::InstalledStatusType types); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index 4c5a0be657..c343e1e059 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -392,10 +392,21 @@ namespace Microsoft.Management.Deployment Windows.Foundation.Collections.IVectorView InstalledStatus { get; }; }; + /// Status of the check installed status call. + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + enum CheckInstalledStatusResultStatus + { + Ok, + InternalError, + }; + /// Interface for retrieving information about a package installer installed status. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] - runtimeclass PackageInstalledStatus + runtimeclass CheckInstalledStatusResult { + /// Status of the check installed status call. + CheckInstalledStatusResultStatus Status { get; }; + /// A list of package installer installed status. Windows.Foundation.Collections.IVectorView InstalledStatus { get; }; }; @@ -428,8 +439,8 @@ namespace Microsoft.Management.Deployment /// Check the installed status of the package. It's required to call this method from a composite package /// with installed info for more accurate and complete installed status. /// This may require downloading information from a server. - Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType types); - PackageInstalledStatus CheckInstalledStatus(InstalledStatusType types); + Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType types); + CheckInstalledStatusResult CheckInstalledStatus(InstalledStatusType types); /// DESIGN NOTE: /// IsSame from IPackage in winget/RepositorySearch is not implemented in V1. @@ -870,7 +881,7 @@ namespace Microsoft.Management.Deployment interface Windows.Foundation.Collections.IVectorView; interface Windows.Foundation.Collections.IVector; interface Windows.Foundation.Collections.IVectorView; - interface Windows.Foundation.Collections.IVector; - interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; + interface Windows.Foundation.Collections.IVectorView; } } From 34e629f4df448200338c115bd9d5580c221c9337 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 27 Jul 2022 19:59:11 -0700 Subject: [PATCH 05/17] Com layer impl --- .../Public/winget/RepositorySearch.h | 12 +++-- .../CatalogPackage.cpp | 44 ++++++++++++++++-- .../CatalogPackage.h | 6 ++- .../CheckInstalledStatusResult.cpp | 25 ++++++++++ .../CheckInstalledStatusResult.h | 29 ++++++++++++ .../Converters.cpp | 46 +++++++++++++++++++ .../Converters.h | 2 + .../FindPackagesResult.h | 3 +- .../InstalledStatus.cpp | 28 +++++++++++ .../InstalledStatus.h | 28 +++++++++++ .../MatchResult.cpp | 1 - .../MatchResult.h | 3 ++ .../Microsoft.Management.Deployment.vcxproj | 8 ++++ ...soft.Management.Deployment.vcxproj.filters | 8 ++++ .../PackageCatalog.cpp | 1 - .../PackageCatalogInfo.cpp | 1 - .../PackageCatalogInfo.h | 1 + .../PackageInstallerInfo.cpp | 36 +++++++++++++++ .../PackageInstallerInfo.h | 28 +++++++++++ .../PackageInstallerInstalledStatus.cpp | 38 +++++++++++++++ .../PackageInstallerInstalledStatus.h | 28 +++++++++++ .../PackageManager.idl | 25 +++++++--- 22 files changed, 381 insertions(+), 20 deletions(-) create mode 100644 src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp create mode 100644 src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h create mode 100644 src/Microsoft.Management.Deployment/InstalledStatus.cpp create mode 100644 src/Microsoft.Management.Deployment/InstalledStatus.h create mode 100644 src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageInstallerInfo.h create mode 100644 src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp create mode 100644 src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index 6babb11c92..fd8a753b5b 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -261,9 +261,13 @@ namespace AppInstaller::Repository // Check default install location with installed files if applicable. DefaultInstallLocationFile = 0x0010, - // All - All = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile | - DefaultInstallLocation | DefaultInstallLocationFile, + // Below are helper values for calling CheckInstalledStatus as input. + // AppsAndFeaturesEntry related checks + AllAppsAndFeaturesEntryChecks = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile, + // DefaultInstallLocation related checks + AllDefaultInstallLocationChecks = DefaultInstallLocation | DefaultInstallLocationFile, + // All checks + AllChecks = AllAppsAndFeaturesEntryChecks | AllDefaultInstallLocationChecks, }; DEFINE_ENUM_FLAG_OPERATORS(InstalledStatusType); @@ -312,7 +316,7 @@ namespace AppInstaller::Repository virtual bool IsUpdateAvailable() const = 0; // Checks installed status of the package. - virtual std::vector CheckInstalledStatus(InstalledStatusType types = InstalledStatusType::All) const = 0; + virtual std::vector CheckInstalledStatus(InstalledStatusType types = InstalledStatusType::AllChecks) const = 0; // Determines if the given IPackage refers to the same package as this one. virtual bool IsSame(const IPackage*) const = 0; diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp index 4465625fdf..759e2570a6 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.cpp +++ b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -8,6 +8,8 @@ #include "PackageCatalog.h" #include "PackageVersionInfo.h" #include "PackageVersionId.h" +#include "PackageInstallerInstalledStatus.h" +#include "CheckInstalledStatusResult.h" #include namespace winrt::Microsoft::Management::Deployment::implementation @@ -97,14 +99,48 @@ namespace winrt::Microsoft::Management::Deployment::implementation return m_package->IsUpdateAvailable(); } Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync( - Microsoft::Management::Deployment::InstalledStatusType types) + Microsoft::Management::Deployment::InstalledStatusType checkTypes) { - co_return CheckInstalledStatus(types); + co_return CheckInstalledStatus(checkTypes); } Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus( - Microsoft::Management::Deployment::InstalledStatusType) + Microsoft::Management::Deployment::InstalledStatusType checkTypes) { - throw hresult_not_implemented(); + Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::Ok; + Windows::Foundation::Collections::IVector installedStatus{ + winrt::single_threaded_vector() }; + + try + { + auto checkResult = m_package->CheckInstalledStatus(static_cast<::AppInstaller::Repository::InstalledStatusType>(checkTypes)); + + // Build the result object from the checkResult + for (auto const& entry : checkResult) + { + auto checkInstallerResult = winrt::make_self>(); + checkInstallerResult->Initialize(entry); + + installedStatus.Append(*checkInstallerResult); + } + } + catch (...) + { + status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::InternalError; + } + + auto checkInstalledStatusResult = winrt::make_self>(); + checkInstalledStatusResult->Initialize(status, installedStatus); + return *checkInstalledStatusResult; + } + winrt::Windows::Foundation::IAsyncOperation CatalogPackage::CheckInstalledStatusAsync() + { + co_return CheckInstalledStatus(InstalledStatusType::AllChecks); + } + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus() + { + return CheckInstalledStatus(InstalledStatusType::AllChecks); } std::shared_ptr<::AppInstaller::Repository::IPackage> CatalogPackage::GetRepositoryPackage() { diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h index 7325272fa6..291e2cb393 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.h +++ b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -25,9 +25,11 @@ namespace winrt::Microsoft::Management::Deployment::implementation bool IsUpdateAvailable(); // Contract 5.0 winrt::Windows::Foundation::IAsyncOperation CheckInstalledStatusAsync( - winrt::Microsoft::Management::Deployment::InstalledStatusType types); + winrt::Microsoft::Management::Deployment::InstalledStatusType checkTypes); winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CheckInstalledStatus( - winrt::Microsoft::Management::Deployment::InstalledStatusType types); + winrt::Microsoft::Management::Deployment::InstalledStatusType checkTypes); + winrt::Windows::Foundation::IAsyncOperation CheckInstalledStatusAsync(); + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CheckInstalledStatus(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: diff --git a/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp new file mode 100644 index 0000000000..4e75e05f60 --- /dev/null +++ b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckInstalledStatusResult.h" +#include "CheckInstalledStatusResult.g.cpp" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void CheckInstalledStatusResult::Initialize( + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status, + Windows::Foundation::Collections::IVector installedStatus) + { + m_status = status; + m_installedStatus = installedStatus; + } + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus CheckInstalledStatusResult::Status() + { + return m_status; + } + winrt::Windows::Foundation::Collections::IVectorView CheckInstalledStatusResult::InstalledStatus() + { + return m_installedStatus.GetView(); + } +} diff --git a/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h new file mode 100644 index 0000000000..32e90b2595 --- /dev/null +++ b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h @@ -0,0 +1,29 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "CheckInstalledStatusResult.g.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct CheckInstalledStatusResult : CheckInstalledStatusResultT + { + CheckInstalledStatusResult() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize( + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status, + Windows::Foundation::Collections::IVector installedStatus); +#endif + + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus Status(); + winrt::Windows::Foundation::Collections::IVectorView InstalledStatus(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus m_status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::Ok; + Windows::Foundation::Collections::IVector m_installedStatus{ + winrt::single_threaded_vector() }; +#endif + }; +} diff --git a/src/Microsoft.Management.Deployment/Converters.cpp b/src/Microsoft.Management.Deployment/Converters.cpp index d114404c70..fe4aee0b89 100644 --- a/src/Microsoft.Management.Deployment/Converters.cpp +++ b/src/Microsoft.Management.Deployment/Converters.cpp @@ -261,4 +261,50 @@ namespace winrt::Microsoft::Management::Deployment::implementation return std::make_pair(::AppInstaller::Manifest::ScopeEnum::Unknown, false); } + + winrt::Microsoft::Management::Deployment::PackageInstallerType GetDeploymentInstallerType(::AppInstaller::Manifest::InstallerTypeEnum installerType) + { + switch (installerType) + { + case ::AppInstaller::Manifest::InstallerTypeEnum::Burn: + return Microsoft::Management::Deployment::PackageInstallerType::Burn; + case ::AppInstaller::Manifest::InstallerTypeEnum::Exe: + return Microsoft::Management::Deployment::PackageInstallerType::Exe; + case ::AppInstaller::Manifest::InstallerTypeEnum::Inno: + return Microsoft::Management::Deployment::PackageInstallerType::Inno; + case ::AppInstaller::Manifest::InstallerTypeEnum::Msi: + return Microsoft::Management::Deployment::PackageInstallerType::Msi; + case ::AppInstaller::Manifest::InstallerTypeEnum::Msix: + return Microsoft::Management::Deployment::PackageInstallerType::Msix; + case ::AppInstaller::Manifest::InstallerTypeEnum::MSStore: + return Microsoft::Management::Deployment::PackageInstallerType::MSStore; + case ::AppInstaller::Manifest::InstallerTypeEnum::Nullsoft: + return Microsoft::Management::Deployment::PackageInstallerType::Nullsoft; + case ::AppInstaller::Manifest::InstallerTypeEnum::Portable: + return Microsoft::Management::Deployment::PackageInstallerType::Portable; + case ::AppInstaller::Manifest::InstallerTypeEnum::Wix: + return Microsoft::Management::Deployment::PackageInstallerType::Wix; + case ::AppInstaller::Manifest::InstallerTypeEnum::Zip: + return Microsoft::Management::Deployment::PackageInstallerType::Zip; + case ::AppInstaller::Manifest::InstallerTypeEnum::Unknown: + return Microsoft::Management::Deployment::PackageInstallerType::Unknown; + } + + return Microsoft::Management::Deployment::PackageInstallerType::Unknown; + } + + winrt::Microsoft::Management::Deployment::PackageInstallerScope GetDeploymentInstallerScope(::AppInstaller::Manifest::ScopeEnum installerScope) + { + switch (installerScope) + { + case ::AppInstaller::Manifest::ScopeEnum::User: + return Microsoft::Management::Deployment::PackageInstallerScope::User; + case ::AppInstaller::Manifest::ScopeEnum::Machine: + return Microsoft::Management::Deployment::PackageInstallerScope::System; + case ::AppInstaller::Manifest::ScopeEnum::Unknown: + return Microsoft::Management::Deployment::PackageInstallerScope::Unknown; + } + + return Microsoft::Management::Deployment::PackageInstallerScope::Unknown; + } } diff --git a/src/Microsoft.Management.Deployment/Converters.h b/src/Microsoft.Management.Deployment/Converters.h index 4f97f7331a..800cb35b98 100644 --- a/src/Microsoft.Management.Deployment/Converters.h +++ b/src/Microsoft.Management.Deployment/Converters.h @@ -18,6 +18,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::optional<::AppInstaller::Utility::Architecture> GetUtilityArchitecture(winrt::Windows::System::ProcessorArchitecture architecture); std::optional GetWindowsSystemProcessorArchitecture(::AppInstaller::Utility::Architecture architecture); std::pair<::AppInstaller::Manifest::ScopeEnum, bool> GetManifestScope(winrt::Microsoft::Management::Deployment::PackageInstallScope scope); + winrt::Microsoft::Management::Deployment::PackageInstallerType GetDeploymentInstallerType(::AppInstaller::Manifest::InstallerTypeEnum installerType); + winrt::Microsoft::Management::Deployment::PackageInstallerScope GetDeploymentInstallerScope(::AppInstaller::Manifest::ScopeEnum installerScope); #define WINGET_GET_OPERATION_RESULT_STATUS(_installResultStatus_, _uninstallResultStatus_) \ if constexpr (std::is_same_v) \ diff --git a/src/Microsoft.Management.Deployment/FindPackagesResult.h b/src/Microsoft.Management.Deployment/FindPackagesResult.h index fcc9117d21..775611e5f1 100644 --- a/src/Microsoft.Management.Deployment/FindPackagesResult.h +++ b/src/Microsoft.Management.Deployment/FindPackagesResult.h @@ -2,6 +2,7 @@ // Licensed under the MIT License. #pragma once #include "FindPackagesResult.g.h" +#include namespace winrt::Microsoft::Management::Deployment::implementation { @@ -11,7 +12,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize( - winrt::Microsoft::Management::Deployment::FindPackagesResultStatus errorCode, + winrt::Microsoft::Management::Deployment::FindPackagesResultStatus status, bool wasLimitExceeded, Windows::Foundation::Collections::IVector matches); #endif diff --git a/src/Microsoft.Management.Deployment/InstalledStatus.cpp b/src/Microsoft.Management.Deployment/InstalledStatus.cpp new file mode 100644 index 0000000000..fa5d8637b4 --- /dev/null +++ b/src/Microsoft.Management.Deployment/InstalledStatus.cpp @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "InstalledStatus.h" +#include "InstalledStatus.g.cpp" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void InstalledStatus::Initialize(const ::AppInstaller::Repository::InstalledStatus& installedStatus) + { + m_type = static_cast(installedStatus.Type); + m_path = winrt::to_hstring(installedStatus.Path); + m_status = installedStatus.Status; + } + winrt::Microsoft::Management::Deployment::InstalledStatusType InstalledStatus::Type() + { + return m_type; + } + hstring InstalledStatus::Path() + { + return m_path; + } + winrt::hresult InstalledStatus::Status() + { + return m_status; + } +} diff --git a/src/Microsoft.Management.Deployment/InstalledStatus.h b/src/Microsoft.Management.Deployment/InstalledStatus.h new file mode 100644 index 0000000000..f0f86d3637 --- /dev/null +++ b/src/Microsoft.Management.Deployment/InstalledStatus.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "InstalledStatus.g.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct InstalledStatus : InstalledStatusT + { + InstalledStatus() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(const ::AppInstaller::Repository::InstalledStatus& installedStatus); +#endif + + winrt::Microsoft::Management::Deployment::InstalledStatusType Type(); + hstring Path(); + winrt::hresult Status(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + winrt::Microsoft::Management::Deployment::InstalledStatusType m_type = winrt::Microsoft::Management::Deployment::InstalledStatusType::None; + hstring m_path; + winrt::hresult m_status = S_OK; +#endif + }; +} diff --git a/src/Microsoft.Management.Deployment/MatchResult.cpp b/src/Microsoft.Management.Deployment/MatchResult.cpp index 96f6fcd40c..82761d0837 100644 --- a/src/Microsoft.Management.Deployment/MatchResult.cpp +++ b/src/Microsoft.Management.Deployment/MatchResult.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include #include #include "MatchResult.h" #include "MatchResult.g.cpp" diff --git a/src/Microsoft.Management.Deployment/MatchResult.h b/src/Microsoft.Management.Deployment/MatchResult.h index 4ee4406aea..0f40b56038 100644 --- a/src/Microsoft.Management.Deployment/MatchResult.h +++ b/src/Microsoft.Management.Deployment/MatchResult.h @@ -8,7 +8,10 @@ namespace winrt::Microsoft::Management::Deployment::implementation struct MatchResult : MatchResultT { MatchResult() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) void Initialize(Microsoft::Management::Deployment::CatalogPackage package, Microsoft::Management::Deployment::PackageMatchFilter matchCriteria); +#endif winrt::Microsoft::Management::Deployment::CatalogPackage CatalogPackage(); winrt::Microsoft::Management::Deployment::PackageMatchFilter MatchCriteria(); diff --git a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj index 3d9be32869..31bfe92177 100644 --- a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj +++ b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj @@ -137,18 +137,22 @@ + + + + @@ -161,6 +165,7 @@ + @@ -168,12 +173,15 @@ + + + diff --git a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj.filters b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj.filters index bb03d687bf..a204e95804 100644 --- a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj.filters +++ b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj.filters @@ -24,6 +24,10 @@ + + + + @@ -50,6 +54,10 @@ Public + + + + diff --git a/src/Microsoft.Management.Deployment/PackageCatalog.cpp b/src/Microsoft.Management.Deployment/PackageCatalog.cpp index af4e4bd15d..d62fe6dbac 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalog.cpp +++ b/src/Microsoft.Management.Deployment/PackageCatalog.cpp @@ -121,7 +121,6 @@ namespace winrt::Microsoft::Management::Deployment::implementation winrt::Microsoft::Management::Deployment::FindPackagesResult PackageCatalog::FindPackages(winrt::Microsoft::Management::Deployment::FindPackagesOptions const& options) { - winrt::Microsoft::Management::Deployment::FindPackagesResultStatus::Ok; bool isTruncated = false; Windows::Foundation::Collections::IVector matches{ winrt::single_threaded_vector() }; ::AppInstaller::Repository::SearchRequest searchRequest; diff --git a/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp b/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp index 436a062e78..1ecdb03f5e 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp +++ b/src/Microsoft.Management.Deployment/PackageCatalogInfo.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include #include "PackageCatalogInfo.h" #include "PackageCatalogInfo.g.cpp" #include diff --git a/src/Microsoft.Management.Deployment/PackageCatalogInfo.h b/src/Microsoft.Management.Deployment/PackageCatalogInfo.h index 3e21b2b3e3..1f720463f0 100644 --- a/src/Microsoft.Management.Deployment/PackageCatalogInfo.h +++ b/src/Microsoft.Management.Deployment/PackageCatalogInfo.h @@ -2,6 +2,7 @@ // Licensed under the MIT License. #pragma once #include "PackageCatalogInfo.g.h" +#include namespace winrt::Microsoft::Management::Deployment::implementation { diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp b/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp new file mode 100644 index 0000000000..1245228512 --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "PackageInstallerInfo.h" +#include "PackageInstallerInfo.g.cpp" +#include "Converters.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageInstallerInfo::Initialize(const ::AppInstaller::Manifest::ManifestInstaller& manifestInstaller) + { + m_manifestInstaller = manifestInstaller; + } + winrt::Microsoft::Management::Deployment::PackageInstallerType PackageInstallerInfo::InstallerType() + { + return GetDeploymentInstallerType(m_manifestInstaller.InstallerType); + } + winrt::Microsoft::Management::Deployment::PackageInstallerType PackageInstallerInfo::NestedInstallerType() + { + return GetDeploymentInstallerType(m_manifestInstaller.NestedInstallerType); + } + winrt::Windows::System::ProcessorArchitecture PackageInstallerInfo::Architecture() + { + auto convertedArchitecture = GetWindowsSystemProcessorArchitecture(m_manifestInstaller.Arch); + return convertedArchitecture ? convertedArchitecture.value() : Windows::System::ProcessorArchitecture::Unknown; + } + winrt::Microsoft::Management::Deployment::PackageInstallerScope PackageInstallerInfo::Scope() + { + return GetDeploymentInstallerScope(m_manifestInstaller.Scope); + } + hstring PackageInstallerInfo::Locale() + { + return winrt::to_hstring(m_manifestInstaller.Locale); + } +} diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInfo.h b/src/Microsoft.Management.Deployment/PackageInstallerInfo.h new file mode 100644 index 0000000000..0a6aec276a --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageInstallerInfo.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageInstallerInfo.g.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageInstallerInfo : PackageInstallerInfoT + { + PackageInstallerInfo() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(const ::AppInstaller::Manifest::ManifestInstaller& manifestInstaller); +#endif + + winrt::Microsoft::Management::Deployment::PackageInstallerType InstallerType(); + winrt::Microsoft::Management::Deployment::PackageInstallerType NestedInstallerType(); + winrt::Windows::System::ProcessorArchitecture Architecture(); + winrt::Microsoft::Management::Deployment::PackageInstallerScope Scope(); + hstring Locale(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + ::AppInstaller::Manifest::ManifestInstaller m_manifestInstaller; +#endif + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp new file mode 100644 index 0000000000..ee93f16e7f --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "PackageInstallerInstalledStatus.h" +#include "PackageInstallerInstalledStatus.g.cpp" +#include "InstalledStatus.h" +#include "PackageInstallerInfo.h" +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageInstallerInstalledStatus::Initialize(const ::AppInstaller::Repository::InstallerInstalledStatus& installerInstalledStatus) + { + // Initialize m_installerInfo + auto installerInfo = winrt::make_self>(); + installerInfo->Initialize(installerInstalledStatus.Installer); + m_installerInfo = *installerInfo; + + // Initialize m_installedStatus + for (auto const& entry : installerInstalledStatus.InstalledStatus) + { + auto status = winrt::make_self>(); + status->Initialize(entry); + + m_installedStatus.Append(*status); + } + } + winrt::Microsoft::Management::Deployment::PackageInstallerInfo PackageInstallerInstalledStatus::InstallerInfo() + { + return m_installerInfo; + } + winrt::Windows::Foundation::Collections::IVectorView PackageInstallerInstalledStatus::InstalledStatus() + { + return m_installedStatus.GetView(); + } +} diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h new file mode 100644 index 0000000000..fe25341cac --- /dev/null +++ b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageInstallerInstalledStatus.g.h" +#include +#include + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageInstallerInstalledStatus : PackageInstallerInstalledStatusT + { + PackageInstallerInstalledStatus() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize(const ::AppInstaller::Repository::InstallerInstalledStatus& installerInstalledStatus); +#endif + + winrt::Microsoft::Management::Deployment::PackageInstallerInfo InstallerInfo(); + winrt::Windows::Foundation::Collections::IVectorView InstalledStatus(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + winrt::Microsoft::Management::Deployment::PackageInstallerInfo m_installerInfo{ nullptr }; + winrt::Windows::Foundation::Collections::IVector m_installedStatus{ + winrt::single_threaded_vector() }; +#endif + }; +} diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index c343e1e059..905e41033e 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -351,7 +351,7 @@ namespace Microsoft.Management.Deployment String Locale{ get; }; }; - /// The installed status type. + /// The installed status type. The values need to match InstalledStatusType from winget/RepositorySearch.h. [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] [flags] enum InstalledStatusType @@ -368,6 +368,14 @@ namespace Microsoft.Management.Deployment DefaultInstallLocation = 0x0008, /// Check default install location with installed files if applicable. DefaultInstallLocationFile = 0x0010, + + /// Below are helper values for calling CheckInstalledStatus as input. + /// AppsAndFeaturesEntry related checks + AllAppsAndFeaturesEntryChecks = AppsAndFeaturesEntry | AppsAndFeaturesEntryInstallLocation | AppsAndFeaturesEntryInstallLocationFile, + /// DefaultInstallLocation related checks + AllDefaultInstallLocationChecks = DefaultInstallLocation | DefaultInstallLocationFile, + /// All checks + AllChecks = AllAppsAndFeaturesEntryChecks | AllDefaultInstallLocationChecks, }; /// Interface representing an individual installed status. @@ -436,11 +444,16 @@ namespace Microsoft.Management.Deployment /// Gets a value indicating whether an available version is newer than the installed version. Boolean IsUpdateAvailable { get; }; - /// Check the installed status of the package. It's required to call this method from a composite package - /// with installed info for more accurate and complete installed status. - /// This may require downloading information from a server. - Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType types); - CheckInstalledStatusResult CheckInstalledStatus(InstalledStatusType types); + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + { + /// Check the installed status of the package. It's required to call this method from a composite package + /// with installed info for more accurate and complete installed status. + /// This may require downloading information from a server. + Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType checkTypes); + CheckInstalledStatusResult CheckInstalledStatus(InstalledStatusType checkTypes); + Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(); + CheckInstalledStatusResult CheckInstalledStatus(); + } /// DESIGN NOTE: /// IsSame from IPackage in winget/RepositorySearch is not implemented in V1. From a4cf9604ede409ad920a3fc5e6e16f7272ac4105 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Fri, 29 Jul 2022 23:50:32 -0700 Subject: [PATCH 06/17] detectimpl --- src/AppInstallerCommonCore/Filesystem.cpp | 5 + .../Public/AppInstallerErrors.h | 12 + .../Public/AppInstallerVersions.h | 3 + .../Public/winget/ManifestCommon.h | 3 + src/AppInstallerCommonCore/Versions.cpp | 16 ++ .../CompositeSource.cpp | 249 +++++++++++++++++- .../Microsoft/ARPHelper.cpp | 5 +- .../Microsoft/SQLiteIndexSource.cpp | 4 +- .../Rest/RestSource.cpp | 2 +- 9 files changed, 289 insertions(+), 10 deletions(-) diff --git a/src/AppInstallerCommonCore/Filesystem.cpp b/src/AppInstallerCommonCore/Filesystem.cpp index b7423e65e5..933f5ca041 100644 --- a/src/AppInstallerCommonCore/Filesystem.cpp +++ b/src/AppInstallerCommonCore/Filesystem.cpp @@ -135,6 +135,11 @@ namespace AppInstaller::Filesystem std::filesystem::path GetExpandedPath(const std::string& path) { + if (path.empty()) + { + return {}; + } + std::wstring widePath = Utility::ConvertToUTF16(path); WCHAR buffer[MAX_PATH]; return PathUnExpandEnvStrings(widePath.c_str(), buffer, ARRAYSIZE(buffer)) ? std::filesystem::path{ buffer } : std::filesystem::path{ path }; diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h index 11d36b9550..cddaa5b8bd 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -126,6 +126,18 @@ #define APPINSTALLER_CLI_ERROR_INSTALL_BLOCKED_BY_POLICY ((HRESULT)0x8A15010F) #define APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES ((HRESULT)0x8A150110) +// Status values for check package installed status results. +#define WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND S_OK +#define WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND ((HRESULT)0x8A150201) +#define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND S_OK +#define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE ((HRESULT)0x8A150202) +#define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND ((HRESULT)0x8A150203) +#define WINGET_INSTALLED_STATUS_FILE_HASH_MATCH S_OK +#define WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH ((HRESULT)0x8A150204) +#define WINGET_INSTALLED_STATUS_FILE_NOT_FOUND ((HRESULT)0x8A150205) +#define WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK ((HRESULT)0x8A150206) +#define WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR ((HRESULT)0x8A150207) + namespace AppInstaller { diff --git a/src/AppInstallerCommonCore/Public/AppInstallerVersions.h b/src/AppInstallerCommonCore/Public/AppInstallerVersions.h index 59f1adf780..954c76a12b 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerVersions.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerVersions.h @@ -104,6 +104,9 @@ namespace AppInstaller::Utility // Returns if the version is an approximate version. bool IsApproximate() const { return m_approximateComparator != ApproximateComparator::None; } + // Get the base version from approximate version, or return a copy if the version is not approximate. + Version GetBaseVersion() const; + protected: bool IsBaseVersionLatest() const; diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h index 3116dc4c6c..8a08c28cce 100644 --- a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h +++ b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -276,6 +276,9 @@ namespace AppInstaller::Manifest { string_t DefaultInstallLocation; std::vector Files; + + // Checks if there are any installation metadata available. + bool HasData() const { return !DefaultInstallLocation.empty() || !Files.empty(); } }; InstallerTypeEnum ConvertToInstallerTypeEnum(const std::string& in); diff --git a/src/AppInstallerCommonCore/Versions.cpp b/src/AppInstallerCommonCore/Versions.cpp index c87d0bd226..a300e8cdcc 100644 --- a/src/AppInstallerCommonCore/Versions.cpp +++ b/src/AppInstallerCommonCore/Versions.cpp @@ -245,6 +245,22 @@ namespace AppInstaller::Utility return s_zero; } } + + Version Version::GetBaseVersion() const + { + Version baseVersion = *this; + baseVersion.m_approximateComparator = ApproximateComparator::None; + if (m_approximateComparator == ApproximateComparator::LessThan) + { + baseVersion.m_version = m_version.substr(s_Approximate_Less_Than.size()); + } + else if (m_approximateComparator == ApproximateComparator::GreaterThan) + { + baseVersion.m_version = m_version.substr(s_Approximate_Greater_Than.size()); + } + + return baseVersion; + } bool Version::IsBaseVersionLatest() const { diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 52da03ebcc..84282cede3 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -2,6 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "CompositeSource.h" +#include namespace AppInstaller::Repository { @@ -247,6 +248,244 @@ namespace AppInstaller::Repository return installedVersion; } + std::vector CheckInstalledStatusInternal( + const std::shared_ptr& installedVersion, + const std::shared_ptr& availablePackage, + InstalledStatusType checkTypes) + { + using namespace AppInstaller::Manifest; + + std::vector result; + bool checkFileHash = false; + std::shared_ptr availableVersion; + // Map to cache already calculated file hashes. + auto filePathComparator = [](const std::filesystem::path& a, const std::filesystem::path& b) + { + if (std::filesystem::equivalent(a, b)) + { + return false; + } + + return a < b; + }; + std::map fileHashes; + + // Variables for metadata from installed version. + InstallerTypeEnum installedType = InstallerTypeEnum::Unknown; + ScopeEnum installedScope = ScopeEnum::Unknown; + std::filesystem::path installedLocation; + std::string installedLocale; + Utility::Architecture installedArchitecture = Utility::Architecture::Unknown; + bool installedLocationExists = false; + + // Determine the available package version to be used for installed status checking. + // Only perform file hash check if we find an available version that matches installed version. + if (installedVersion) + { + Utility::Version installedVersionAsVersion{ installedVersion->GetProperty(PackageVersionProperty::Version) }; + auto installedChannel = installedVersion->GetProperty(PackageVersionProperty::Channel); + + PackageVersionKey versionKey; + versionKey.Channel = installedChannel.get(); + + if (installedVersionAsVersion.IsApproximate()) + { + // Use the base version as available version if installed version is mapped to be an approximate. + versionKey.Version = installedVersionAsVersion.GetBaseVersion().ToString(); + availableVersion = availablePackage->GetAvailableVersion(versionKey); + // It's unexpected if the installed version is already mapped to some version. + THROW_HR_IF(E_UNEXPECTED, !availableVersion); + } + else + { + versionKey.Version = installedVersionAsVersion.ToString(); + availableVersion = availablePackage->GetAvailableVersion(versionKey); + if (availableVersion) + { + checkFileHash = true; + } + } + } + + if (!availableVersion) + { + // No installed version, or installed version not found in available versions, + // then attempt to check installed status using latest version. + availableVersion = availablePackage->GetLatestAvailableVersion(); + THROW_HR_IF(E_UNEXPECTED, !availableVersion); + } + + // Prepare installation metadata if installed version is not null + if (installedVersion) + { + auto installedMetadata = installedVersion->GetMetadata(); + installedType = ConvertToInstallerTypeEnum(installedMetadata[PackageVersionMetadata::InstalledType]); + installedScope = ConvertToScopeEnum(installedMetadata[PackageVersionMetadata::InstalledScope]); + installedLocation = Filesystem::GetExpandedPath(installedMetadata[PackageVersionMetadata::InstalledLocation]); + installedLocale = installedMetadata[PackageVersionMetadata::InstalledLocale]; + installedArchitecture = Utility::ConvertToArchitectureEnum(installedMetadata[PackageVersionMetadata::InstalledArchitecture]); + // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. + std::error_code error; + installedLocationExists = std::filesystem::exists(installedLocation, error) && std::filesystem::is_directory(installedLocation, error); + } + + auto manifest = availableVersion->GetManifest(); + for (auto const& installer : manifest.Installers) + { + if (installer.InstallationMetadata.HasData()) + { + InstallerInstalledStatus installerStatus; + installerStatus.Installer = installer; + + // ARP related checks + if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllAppsAndFeaturesEntryChecks)) + { + bool isMatchingInstaller = + installedVersion && + IsInstallerTypeCompatible(installedType, IsArchiveType(installer.InstallerType) ? installer.NestedInstallerType : installer.InstallerType) && + (installedScope == ScopeEnum::Unknown || installer.Scope == ScopeEnum::Unknown || installedScope == installer.Scope) && // Treat unknown scope as compatible + (installedArchitecture == Utility::Architecture::Unknown || installedArchitecture == installer.Arch) && // Treat unknown installed architecture as compatible + (installedLocale.empty() || installer.Locale.empty() || !Locale::IsWellFormedBcp47Tag(installedLocale) || Locale::GetDistanceOfLanguage(installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch); // Treat invalid locale as compatible + + // ARP entry status + if (WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntry)) + { + installerStatus.InstalledStatus.emplace_back( + InstalledStatusType::AppsAndFeaturesEntry, + "", + isMatchingInstaller ? WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND : WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND); + } + + // ARP install location status + if (isMatchingInstaller && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocation)) + { + HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; + if (installedLocation.empty()) + { + installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; + } + else if (installedLocationExists) + { + installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND; + } + + installerStatus.InstalledStatus.emplace_back( + InstalledStatusType::AppsAndFeaturesEntryInstallLocation, + installedLocation, + installLocationStatus); + } + + // ARP install location files + if (isMatchingInstaller && installedLocationExists && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile)) + { + for (auto const& file : installer.InstallationMetadata.Files) + { + HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; + std::filesystem::path filePath = installedLocation / std::filesystem::path{ static_cast (file.RelativeFilePath) }; + try + { + if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; + if (checkFileHash && !file.FileSha256.empty()) + { + if (fileHashes.find(filePath) == fileHashes.end()) + { + // If not found in cache, computhe the hash. + std::ifstream in{ filePath, std::ifstream::binary }; + fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)); + } + + fileStatus = Utility::SHA256::AreEqual(file.FileSha256, fileHashes.at(filePath)) ? WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; + } + } + } + catch (...) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; + } + + installerStatus.InstalledStatus.emplace_back( + InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, + filePath, + fileStatus); + } + } + } + + // Default install location related checks + if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllDefaultInstallLocationChecks)) + { + auto defaultInstalledLocation = Filesystem::GetExpandedPath(installer.InstallationMetadata.DefaultInstallLocation); + // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. + std::error_code error; + bool defaultInstalledLocationExists = std::filesystem::exists(defaultInstalledLocation, error) && std::filesystem::is_directory(defaultInstalledLocation, error); + + // Default install location status + if (WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocation)) + { + HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; + if (defaultInstalledLocation.empty()) + { + installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; + } + else if (defaultInstalledLocationExists) + { + installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND; + } + + installerStatus.InstalledStatus.emplace_back( + InstalledStatusType::DefaultInstallLocation, + defaultInstalledLocation, + installLocationStatus); + } + + // Default install location files + if (defaultInstalledLocationExists && WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocationFile)) + { + for (auto const& file : installer.InstallationMetadata.Files) + { + HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; + std::filesystem::path filePath = defaultInstalledLocation / std::filesystem::path{ static_cast (file.RelativeFilePath) }; + try + { + if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; + if (checkFileHash && !file.FileSha256.empty()) + { + if (fileHashes.find(filePath) == fileHashes.end()) + { + // If not found in cache, computhe the hash. + std::ifstream in{ filePath, std::ifstream::binary }; + fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)); + } + + fileStatus = Utility::SHA256::AreEqual(file.FileSha256, fileHashes.at(filePath)) ? WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; + } + } + } + catch (...) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; + } + + installerStatus.InstalledStatus.emplace_back( + InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, + filePath, + fileStatus); + } + } + } + + if (!installerStatus.InstalledStatus.empty()) + { + result.emplace_back(std::move(installerStatus)); + } + } + } + } + // A composite package installed version that allows us to override the source or the version. struct CompositeInstalledVersion : public IPackageVersion { @@ -400,9 +639,9 @@ namespace AppInstaller::Repository return (latest && (GetVACFromVersion(installed.get()).IsUpdatedBy(GetVACFromVersion(latest.get())))); } - std::vector CheckInstalledStatus(InstalledStatusType) const override + std::vector CheckInstalledStatus(InstalledStatusType checkTypes) const override { - return {}; + return CheckInstalledStatusInternal(GetInstalledVersion(), GetAvailablePackage(), checkTypes); } bool IsSame(const IPackage* other) const override @@ -421,17 +660,17 @@ namespace AppInstaller::Repository return true; } - const std::shared_ptr& GetInstalledPackage() + const std::shared_ptr& GetInstalledPackage() const { return m_installedPackage; } - const std::shared_ptr& GetAvailablePackage() + const std::shared_ptr& GetAvailablePackage() const { return m_availablePackage; } - const std::shared_ptr& GetTrackingPackage() + const std::shared_ptr& GetTrackingPackage() const { return m_trackingPackage; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp index 41ec717a49..83b6ef7173 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp @@ -347,6 +347,9 @@ namespace AppInstaller::Repository::Microsoft // Pick up Language to enable proper selection of language for upgrade. AddMetadataIfPresent(arpKey, Language, index, manifestId, PackageVersionMetadata::InstalledLocale); + // Set installed architecture info. + index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledArchitecture, architecture); + // Pick up WindowsInstaller to determine if this is an MSI install. // TODO: Could also determine Inno (and maybe other types) through detecting other keys here. auto installedType = Manifest::InstallerTypeEnum::Exe; @@ -358,8 +361,6 @@ namespace AppInstaller::Repository::Microsoft if (Manifest::ConvertToInstallerTypeEnum(GetStringValue(arpKey, std::wstring{ ToString(PortableValueName::WinGetInstallerType) })) == Manifest::InstallerTypeEnum::Portable) { - // Portable uninstall requires the installed architecture for locating the entry in the registry. - index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledArchitecture, architecture); installedType = Manifest::InstallerTypeEnum::Portable; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index c354ed6fce..300e585671 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -284,7 +284,7 @@ namespace AppInstaller::Repository::Microsoft std::vector CheckInstalledStatus(InstalledStatusType) const override { - return {}; + THROW_HR(E_NOTIMPL); } bool IsSame(const IPackage* other) const override @@ -338,7 +338,7 @@ namespace AppInstaller::Repository::Microsoft std::vector CheckInstalledStatus(InstalledStatusType) const override { - return {}; + THROW_HR(E_NOTIMPL); } bool IsSame(const IPackage* other) const override diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index f747462cdb..2ccc078a38 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -87,7 +87,7 @@ namespace AppInstaller::Repository::Rest std::vector CheckInstalledStatus(InstalledStatusType) const override { - return {}; + THROW_HR(E_NOTIMPL); } bool IsSame(const IPackage* other) const override From 89518eacc255fe9b7cbbbf471299474eedc9c5a7 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Mon, 1 Aug 2022 17:10:40 -0700 Subject: [PATCH 07/17] Move common code --- .../CompositeSource.cpp | 194 ++++++++---------- 1 file changed, 89 insertions(+), 105 deletions(-) diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 84282cede3..928888909a 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -248,6 +248,67 @@ namespace AppInstaller::Repository return installedVersion; } + HRESULT CheckInstalledLocationStatus(const std::filesystem::path& installedLocation) + { + HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; + if (!installedLocation.empty()) + { + // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. + std::error_code error; + installLocationStatus = + std::filesystem::exists(installedLocation, error) && std::filesystem::is_directory(installedLocation, error) ? + WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND : + WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; + } + + return installLocationStatus; + } + + // Map to cache already calculated file hashes. + auto filePathComparator = [](const std::filesystem::path& a, const std::filesystem::path& b) + { + if (std::filesystem::equivalent(a, b)) + { + return false; + } + + return a < b; + }; + using FileHashMap = std::map; + + HRESULT CheckInstalledFileStatus( + const std::filesystem::path& filePath, + const Utility::SHA256::HashBuffer& expectedHash, + FileHashMap& fileHashes) + { + HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; + try + { + if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; + if (!expectedHash.empty()) + { + if (fileHashes.find(filePath) == fileHashes.end()) + { + // If not found in cache, computhe the hash. + std::ifstream in{ filePath, std::ifstream::binary }; + fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)); + } + + fileStatus = Utility::SHA256::AreEqual(expectedHash, fileHashes.at(filePath)) ? + WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; + } + } + } + catch (...) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; + } + + return fileStatus; + } + std::vector CheckInstalledStatusInternal( const std::shared_ptr& installedVersion, const std::shared_ptr& availablePackage, @@ -258,17 +319,7 @@ namespace AppInstaller::Repository std::vector result; bool checkFileHash = false; std::shared_ptr availableVersion; - // Map to cache already calculated file hashes. - auto filePathComparator = [](const std::filesystem::path& a, const std::filesystem::path& b) - { - if (std::filesystem::equivalent(a, b)) - { - return false; - } - - return a < b; - }; - std::map fileHashes; + FileHashMap fileHashes; // Variables for metadata from installed version. InstallerTypeEnum installedType = InstallerTypeEnum::Unknown; @@ -276,15 +327,25 @@ namespace AppInstaller::Repository std::filesystem::path installedLocation; std::string installedLocale; Utility::Architecture installedArchitecture = Utility::Architecture::Unknown; - bool installedLocationExists = false; + HRESULT installedLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; + // Prepare installed metadata from installed version. // Determine the available package version to be used for installed status checking. // Only perform file hash check if we find an available version that matches installed version. if (installedVersion) { + // Installed metadata. + auto installedMetadata = installedVersion->GetMetadata(); + installedType = ConvertToInstallerTypeEnum(installedMetadata[PackageVersionMetadata::InstalledType]); + installedScope = ConvertToScopeEnum(installedMetadata[PackageVersionMetadata::InstalledScope]); + installedLocation = Filesystem::GetExpandedPath(installedMetadata[PackageVersionMetadata::InstalledLocation]); + installedLocale = installedMetadata[PackageVersionMetadata::InstalledLocale]; + installedArchitecture = Utility::ConvertToArchitectureEnum(installedMetadata[PackageVersionMetadata::InstalledArchitecture]); + installedLocationStatus = CheckInstalledLocationStatus(installedLocation); + + // Determine available version. Utility::Version installedVersionAsVersion{ installedVersion->GetProperty(PackageVersionProperty::Version) }; auto installedChannel = installedVersion->GetProperty(PackageVersionProperty::Channel); - PackageVersionKey versionKey; versionKey.Channel = installedChannel.get(); @@ -315,20 +376,6 @@ namespace AppInstaller::Repository THROW_HR_IF(E_UNEXPECTED, !availableVersion); } - // Prepare installation metadata if installed version is not null - if (installedVersion) - { - auto installedMetadata = installedVersion->GetMetadata(); - installedType = ConvertToInstallerTypeEnum(installedMetadata[PackageVersionMetadata::InstalledType]); - installedScope = ConvertToScopeEnum(installedMetadata[PackageVersionMetadata::InstalledScope]); - installedLocation = Filesystem::GetExpandedPath(installedMetadata[PackageVersionMetadata::InstalledLocation]); - installedLocale = installedMetadata[PackageVersionMetadata::InstalledLocale]; - installedArchitecture = Utility::ConvertToArchitectureEnum(installedMetadata[PackageVersionMetadata::InstalledArchitecture]); - // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. - std::error_code error; - installedLocationExists = std::filesystem::exists(installedLocation, error) && std::filesystem::is_directory(installedLocation, error); - } - auto manifest = availableVersion->GetManifest(); for (auto const& installer : manifest.Installers) { @@ -344,7 +391,7 @@ namespace AppInstaller::Repository installedVersion && IsInstallerTypeCompatible(installedType, IsArchiveType(installer.InstallerType) ? installer.NestedInstallerType : installer.InstallerType) && (installedScope == ScopeEnum::Unknown || installer.Scope == ScopeEnum::Unknown || installedScope == installer.Scope) && // Treat unknown scope as compatible - (installedArchitecture == Utility::Architecture::Unknown || installedArchitecture == installer.Arch) && // Treat unknown installed architecture as compatible + (installedArchitecture == Utility::Architecture::Unknown || installer.Arch == Utility::Architecture::Neutral || installedArchitecture == installer.Arch) && // Treat unknown installed architecture as compatible (installedLocale.empty() || installer.Locale.empty() || !Locale::IsWellFormedBcp47Tag(installedLocale) || Locale::GetDistanceOfLanguage(installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch); // Treat invalid locale as compatible // ARP entry status @@ -359,52 +406,22 @@ namespace AppInstaller::Repository // ARP install location status if (isMatchingInstaller && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocation)) { - HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; - if (installedLocation.empty()) - { - installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; - } - else if (installedLocationExists) - { - installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND; - } - installerStatus.InstalledStatus.emplace_back( InstalledStatusType::AppsAndFeaturesEntryInstallLocation, installedLocation, - installLocationStatus); + installedLocationStatus); } // ARP install location files - if (isMatchingInstaller && installedLocationExists && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile)) + if (isMatchingInstaller && + installedLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && + WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile)) { for (auto const& file : installer.InstallationMetadata.Files) { - HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; - std::filesystem::path filePath = installedLocation / std::filesystem::path{ static_cast (file.RelativeFilePath) }; - try - { - if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) - { - fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; - if (checkFileHash && !file.FileSha256.empty()) - { - if (fileHashes.find(filePath) == fileHashes.end()) - { - // If not found in cache, computhe the hash. - std::ifstream in{ filePath, std::ifstream::binary }; - fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)); - } - - fileStatus = Utility::SHA256::AreEqual(file.FileSha256, fileHashes.at(filePath)) ? WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; - } - } - } - catch (...) - { - fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; - } - + std::filesystem::path filePath = installedLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; + auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); + installerStatus.InstalledStatus.emplace_back( InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, filePath, @@ -417,61 +434,28 @@ namespace AppInstaller::Repository if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllDefaultInstallLocationChecks)) { auto defaultInstalledLocation = Filesystem::GetExpandedPath(installer.InstallationMetadata.DefaultInstallLocation); - // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. - std::error_code error; - bool defaultInstalledLocationExists = std::filesystem::exists(defaultInstalledLocation, error) && std::filesystem::is_directory(defaultInstalledLocation, error); + HRESULT defaultInstalledLocationStatus = CheckInstalledLocationStatus(defaultInstalledLocation); // Default install location status if (WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocation)) { - HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; - if (defaultInstalledLocation.empty()) - { - installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; - } - else if (defaultInstalledLocationExists) - { - installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND; - } - installerStatus.InstalledStatus.emplace_back( InstalledStatusType::DefaultInstallLocation, defaultInstalledLocation, - installLocationStatus); + defaultInstalledLocationStatus); } // Default install location files - if (defaultInstalledLocationExists && WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocationFile)) + if (defaultInstalledLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && + WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocationFile)) { for (auto const& file : installer.InstallationMetadata.Files) { - HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; - std::filesystem::path filePath = defaultInstalledLocation / std::filesystem::path{ static_cast (file.RelativeFilePath) }; - try - { - if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) - { - fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; - if (checkFileHash && !file.FileSha256.empty()) - { - if (fileHashes.find(filePath) == fileHashes.end()) - { - // If not found in cache, computhe the hash. - std::ifstream in{ filePath, std::ifstream::binary }; - fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)); - } - - fileStatus = Utility::SHA256::AreEqual(file.FileSha256, fileHashes.at(filePath)) ? WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; - } - } - } - catch (...) - { - fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; - } + std::filesystem::path filePath = defaultInstalledLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; + auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); installerStatus.InstalledStatus.emplace_back( - InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, + InstalledStatusType::DefaultInstallLocationFile, filePath, fileStatus); } From 2594821315d9051f80e475b82f16c66acc0f82b5 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Tue, 2 Aug 2022 22:28:28 -0700 Subject: [PATCH 08/17] Fix build --- .../Interop/CheckInstalledStatusInterop.cs | 48 +++++++++++++++++++ .../Manifests/TestInstalledStatus.yaml | 22 +++++++++ .../CompositeSource.cpp | 37 +++++++------- .../Public/winget/RepositorySearch.h | 5 +- src/AppInstallerTestExeInstaller/main.cpp | 16 ++++++- .../CheckInstalledStatusResult.cpp | 2 +- .../CheckInstalledStatusResult.h | 2 +- .../PackageInstallerInstalledStatus.cpp | 4 +- .../PackageInstallerInstalledStatus.h | 2 +- .../PackageManager.idl | 4 +- .../UndockedRegFreeWinRT/catalog.cpp | 2 +- 11 files changed, 117 insertions(+), 27 deletions(-) create mode 100644 src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs create mode 100644 src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml diff --git a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs new file mode 100644 index 0000000000..4d9b404f72 --- /dev/null +++ b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace AppInstallerCLIE2ETests.Interop +{ + using Microsoft.Management.Deployment; + using Microsoft.Management.Deployment.Projection; + using NUnit.Framework; + using System; + using System.IO; + using System.Threading.Tasks; + + [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] + [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] + public class CheckInstalledStatusInterop : BaseInterop + { + private string installDir; + private PackageManager packageManager; + private PackageCatalogReference testSource; + + public CheckInstalledStatusInterop(IInstanceInitializer initializer) : base(initializer) { } + + [SetUp] + public void SetUp() + { + packageManager = TestFactory.CreatePackageManager(); + testSource = packageManager.GetPackageCatalogByName(Constants.TestSourceName); + installDir = TestCommon.GetRandomTestDir(); + } + + [Test] + public async Task CheckInstalledStatus() + { + // Find and install the test package. + var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + var installOptions = TestFactory.CreateInstallOptions(); + installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus"; + var installResult = await packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); + Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); + + var checkResult = searchResult.CatalogPackage.CheckInstalledStatus(); + Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); + Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); + + // checkResult.InstalledStatus[0]. + } + } +} \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml new file mode 100644 index 0000000000..b7d2792cdd --- /dev/null +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml @@ -0,0 +1,22 @@ +PackageIdentifier: AppInstallerTest.TestCheckInstalledStatus +PackageVersion: 1.0 +PackageName: TestCheckInstalledStatus +PackageLocale: en-US +Publisher: Microsoft +License: Test +ShortDescription: E2E test for check installed status test. +Installers: + - Architecture: neutral + InstallerUrl: https://localhost:5001/TestKit/AppInstallerTestExeInstaller/AppInstallerTestExeInstaller.exe + InstallerType: exe + InstallerSha256: + ProductCode: CheckInstalledStatusProductId + InstallationMetadata: + DefaultInstallLocation: "%ProgramFiles%\\TestApp" + Files: + - RelativeFilePath: "data.txt" + # Hash value is for a txt file with "Test" as content in utf8 + FileSha256: 532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25 + FileType: other +ManifestType: singleton +ManifestVersion: 1.3.0 diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 928888909a..fecd2ffd2f 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -265,16 +265,19 @@ namespace AppInstaller::Repository } // Map to cache already calculated file hashes. - auto filePathComparator = [](const std::filesystem::path& a, const std::filesystem::path& b) + struct FilePathComparator { - if (std::filesystem::equivalent(a, b)) + bool operator()(const std::filesystem::path& a, const std::filesystem::path& b) const { - return false; - } + if (std::filesystem::equivalent(a, b)) + { + return false; + } - return a < b; + return a < b; + } }; - using FileHashMap = std::map; + using FileHashMap = std::map; HRESULT CheckInstalledFileStatus( const std::filesystem::path& filePath, @@ -397,7 +400,7 @@ namespace AppInstaller::Repository // ARP entry status if (WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntry)) { - installerStatus.InstalledStatus.emplace_back( + installerStatus.Status.emplace_back( InstalledStatusType::AppsAndFeaturesEntry, "", isMatchingInstaller ? WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND : WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND); @@ -406,9 +409,9 @@ namespace AppInstaller::Repository // ARP install location status if (isMatchingInstaller && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocation)) { - installerStatus.InstalledStatus.emplace_back( + installerStatus.Status.emplace_back( InstalledStatusType::AppsAndFeaturesEntryInstallLocation, - installedLocation, + installedLocation.string(), installedLocationStatus); } @@ -422,9 +425,9 @@ namespace AppInstaller::Repository std::filesystem::path filePath = installedLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); - installerStatus.InstalledStatus.emplace_back( + installerStatus.Status.emplace_back( InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, - filePath, + filePath.string(), fileStatus); } } @@ -439,9 +442,9 @@ namespace AppInstaller::Repository // Default install location status if (WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocation)) { - installerStatus.InstalledStatus.emplace_back( + installerStatus.Status.emplace_back( InstalledStatusType::DefaultInstallLocation, - defaultInstalledLocation, + defaultInstalledLocation.string(), defaultInstalledLocationStatus); } @@ -454,20 +457,22 @@ namespace AppInstaller::Repository std::filesystem::path filePath = defaultInstalledLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); - installerStatus.InstalledStatus.emplace_back( + installerStatus.Status.emplace_back( InstalledStatusType::DefaultInstallLocationFile, - filePath, + filePath.string(), fileStatus); } } } - if (!installerStatus.InstalledStatus.empty()) + if (!installerStatus.Status.empty()) { result.emplace_back(std::move(installerStatus)); } } } + + return result; } // A composite package installed version that allows us to override the source or the version. diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index fd8a753b5b..5aca15fe96 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -281,13 +281,16 @@ namespace AppInstaller::Repository Utility::NormalizedString Path; // The installed status result. HRESULT Status; + + InstalledStatus(InstalledStatusType type, Utility::NormalizedString path, HRESULT status) : + Type(type), Path(std::move(path)), Status(status) {} }; // Struct representing installed status from an installer. struct InstallerInstalledStatus { Manifest::ManifestInstaller Installer; - std::vector InstalledStatus; + std::vector Status; }; // A package, potentially containing information about it's local state and the available versions. diff --git a/src/AppInstallerTestExeInstaller/main.cpp b/src/AppInstallerTestExeInstaller/main.cpp index 3ea98ae7c5..127f008e32 100644 --- a/src/AppInstallerTestExeInstaller/main.cpp +++ b/src/AppInstallerTestExeInstaller/main.cpp @@ -47,7 +47,13 @@ path GenerateUninstaller(std::wostream& out, const path& installDirectory, const return uninstallerPath; } -void WriteToUninstallRegistry(std::wostream& out, const std::wstring& productID, const path& uninstallerPath, const std::wstring& displayName, const std::wstring& displayVersion) +void WriteToUninstallRegistry( + std::wostream& out, + const std::wstring& productID, + const path& uninstallerPath, + const std::wstring& displayName, + const std::wstring& displayVersion, + const std::wstring& installLocation) { HKEY hkey; LONG lReg; @@ -115,6 +121,12 @@ void WriteToUninstallRegistry(std::wostream& out, const std::wstring& productID, out << "Failed to write Version value. Error Code: " << res << std::endl; } + // Set InstallLocation Property Value + if (LONG res = RegSetValueEx(hkey, L"InstallLocation", NULL, REG_SZ, (LPBYTE)installLocation.c_str(), (DWORD)(installLocation.length() + 1) * sizeof(wchar_t)) != ERROR_SUCCESS) + { + out << "Failed to write InstallLocation value. Error Code: " << res << std::endl; + } + out << "Write to registry key completed" << std::endl; } else { @@ -220,7 +232,7 @@ int wmain(int argc, const wchar_t** argv) path uninstallerPath = GenerateUninstaller(*out, installDirectory, productCode); - WriteToUninstallRegistry(*out, productCode, uninstallerPath, displayName, displayVersion); + WriteToUninstallRegistry(*out, productCode, uninstallerPath, displayName, displayVersion, installDirectory.wstring()); return exitCode; } diff --git a/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp index 4e75e05f60..d847eacd09 100644 --- a/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp +++ b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.cpp @@ -18,7 +18,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_status; } - winrt::Windows::Foundation::Collections::IVectorView CheckInstalledStatusResult::InstalledStatus() + winrt::Windows::Foundation::Collections::IVectorView CheckInstalledStatusResult::PackageInstalledStatus() { return m_installedStatus.GetView(); } diff --git a/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h index 32e90b2595..db5a969955 100644 --- a/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h +++ b/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h @@ -17,7 +17,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation #endif winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus Status(); - winrt::Windows::Foundation::Collections::IVectorView InstalledStatus(); + winrt::Windows::Foundation::Collections::IVectorView PackageInstalledStatus(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp index ee93f16e7f..5fb10472eb 100644 --- a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp +++ b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.cpp @@ -18,7 +18,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation m_installerInfo = *installerInfo; // Initialize m_installedStatus - for (auto const& entry : installerInstalledStatus.InstalledStatus) + for (auto const& entry : installerInstalledStatus.Status) { auto status = winrt::make_self>(); @@ -31,7 +31,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_installerInfo; } - winrt::Windows::Foundation::Collections::IVectorView PackageInstallerInstalledStatus::InstalledStatus() + winrt::Windows::Foundation::Collections::IVectorView PackageInstallerInstalledStatus::InstallerInstalledStatus() { return m_installedStatus.GetView(); } diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h index fe25341cac..471a03b3a8 100644 --- a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h +++ b/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h @@ -16,7 +16,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation #endif winrt::Microsoft::Management::Deployment::PackageInstallerInfo InstallerInfo(); - winrt::Windows::Foundation::Collections::IVectorView InstalledStatus(); + winrt::Windows::Foundation::Collections::IVectorView InstallerInstalledStatus(); #if !defined(INCLUDE_ONLY_INTERFACE_METHODS) private: diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index 905e41033e..84f3bbc6b9 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -397,7 +397,7 @@ namespace Microsoft.Management.Deployment /// The package installer info. PackageInstallerInfo InstallerInfo { get; }; /// A list of various types of installed status of the package installer. - Windows.Foundation.Collections.IVectorView InstalledStatus { get; }; + Windows.Foundation.Collections.IVectorView InstallerInstalledStatus { get; }; }; /// Status of the check installed status call. @@ -416,7 +416,7 @@ namespace Microsoft.Management.Deployment CheckInstalledStatusResultStatus Status { get; }; /// A list of package installer installed status. - Windows.Foundation.Collections.IVectorView InstalledStatus { get; }; + Windows.Foundation.Collections.IVectorView PackageInstalledStatus { get; }; }; /// IMPLEMENTATION NOTE: IPackage from winget/RepositorySearch.h diff --git a/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.cpp b/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.cpp index 5e064ccf74..448756179f 100644 --- a/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.cpp +++ b/src/UndockedRegFreeWinRT/UndockedRegFreeWinRT/catalog.cpp @@ -152,7 +152,7 @@ HRESULT WinRTLoadComponentFromFilePath(PCWSTR manifestPath) HRESULT WinRTLoadComponentFromString(std::string_view xmlStringValue) { ComPtr xmlStream = nullptr; - xmlStream.Attach(SHCreateMemStream(reinterpret_cast(xmlStringValue.data()), strlen(xmlStringValue.data()) * sizeof(CHAR))); + xmlStream.Attach(SHCreateMemStream(reinterpret_cast(xmlStringValue.data()), static_cast(strlen(xmlStringValue.data()) * sizeof(CHAR)))); RETURN_HR_IF_NULL(E_OUTOFMEMORY, xmlStream); ComPtr xmlReaderInput; RETURN_IF_FAILED(CreateXmlReaderInputWithEncodingName(xmlStream.Get(), nullptr, L"utf-8", FALSE, nullptr, &xmlReaderInput)); From f743f7d34d063f826885f0e8561ff8572a8148b2 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 3 Aug 2022 16:57:22 -0700 Subject: [PATCH 09/17] test debug --- .../Interop/CheckInstalledStatusInterop.cs | 37 ++++++++++++++++--- .../Manifests/TestInstalledStatus.yaml | 4 +- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs index 4d9b404f72..a268632d93 100644 --- a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs +++ b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs @@ -26,23 +26,48 @@ public void SetUp() packageManager = TestFactory.CreatePackageManager(); testSource = packageManager.GetPackageCatalogByName(Constants.TestSourceName); installDir = TestCommon.GetRandomTestDir(); - } - [Test] - public async Task CheckInstalledStatus() - { // Find and install the test package. var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); var installOptions = TestFactory.CreateInstallOptions(); installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus"; - var installResult = await packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); + var installResult = packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions).GetResults(); Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); + // Add the data file listed in the manifest + File.WriteAllText(Path.Combine(installDir, "data.txt"), "Test"); + } + + [TearDown] + public void Cleanup() + { + // Find and uninstall the test package. + var options = TestFactory.CreateCreateCompositePackageCatalogOptions(); + options.Catalogs.Add(testSource); + options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; + var compositeSource = packageManager.CreateCompositePackageCatalog(options); + var searchResult = FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + var uninstallOptions = TestFactory.CreateUninstallOptions(); + var uninstallResult = packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions).GetResults(); + Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); + + // Delete the extra data file listed in the manifest + File.Delete(Path.Combine(installDir, "data.txt")); + } + + [Test] + public void CheckInstalledStatus() + { + var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); var checkResult = searchResult.CatalogPackage.CheckInstalledStatus(); Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); - // checkResult.InstalledStatus[0]. + var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; + Assert.AreEqual(Windows.System.ProcessorArchitecture.Neutral, installerInstalledStatus.InstallerInfo.Architecture); + Assert.AreEqual(PackageInstallerType.Exe, installerInstalledStatus.InstallerInfo.InstallerType); + Assert.AreEqual(PackageInstallerType.Unknown, installerInstalledStatus.InstallerInfo.NestedInstallerType); + Assert.AreEqual(PackageInstallerScope.Unknown, installerInstalledStatus.InstallerInfo.Scope); } } } \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml index b7d2792cdd..02b73f171d 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml @@ -12,11 +12,13 @@ Installers: InstallerSha256: ProductCode: CheckInstalledStatusProductId InstallationMetadata: - DefaultInstallLocation: "%ProgramFiles%\\TestApp" + DefaultInstallLocation: "%TEMP%\\TestInstalledStatus" Files: - RelativeFilePath: "data.txt" # Hash value is for a txt file with "Test" as content in utf8 FileSha256: 532eaabd9574880dbf76b9b8cc00832c20a6ec113d682299550d7a6e0f345e25 FileType: other + - RelativeFilePath: "TestExeInstalled.txt" + FileType: other ManifestType: singleton ManifestVersion: 1.3.0 From 5478bec9447848acfa7b79bb69f3ef8964293416 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 4 Aug 2022 13:42:29 -0700 Subject: [PATCH 10/17] Test fix --- src/AppInstallerCLIE2ETests/Constants.cs | 8 + .../Interop/CheckInstalledStatusInterop.cs | 216 ++++++++++++++++-- src/AppInstallerCommonCore/Filesystem.cpp | 21 +- 3 files changed, 222 insertions(+), 23 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index 61143c16c0..be24d353af 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -213,6 +213,14 @@ public class ErrorCode public const int ERROR_INSTALL_DOWNGRADE = unchecked((int)0x8A15010E); public const int ERROR_INSTALL_BLOCKED_BY_POLICY = unchecked((int)0x8A15010F); public const int ERROR_INSTALL_DEPENDENCIES = unchecked((int)0x8A150110); + + public const int INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND = unchecked((int)0x8A150201); + public const int INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE = unchecked((int)0x8A150202); + public const int INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND = unchecked((int)0x8A150203); + public const int INSTALLED_STATUS_FILE_HASH_MISMATCH = unchecked((int)0x8A150204); + public const int INSTALLED_STATUS_FILE_NOT_FOUND = unchecked((int)0x8A150205); + public const int INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK = unchecked((int)0x8A150206); + public const int INSTALLED_STATUS_FILE_ACCESS_ERROR = unchecked((int)0x8A150207); } } } diff --git a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs index a268632d93..888e63991e 100644 --- a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs +++ b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs @@ -15,6 +15,7 @@ namespace AppInstallerCLIE2ETests.Interop public class CheckInstalledStatusInterop : BaseInterop { private string installDir; + private string defaultInstallDir = Path.Combine(Path.GetTempPath(), "TestInstalledStatus"); private PackageManager packageManager; private PackageCatalogReference testSource; @@ -26,48 +27,225 @@ public void SetUp() packageManager = TestFactory.CreatePackageManager(); testSource = packageManager.GetPackageCatalogByName(Constants.TestSourceName); installDir = TestCommon.GetRandomTestDir(); - - // Find and install the test package. - var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); - var installOptions = TestFactory.CreateInstallOptions(); - installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus"; - var installResult = packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions).GetResults(); - Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); - - // Add the data file listed in the manifest - File.WriteAllText(Path.Combine(installDir, "data.txt"), "Test"); } [TearDown] - public void Cleanup() + public async Task Cleanup() { - // Find and uninstall the test package. + // Find and uninstall the test package if applicable. var options = TestFactory.CreateCreateCompositePackageCatalogOptions(); options.Catalogs.Add(testSource); options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; var compositeSource = packageManager.CreateCompositePackageCatalog(options); var searchResult = FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); - var uninstallOptions = TestFactory.CreateUninstallOptions(); - var uninstallResult = packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions).GetResults(); - Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); + if (searchResult.CatalogPackage.InstalledVersion != null) + { + var uninstallOptions = TestFactory.CreateUninstallOptions(); + var uninstallResult = await packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions); + Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); + } - // Delete the extra data file listed in the manifest - File.Delete(Path.Combine(installDir, "data.txt")); + // Remove default install location + if (Directory.Exists(defaultInstallDir)) + { + Directory.Delete(defaultInstallDir, true); + } } [Test] - public void CheckInstalledStatus() + public async Task CheckInstalledStatusArpVersionMatched() { + // Find and install the test package. var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); - var checkResult = searchResult.CatalogPackage.CheckInstalledStatus(); + var installOptions = TestFactory.CreateInstallOptions(); + installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 1.0"; + var installResult = await packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); + Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); + + // Add the data file listed in the manifest + File.WriteAllText(Path.Combine(installDir, "data.txt"), "Test"); + + // Search from composite source again after installation + var options = TestFactory.CreateCreateCompositePackageCatalogOptions(); + options.Catalogs.Add(testSource); + options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; + var compositeSource = packageManager.CreateCompositePackageCatalog(options); + searchResult = FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + + // Check installed status + var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(); Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); + // Installer info var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(Windows.System.ProcessorArchitecture.Neutral, installerInstalledStatus.InstallerInfo.Architecture); Assert.AreEqual(PackageInstallerType.Exe, installerInstalledStatus.InstallerInfo.InstallerType); Assert.AreEqual(PackageInstallerType.Unknown, installerInstalledStatus.InstallerInfo.NestedInstallerType); Assert.AreEqual(PackageInstallerScope.Unknown, installerInstalledStatus.InstallerInfo.Scope); + + // Installer status + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); + Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); + Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[2].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); + Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[4].Type); + Assert.AreEqual(defaultInstallDir, installerInstalledStatus.InstallerInstalledStatus[4].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[4].Status.HResult); + } + + [Test] + public async Task CheckInstalledStatusArpVersionNotMatched() + { + // Find and install the test package. + var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + var installOptions = TestFactory.CreateInstallOptions(); + installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 2.0"; + var installResult = await packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); + Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); + + // Add the data file listed in the manifest + File.WriteAllText(Path.Combine(installDir, "data.txt"), "Test"); + + // Search from composite source again after installation + var options = TestFactory.CreateCreateCompositePackageCatalogOptions(); + options.Catalogs.Add(testSource); + options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; + var compositeSource = packageManager.CreateCompositePackageCatalog(options); + searchResult = FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + + // Check installed status + var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(InstalledStatusType.AllAppsAndFeaturesEntryChecks); + Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); + Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); + + var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); + Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); + Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); + Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + } + + [Test] + public async Task CheckInstalledStatusArpVersionMatchedFileNotFound() + { + // Find and install the test package. + var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + var installOptions = TestFactory.CreateInstallOptions(); + installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 1.0"; + var installResult = await packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); + Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); + + // Search from composite source again after installation + var options = TestFactory.CreateCreateCompositePackageCatalogOptions(); + options.Catalogs.Add(testSource); + options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; + var compositeSource = packageManager.CreateCompositePackageCatalog(options); + searchResult = FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + + // Check installed status + var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(InstalledStatusType.AllAppsAndFeaturesEntryChecks); + Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); + Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); + + var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); + Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); + Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); + Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + } + + [Test] + public async Task CheckInstalledStatusArpVersionMatchedFileHashMisMatch() + { + // Find and install the test package. + var searchResult = FindOnePackage(testSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + var installOptions = TestFactory.CreateInstallOptions(); + installOptions.ReplacementInstallerArguments = $"/InstallDir {installDir} /ProductID CheckInstalledStatusProductId /DisplayName TestCheckInstalledStatus /Version 1.0"; + var installResult = await packageManager.InstallPackageAsync(searchResult.CatalogPackage, installOptions); + Assert.AreEqual(InstallResultStatus.Ok, installResult.Status); + + // Add the data file listed in the manifest + File.WriteAllText(Path.Combine(installDir, "data.txt"), "WrongData"); + + // Search from composite source again after installation + var options = TestFactory.CreateCreateCompositePackageCatalogOptions(); + options.Catalogs.Add(testSource); + options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; + var compositeSource = packageManager.CreateCompositePackageCatalog(options); + searchResult = FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + + // Check installed status + var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(InstalledStatusType.AllAppsAndFeaturesEntryChecks); + Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); + Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); + + var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); + Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); + Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_HASH_MISMATCH, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); + Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + } + + [Test] + public async Task CheckInstalledStatusArpNotFoundDefaultInstallLocationFound() + { + // Add the data file listed in the manifest to default install location. + Directory.CreateDirectory(defaultInstallDir); + File.WriteAllText(Path.Combine(defaultInstallDir, "data.txt"), "Test"); + + // Search from composite source without installation + var options = TestFactory.CreateCreateCompositePackageCatalogOptions(); + options.Catalogs.Add(testSource); + options.CompositeSearchBehavior = CompositeSearchBehavior.AllCatalogs; + var compositeSource = packageManager.CreateCompositePackageCatalog(options); + var searchResult = FindOnePackage(compositeSource, PackageMatchField.Id, PackageFieldMatchOption.Equals, "AppInstallerTest.TestCheckInstalledStatus"); + + // Check installed status + var checkResult = await searchResult.CatalogPackage.CheckInstalledStatusAsync(); + Assert.AreEqual(CheckInstalledStatusResultStatus.Ok, checkResult.Status); + Assert.AreEqual(1, checkResult.PackageInstalledStatus.Count); + + var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; + Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[0].Status.HResult); + Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); + Assert.AreEqual(defaultInstallDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); + Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); + Assert.AreEqual(Path.Combine(defaultInstallDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); + Assert.AreEqual(Path.Combine(defaultInstallDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); } } } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Filesystem.cpp b/src/AppInstallerCommonCore/Filesystem.cpp index 933f5ca041..e4977e84fd 100644 --- a/src/AppInstallerCommonCore/Filesystem.cpp +++ b/src/AppInstallerCommonCore/Filesystem.cpp @@ -135,13 +135,26 @@ namespace AppInstaller::Filesystem std::filesystem::path GetExpandedPath(const std::string& path) { - if (path.empty()) + std::string trimPath = path; + Utility::Trim(trimPath); + + if (trimPath.empty()) { return {}; } - std::wstring widePath = Utility::ConvertToUTF16(path); - WCHAR buffer[MAX_PATH]; - return PathUnExpandEnvStrings(widePath.c_str(), buffer, ARRAYSIZE(buffer)) ? std::filesystem::path{ buffer } : std::filesystem::path{ path }; + std::wstring widePath = Utility::ConvertToUTF16(trimPath); + DWORD count = ExpandEnvironmentStrings(widePath.c_str(), nullptr, 0); + std::wstring buffer; + buffer.resize(count); + if (ExpandEnvironmentStrings(widePath.c_str(), buffer.data(), count) > 0) + { + buffer.resize(count - 1); + return buffer; + } + else + { + return trimPath; + } } } \ No newline at end of file From 82927ee106407304cab2fc11dab19400a2412a78 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Thu, 4 Aug 2022 17:03:15 -0700 Subject: [PATCH 11/17] spelling --- src/AppInstallerRepositoryCore/CompositeSource.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index fecd2ffd2f..7b61c54e58 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -294,7 +294,7 @@ namespace AppInstaller::Repository { if (fileHashes.find(filePath) == fileHashes.end()) { - // If not found in cache, computhe the hash. + // If not found in cache, compute the hash. std::ifstream in{ filePath, std::ifstream::binary }; fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)); } From 858b37f8f963ca9d70ca2879f20e30a7f1faddd4 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 10 Aug 2022 14:16:11 -0700 Subject: [PATCH 12/17] Try fix e2e --- .../Interop/CheckInstalledStatusInterop.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs index 888e63991e..450892ecff 100644 --- a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs +++ b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs @@ -97,7 +97,7 @@ public async Task CheckInstalledStatusArpVersionMatched() Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[4].Type); - Assert.AreEqual(defaultInstallDir, installerInstalledStatus.InstallerInstalledStatus[4].Path); + Assert.AreEqual(defaultInstallDir, Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[4].Path)); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[4].Status.HResult); } @@ -238,13 +238,13 @@ public async Task CheckInstalledStatusArpNotFoundDefaultInstallLocationFound() Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[0].Status.HResult); Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); - Assert.AreEqual(defaultInstallDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); + Assert.AreEqual(defaultInstallDir, Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[1].Path)); Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); - Assert.AreEqual(Path.Combine(defaultInstallDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); + Assert.AreEqual(Path.Combine(defaultInstallDir, "data.txt"), Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[2].Path)); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); - Assert.AreEqual(Path.Combine(defaultInstallDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); + Assert.AreEqual(Path.Combine(defaultInstallDir, "TestExeInstalled.txt"), Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[3].Path)); Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); } } From eaffb417d4a77a87e7ede196515f299c5dc89ed4 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Tue, 16 Aug 2022 20:19:20 -0700 Subject: [PATCH 13/17] fix build after merge --- .../TestData/Manifests/TestInstalledStatus.yaml | 2 +- src/AppInstallerCommonCore/Filesystem.cpp | 2 +- .../Manifest/ManifestValidation.cpp | 10 +++++----- src/AppInstallerRepositoryCore/CompositeSource.cpp | 2 +- .../PackageInstallerInfo.cpp | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml index 02b73f171d..56e578b1b2 100644 --- a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml +++ b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml @@ -21,4 +21,4 @@ Installers: - RelativeFilePath: "TestExeInstalled.txt" FileType: other ManifestType: singleton -ManifestVersion: 1.3.0 +ManifestVersion: 1.4.0 diff --git a/src/AppInstallerCommonCore/Filesystem.cpp b/src/AppInstallerCommonCore/Filesystem.cpp index 3d921e96c2..e5432090ba 100644 --- a/src/AppInstallerCommonCore/Filesystem.cpp +++ b/src/AppInstallerCommonCore/Filesystem.cpp @@ -2,7 +2,7 @@ // Licensed under the MIT License. #include "pch.h" #include "Public/AppInstallerStrings.h" -#include "public/winget/FileSystem.h" +#include "public/winget/Filesystem.h" namespace AppInstaller::Filesystem { diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp index 7f7b2db442..5eb4076f5d 100644 --- a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp +++ b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -42,16 +42,16 @@ namespace AppInstaller::Manifest // Todo: use the comparator from ManifestComparator when that one is fully implemented. auto installerCmp = [](const ManifestInstaller& in1, const ManifestInstaller& in2) { - if (in1.EffectiveInstallerType() != in2.EffectiveInstallerType()) + if (in1.BaseInstallerType != in2.BaseInstallerType) { - return in1.EffectiveInstallerType() < in2.EffectiveInstallerType(); + return in1.BaseInstallerType < in2.BaseInstallerType; } - else if (IsArchiveType(in1.InstallerType)) + else if (IsArchiveType(in1.BaseInstallerType)) { - // Compare nested installer type if installer type is archive. + // Compare nested installer type if base installer type is archive. if (in1.NestedInstallerType != in2.NestedInstallerType) { - return in1.NestedInstallerType != in2.NestedInstallerType; + return in1.NestedInstallerType < in2.NestedInstallerType; } } diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index afce1d32c3..1199db5ffb 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -393,7 +393,7 @@ namespace AppInstaller::Repository { bool isMatchingInstaller = installedVersion && - IsInstallerTypeCompatible(installedType, IsArchiveType(installer.InstallerType) ? installer.NestedInstallerType : installer.InstallerType) && + IsInstallerTypeCompatible(installedType, installer.EffectiveInstallerType()) && (installedScope == ScopeEnum::Unknown || installer.Scope == ScopeEnum::Unknown || installedScope == installer.Scope) && // Treat unknown scope as compatible (installedArchitecture == Utility::Architecture::Unknown || installer.Arch == Utility::Architecture::Neutral || installedArchitecture == installer.Arch) && // Treat unknown installed architecture as compatible (installedLocale.empty() || installer.Locale.empty() || !Locale::IsWellFormedBcp47Tag(installedLocale) || Locale::GetDistanceOfLanguage(installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch); // Treat invalid locale as compatible diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp b/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp index 1245228512..871fa806f7 100644 --- a/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp +++ b/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp @@ -14,7 +14,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation } winrt::Microsoft::Management::Deployment::PackageInstallerType PackageInstallerInfo::InstallerType() { - return GetDeploymentInstallerType(m_manifestInstaller.InstallerType); + return GetDeploymentInstallerType(m_manifestInstaller.BaseInstallerType); } winrt::Microsoft::Management::Deployment::PackageInstallerType PackageInstallerInfo::NestedInstallerType() { From 5d331ca70ed3d4bc0c85432aa72f0bc2b70cabee Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Tue, 16 Aug 2022 21:16:11 -0700 Subject: [PATCH 14/17] pr comments --- src/AppInstallerCLIE2ETests/Constants.cs | 4 +- src/AppInstallerCLITests/TestSource.cpp | 5 - src/AppInstallerCLITests/TestSource.h | 1 - .../Public/AppInstallerErrors.h | 5 +- .../AppInstallerRepositoryCore.vcxproj | 1 + ...AppInstallerRepositoryCore.vcxproj.filters | 3 + .../CompositeSource.cpp | 233 ----------------- .../Microsoft/ARPHelper.cpp | 5 +- .../Microsoft/SQLiteIndexSource.cpp | 10 - .../PackageInstalledStatus.cpp | 247 ++++++++++++++++++ .../Public/winget/RepositorySearch.h | 6 +- .../Rest/RestSource.cpp | 5 - .../CatalogPackage.cpp | 2 +- .../PackageManager.idl | 4 +- 14 files changed, 264 insertions(+), 267 deletions(-) create mode 100644 src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs index c79aa9382e..bcd821863c 100644 --- a/src/AppInstallerCLIE2ETests/Constants.cs +++ b/src/AppInstallerCLIE2ETests/Constants.cs @@ -217,11 +217,11 @@ public class ErrorCode public const int ERROR_INSTALL_DEPENDENCIES = unchecked((int)0x8A150110); public const int INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND = unchecked((int)0x8A150201); - public const int INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE = unchecked((int)0x8A150202); + public const int INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE = unchecked((int)0x0A150202); public const int INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND = unchecked((int)0x8A150203); public const int INSTALLED_STATUS_FILE_HASH_MISMATCH = unchecked((int)0x8A150204); public const int INSTALLED_STATUS_FILE_NOT_FOUND = unchecked((int)0x8A150205); - public const int INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK = unchecked((int)0x8A150206); + public const int INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK = unchecked((int)0x0A150206); public const int INSTALLED_STATUS_FILE_ACCESS_ERROR = unchecked((int)0x8A150207); } } diff --git a/src/AppInstallerCLITests/TestSource.cpp b/src/AppInstallerCLITests/TestSource.cpp index 044068a6ed..11b382de0c 100644 --- a/src/AppInstallerCLITests/TestSource.cpp +++ b/src/AppInstallerCLITests/TestSource.cpp @@ -224,11 +224,6 @@ namespace TestCommon return false; } - std::vector TestPackage::CheckInstalledStatus(InstalledStatusType) const - { - return {}; - } - bool TestPackage::IsSame(const IPackage* other) const { if (IsSameOverride) diff --git a/src/AppInstallerCLITests/TestSource.h b/src/AppInstallerCLITests/TestSource.h index 34ef5539a8..bcac291f47 100644 --- a/src/AppInstallerCLITests/TestSource.h +++ b/src/AppInstallerCLITests/TestSource.h @@ -67,7 +67,6 @@ namespace TestCommon std::shared_ptr GetLatestAvailableVersion() const override; std::shared_ptr GetAvailableVersion(const AppInstaller::Repository::PackageVersionKey& versionKey) const override; bool IsUpdateAvailable() const override; - std::vector CheckInstalledStatus(AppInstaller::Repository::InstalledStatusType types) const override; bool IsSame(const IPackage* other) const override; std::shared_ptr InstalledVersion; diff --git a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h index abdf8cedac..86afb33c81 100644 --- a/src/AppInstallerCommonCore/Public/AppInstallerErrors.h +++ b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -128,15 +128,16 @@ #define APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES ((HRESULT)0x8A150110) // Status values for check package installed status results. +// Partial success has the success bit(first bit) set to 0. #define WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND S_OK #define WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND ((HRESULT)0x8A150201) #define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND S_OK -#define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE ((HRESULT)0x8A150202) +#define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE ((HRESULT)0x0A150202) #define WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND ((HRESULT)0x8A150203) #define WINGET_INSTALLED_STATUS_FILE_HASH_MATCH S_OK #define WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH ((HRESULT)0x8A150204) #define WINGET_INSTALLED_STATUS_FILE_NOT_FOUND ((HRESULT)0x8A150205) -#define WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK ((HRESULT)0x8A150206) +#define WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK ((HRESULT)0x0A150206) #define WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR ((HRESULT)0x8A150207) diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj index 80103826bd..3d11edbcfb 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -354,6 +354,7 @@ + Create diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters index 25b7164366..7f043f21a5 100644 --- a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters +++ b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -470,6 +470,9 @@ Microsoft\Schema\1_6 + + Source Files + diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp index 1199db5ffb..1e4504d93f 100644 --- a/src/AppInstallerRepositoryCore/CompositeSource.cpp +++ b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -2,7 +2,6 @@ // Licensed under the MIT License. #include "pch.h" #include "CompositeSource.h" -#include namespace AppInstaller::Repository { @@ -249,233 +248,6 @@ namespace AppInstaller::Repository return installedVersion; } - HRESULT CheckInstalledLocationStatus(const std::filesystem::path& installedLocation) - { - HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; - if (!installedLocation.empty()) - { - // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. - std::error_code error; - installLocationStatus = - std::filesystem::exists(installedLocation, error) && std::filesystem::is_directory(installedLocation, error) ? - WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND : - WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; - } - - return installLocationStatus; - } - - // Map to cache already calculated file hashes. - struct FilePathComparator - { - bool operator()(const std::filesystem::path& a, const std::filesystem::path& b) const - { - if (std::filesystem::equivalent(a, b)) - { - return false; - } - - return a < b; - } - }; - using FileHashMap = std::map; - - HRESULT CheckInstalledFileStatus( - const std::filesystem::path& filePath, - const Utility::SHA256::HashBuffer& expectedHash, - FileHashMap& fileHashes) - { - HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; - try - { - if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) - { - fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; - if (!expectedHash.empty()) - { - if (fileHashes.find(filePath) == fileHashes.end()) - { - // If not found in cache, compute the hash. - std::ifstream in{ filePath, std::ifstream::binary }; - fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)); - } - - fileStatus = Utility::SHA256::AreEqual(expectedHash, fileHashes.at(filePath)) ? - WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; - } - } - } - catch (...) - { - fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; - } - - return fileStatus; - } - - std::vector CheckInstalledStatusInternal( - const std::shared_ptr& installedVersion, - const std::shared_ptr& availablePackage, - InstalledStatusType checkTypes) - { - using namespace AppInstaller::Manifest; - - std::vector result; - bool checkFileHash = false; - std::shared_ptr availableVersion; - FileHashMap fileHashes; - - // Variables for metadata from installed version. - InstallerTypeEnum installedType = InstallerTypeEnum::Unknown; - ScopeEnum installedScope = ScopeEnum::Unknown; - std::filesystem::path installedLocation; - std::string installedLocale; - Utility::Architecture installedArchitecture = Utility::Architecture::Unknown; - HRESULT installedLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; - - // Prepare installed metadata from installed version. - // Determine the available package version to be used for installed status checking. - // Only perform file hash check if we find an available version that matches installed version. - if (installedVersion) - { - // Installed metadata. - auto installedMetadata = installedVersion->GetMetadata(); - installedType = ConvertToInstallerTypeEnum(installedMetadata[PackageVersionMetadata::InstalledType]); - installedScope = ConvertToScopeEnum(installedMetadata[PackageVersionMetadata::InstalledScope]); - installedLocation = Filesystem::GetExpandedPath(installedMetadata[PackageVersionMetadata::InstalledLocation]); - installedLocale = installedMetadata[PackageVersionMetadata::InstalledLocale]; - installedArchitecture = Utility::ConvertToArchitectureEnum(installedMetadata[PackageVersionMetadata::InstalledArchitecture]); - installedLocationStatus = CheckInstalledLocationStatus(installedLocation); - - // Determine available version. - Utility::Version installedVersionAsVersion{ installedVersion->GetProperty(PackageVersionProperty::Version) }; - auto installedChannel = installedVersion->GetProperty(PackageVersionProperty::Channel); - PackageVersionKey versionKey; - versionKey.Channel = installedChannel.get(); - - if (installedVersionAsVersion.IsApproximate()) - { - // Use the base version as available version if installed version is mapped to be an approximate. - versionKey.Version = installedVersionAsVersion.GetBaseVersion().ToString(); - availableVersion = availablePackage->GetAvailableVersion(versionKey); - // It's unexpected if the installed version is already mapped to some version. - THROW_HR_IF(E_UNEXPECTED, !availableVersion); - } - else - { - versionKey.Version = installedVersionAsVersion.ToString(); - availableVersion = availablePackage->GetAvailableVersion(versionKey); - if (availableVersion) - { - checkFileHash = true; - } - } - } - - if (!availableVersion) - { - // No installed version, or installed version not found in available versions, - // then attempt to check installed status using latest version. - availableVersion = availablePackage->GetLatestAvailableVersion(); - THROW_HR_IF(E_UNEXPECTED, !availableVersion); - } - - auto manifest = availableVersion->GetManifest(); - for (auto const& installer : manifest.Installers) - { - if (installer.InstallationMetadata.HasData()) - { - InstallerInstalledStatus installerStatus; - installerStatus.Installer = installer; - - // ARP related checks - if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllAppsAndFeaturesEntryChecks)) - { - bool isMatchingInstaller = - installedVersion && - IsInstallerTypeCompatible(installedType, installer.EffectiveInstallerType()) && - (installedScope == ScopeEnum::Unknown || installer.Scope == ScopeEnum::Unknown || installedScope == installer.Scope) && // Treat unknown scope as compatible - (installedArchitecture == Utility::Architecture::Unknown || installer.Arch == Utility::Architecture::Neutral || installedArchitecture == installer.Arch) && // Treat unknown installed architecture as compatible - (installedLocale.empty() || installer.Locale.empty() || !Locale::IsWellFormedBcp47Tag(installedLocale) || Locale::GetDistanceOfLanguage(installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch); // Treat invalid locale as compatible - - // ARP entry status - if (WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntry)) - { - installerStatus.Status.emplace_back( - InstalledStatusType::AppsAndFeaturesEntry, - "", - isMatchingInstaller ? WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND : WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND); - } - - // ARP install location status - if (isMatchingInstaller && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocation)) - { - installerStatus.Status.emplace_back( - InstalledStatusType::AppsAndFeaturesEntryInstallLocation, - installedLocation.string(), - installedLocationStatus); - } - - // ARP install location files - if (isMatchingInstaller && - installedLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && - WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile)) - { - for (auto const& file : installer.InstallationMetadata.Files) - { - std::filesystem::path filePath = installedLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; - auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); - - installerStatus.Status.emplace_back( - InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, - filePath.string(), - fileStatus); - } - } - } - - // Default install location related checks - if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllDefaultInstallLocationChecks)) - { - auto defaultInstalledLocation = Filesystem::GetExpandedPath(installer.InstallationMetadata.DefaultInstallLocation); - HRESULT defaultInstalledLocationStatus = CheckInstalledLocationStatus(defaultInstalledLocation); - - // Default install location status - if (WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocation)) - { - installerStatus.Status.emplace_back( - InstalledStatusType::DefaultInstallLocation, - defaultInstalledLocation.string(), - defaultInstalledLocationStatus); - } - - // Default install location files - if (defaultInstalledLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && - WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocationFile)) - { - for (auto const& file : installer.InstallationMetadata.Files) - { - std::filesystem::path filePath = defaultInstalledLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; - auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); - - installerStatus.Status.emplace_back( - InstalledStatusType::DefaultInstallLocationFile, - filePath.string(), - fileStatus); - } - } - } - - if (!installerStatus.Status.empty()) - { - result.emplace_back(std::move(installerStatus)); - } - } - } - - return result; - } - // A composite package installed version that allows us to override the source or the version. struct CompositeInstalledVersion : public IPackageVersion { @@ -629,11 +401,6 @@ namespace AppInstaller::Repository return (latest && (GetVACFromVersion(installed.get()).IsUpdatedBy(GetVACFromVersion(latest.get())))); } - std::vector CheckInstalledStatus(InstalledStatusType checkTypes) const override - { - return CheckInstalledStatusInternal(GetInstalledVersion(), GetAvailablePackage(), checkTypes); - } - bool IsSame(const IPackage* other) const override { const CompositePackage* otherComposite = dynamic_cast(other); diff --git a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp index 2ed03d3fe9..699deeaa9d 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/ARPHelper.cpp @@ -462,11 +462,10 @@ namespace AppInstaller::Repository::Microsoft // Pick up Language to enable proper selection of language for upgrade. AddMetadataIfPresent(arpKey, Language, index, manifestId, PackageVersionMetadata::InstalledLocale); - // Set installed architecture info. - index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledArchitecture, architecture); - if (Manifest::ConvertToInstallerTypeEnum(GetStringValue(arpKey, std::wstring{ ToString(PortableValueName::WinGetInstallerType) })) == Manifest::InstallerTypeEnum::Portable) { + // Portable uninstall requires the installed architecture for locating the entry in the registry. + index.SetMetadataByManifestId(manifestId, PackageVersionMetadata::InstalledArchitecture, architecture); installedType = Manifest::InstallerTypeEnum::Portable; } diff --git a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp index 300e585671..bb74dda174 100644 --- a/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp +++ b/src/AppInstallerRepositoryCore/Microsoft/SQLiteIndexSource.cpp @@ -282,11 +282,6 @@ namespace AppInstaller::Repository::Microsoft return false; } - std::vector CheckInstalledStatus(InstalledStatusType) const override - { - THROW_HR(E_NOTIMPL); - } - bool IsSame(const IPackage* other) const override { const AvailablePackage* otherAvailable = dynamic_cast(other); @@ -336,11 +331,6 @@ namespace AppInstaller::Repository::Microsoft return false; } - std::vector CheckInstalledStatus(InstalledStatusType) const override - { - THROW_HR(E_NOTIMPL); - } - bool IsSame(const IPackage* other) const override { const InstalledPackage* otherInstalled = dynamic_cast(other); diff --git a/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp b/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp new file mode 100644 index 0000000000..2270db5415 --- /dev/null +++ b/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "Public/winget/RepositorySearch.h" +#include + +using namespace AppInstaller::Settings; +using namespace std::chrono_literals; + +namespace AppInstaller::Repository +{ + namespace + { + HRESULT CheckInstalledLocationStatus(const std::filesystem::path& installedLocation) + { + HRESULT installLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; + if (!installedLocation.empty()) + { + // Use the none throw version, if the directory cannot be reached, it's treated as not found and later file checks are not performed. + std::error_code error; + installLocationStatus = + std::filesystem::exists(installedLocation, error) && std::filesystem::is_directory(installedLocation, error) ? + WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND : + WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND; + } + + return installLocationStatus; + } + + // Map to cache already calculated file hashes. + struct FilePathComparator + { + bool operator()(const std::filesystem::path& a, const std::filesystem::path& b) const + { + if (std::filesystem::equivalent(a, b)) + { + return false; + } + + return a < b; + } + }; + using FileHashMap = std::map; + + HRESULT CheckInstalledFileStatus( + const std::filesystem::path& filePath, + const Utility::SHA256::HashBuffer& expectedHash, + FileHashMap& fileHashes) + { + HRESULT fileStatus = WINGET_INSTALLED_STATUS_FILE_NOT_FOUND; + try + { + if (std::filesystem::exists(filePath) && std::filesystem::is_regular_file(filePath)) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK; + if (!expectedHash.empty()) + { + auto itr = fileHashes.find(filePath); + if (itr == fileHashes.end()) + { + // If not found in cache, compute the hash. + std::ifstream in{ filePath, std::ifstream::binary }; + itr = fileHashes.emplace(filePath, Utility::SHA256::ComputeHash(in)).first; + } + + fileStatus = Utility::SHA256::AreEqual(expectedHash, itr->second) ? + WINGET_INSTALLED_STATUS_FILE_HASH_MATCH : WINGET_INSTALLED_STATUS_FILE_HASH_MISMATCH; + } + } + } + catch (...) + { + fileStatus = WINGET_INSTALLED_STATUS_FILE_ACCESS_ERROR; + } + + return fileStatus; + } + + std::vector CheckInstalledStatusInternal( + const std::shared_ptr& package, + InstalledStatusType checkTypes) + { + using namespace AppInstaller::Manifest; + + std::vector result; + bool checkFileHash = false; + std::shared_ptr installedVersion = package->GetInstalledVersion(); + std::shared_ptr availableVersion; + FileHashMap fileHashes; + + // Variables for metadata from installed version. + InstallerTypeEnum installedType = InstallerTypeEnum::Unknown; + ScopeEnum installedScope = ScopeEnum::Unknown; + std::filesystem::path installedLocation; + std::string installedLocale; + Utility::Architecture installedArchitecture = Utility::Architecture::Unknown; + HRESULT installedLocationStatus = WINGET_INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE; + + // Prepare installed metadata from installed version. + // Determine the available package version to be used for installed status checking. + // Only perform file hash check if we find an available version that matches installed version. + if (installedVersion) + { + // Installed metadata. + auto installedMetadata = installedVersion->GetMetadata(); + installedType = ConvertToInstallerTypeEnum(installedMetadata[PackageVersionMetadata::InstalledType]); + installedScope = ConvertToScopeEnum(installedMetadata[PackageVersionMetadata::InstalledScope]); + installedLocation = Filesystem::GetExpandedPath(installedMetadata[PackageVersionMetadata::InstalledLocation]); + installedLocale = installedMetadata[PackageVersionMetadata::InstalledLocale]; + installedArchitecture = Utility::ConvertToArchitectureEnum(installedMetadata[PackageVersionMetadata::InstalledArchitecture]); + installedLocationStatus = CheckInstalledLocationStatus(installedLocation); + + // Determine available version. + Utility::Version installedVersionAsVersion{ installedVersion->GetProperty(PackageVersionProperty::Version) }; + auto installedChannel = installedVersion->GetProperty(PackageVersionProperty::Channel); + PackageVersionKey versionKey; + versionKey.Channel = installedChannel.get(); + + if (installedVersionAsVersion.IsApproximate()) + { + // Use the base version as available version if installed version is mapped to be an approximate. + versionKey.Version = installedVersionAsVersion.GetBaseVersion().ToString(); + availableVersion = package->GetAvailableVersion(versionKey); + // It's unexpected if the installed version is already mapped to some version. + THROW_HR_IF(E_UNEXPECTED, !availableVersion); + } + else + { + versionKey.Version = installedVersionAsVersion.ToString(); + availableVersion = package->GetAvailableVersion(versionKey); + if (availableVersion) + { + checkFileHash = true; + } + } + } + + if (!availableVersion) + { + // No installed version, or installed version not found in available versions, + // then attempt to check installed status using latest version. + availableVersion = package->GetLatestAvailableVersion(); + THROW_HR_IF(E_UNEXPECTED, !availableVersion); + } + + auto manifest = availableVersion->GetManifest(); + for (auto const& installer : manifest.Installers) + { + if (installer.InstallationMetadata.HasData()) + { + InstallerInstalledStatus installerStatus; + installerStatus.Installer = installer; + + // ARP related checks + if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllAppsAndFeaturesEntryChecks)) + { + bool isMatchingInstaller = + installedVersion && + IsInstallerTypeCompatible(installedType, installer.EffectiveInstallerType()) && + (installedScope == ScopeEnum::Unknown || installer.Scope == ScopeEnum::Unknown || installedScope == installer.Scope) && // Treat unknown scope as compatible + (installedArchitecture == Utility::Architecture::Unknown || installer.Arch == Utility::Architecture::Neutral || installedArchitecture == installer.Arch) && // Treat unknown installed architecture as compatible + (installedLocale.empty() || installer.Locale.empty() || !Locale::IsWellFormedBcp47Tag(installedLocale) || Locale::GetDistanceOfLanguage(installedLocale, installer.Locale) >= Locale::MinimumDistanceScoreAsCompatibleMatch); // Treat invalid locale as compatible + + // ARP entry status + if (WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntry)) + { + installerStatus.Status.emplace_back( + InstalledStatusType::AppsAndFeaturesEntry, + "", + isMatchingInstaller ? WINGET_INSTALLED_STATUS_ARP_ENTRY_FOUND : WINGET_INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND); + } + + // ARP install location status + if (isMatchingInstaller && WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocation)) + { + installerStatus.Status.emplace_back( + InstalledStatusType::AppsAndFeaturesEntryInstallLocation, + installedLocation.string(), + installedLocationStatus); + } + + // ARP install location files + if (isMatchingInstaller && + installedLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && + WI_IsFlagSet(checkTypes, InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile)) + { + for (auto const& file : installer.InstallationMetadata.Files) + { + std::filesystem::path filePath = installedLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; + auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); + + installerStatus.Status.emplace_back( + InstalledStatusType::AppsAndFeaturesEntryInstallLocationFile, + filePath.string(), + fileStatus); + } + } + } + + // Default install location related checks + if (WI_IsAnyFlagSet(checkTypes, InstalledStatusType::AllDefaultInstallLocationChecks)) + { + auto defaultInstalledLocation = Filesystem::GetExpandedPath(installer.InstallationMetadata.DefaultInstallLocation); + HRESULT defaultInstalledLocationStatus = CheckInstalledLocationStatus(defaultInstalledLocation); + + // Default install location status + if (WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocation)) + { + installerStatus.Status.emplace_back( + InstalledStatusType::DefaultInstallLocation, + defaultInstalledLocation.string(), + defaultInstalledLocationStatus); + } + + // Default install location files + if (defaultInstalledLocationStatus == WINGET_INSTALLED_STATUS_INSTALL_LOCATION_FOUND && + WI_IsFlagSet(checkTypes, InstalledStatusType::DefaultInstallLocationFile)) + { + for (auto const& file : installer.InstallationMetadata.Files) + { + std::filesystem::path filePath = defaultInstalledLocation / std::filesystem::path{ static_cast(file.RelativeFilePath) }; + auto fileStatus = CheckInstalledFileStatus(filePath, checkFileHash ? file.FileSha256 : Utility::SHA256::HashBuffer{}, fileHashes); + + installerStatus.Status.emplace_back( + InstalledStatusType::DefaultInstallLocationFile, + filePath.string(), + fileStatus); + } + } + } + + if (!installerStatus.Status.empty()) + { + result.emplace_back(std::move(installerStatus)); + } + } + } + + return result; + } + } + + std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes) + { + return CheckInstalledStatusInternal(package, checkTypes); + } +} diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h index 932eee698c..0a6e6d232b 100644 --- a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h +++ b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -321,9 +321,6 @@ namespace AppInstaller::Repository // Gets a value indicating whether an available version is newer than the installed version. virtual bool IsUpdateAvailable() const = 0; - // Checks installed status of the package. - virtual std::vector CheckInstalledStatus(InstalledStatusType types = InstalledStatusType::AllChecks) const = 0; - // Determines if the given IPackage refers to the same package as this one. virtual bool IsSame(const IPackage*) const = 0; }; @@ -383,4 +380,7 @@ namespace AppInstaller::Repository private: mutable std::string m_whatMessage; }; + + // Checks installed status of a package. + std::vector CheckPackageInstalledStatus(const std::shared_ptr& package, InstalledStatusType checkTypes = InstalledStatusType::AllChecks); } diff --git a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp index 2ccc078a38..e5a7ba0d78 100644 --- a/src/AppInstallerRepositoryCore/Rest/RestSource.cpp +++ b/src/AppInstallerRepositoryCore/Rest/RestSource.cpp @@ -85,11 +85,6 @@ namespace AppInstaller::Repository::Rest return false; } - std::vector CheckInstalledStatus(InstalledStatusType) const override - { - THROW_HR(E_NOTIMPL); - } - bool IsSame(const IPackage* other) const override { const AvailablePackage* otherAvailablePackage = dynamic_cast(other); diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.cpp b/src/Microsoft.Management.Deployment/CatalogPackage.cpp index 759e2570a6..16abdd270b 100644 --- a/src/Microsoft.Management.Deployment/CatalogPackage.cpp +++ b/src/Microsoft.Management.Deployment/CatalogPackage.cpp @@ -112,7 +112,7 @@ namespace winrt::Microsoft::Management::Deployment::implementation try { - auto checkResult = m_package->CheckInstalledStatus(static_cast<::AppInstaller::Repository::InstalledStatusType>(checkTypes)); + auto checkResult = ::AppInstaller::Repository::CheckPackageInstalledStatus(m_package, static_cast<::AppInstaller::Repository::InstalledStatusType>(checkTypes)); // Build the result object from the checkResult for (auto const& entry : checkResult) diff --git a/src/Microsoft.Management.Deployment/PackageManager.idl b/src/Microsoft.Management.Deployment/PackageManager.idl index 84f3bbc6b9..aca6430c76 100644 --- a/src/Microsoft.Management.Deployment/PackageManager.idl +++ b/src/Microsoft.Management.Deployment/PackageManager.idl @@ -446,8 +446,8 @@ namespace Microsoft.Management.Deployment [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] { - /// Check the installed status of the package. It's required to call this method from a composite package - /// with installed info for more accurate and complete installed status. + /// Check the installed status of the package. For more accurate and complete installed status, it's required to + /// call this method from a composite package from a newly created package catalog with installed info. /// This may require downloading information from a server. Windows.Foundation.IAsyncOperation CheckInstalledStatusAsync(InstalledStatusType checkTypes); CheckInstalledStatusResult CheckInstalledStatus(InstalledStatusType checkTypes); From 55e2208e0038f06cb6def7aa7ca46c366c429347 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 17 Aug 2022 13:15:15 -0700 Subject: [PATCH 15/17] try fix e2e tests --- .../Interop/CheckInstalledStatusInterop.cs | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs index 450892ecff..8f16d86807 100644 --- a/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs +++ b/src/AppInstallerCLIE2ETests/Interop/CheckInstalledStatusInterop.cs @@ -5,11 +5,13 @@ namespace AppInstallerCLIE2ETests.Interop { using Microsoft.Management.Deployment; using Microsoft.Management.Deployment.Projection; - using NUnit.Framework; + using NUnit.Framework; + using WinRT; using System; using System.IO; - using System.Threading.Tasks; - + using System.Threading.Tasks; + + [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.InProcess), Category = nameof(InstanceInitializersSource.InProcess))] [TestFixtureSource(typeof(InstanceInitializersSource), nameof(InstanceInitializersSource.OutOfProcess), Category = nameof(InstanceInitializersSource.OutOfProcess))] public class CheckInstalledStatusInterop : BaseInterop @@ -86,19 +88,19 @@ public async Task CheckInstalledStatusArpVersionMatched() // Installer status Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[2].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[4].Type); Assert.AreEqual(defaultInstallDir, Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[4].Path)); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[4].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_INSTALL_LOCATION_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[4])); } [Test] @@ -128,16 +130,16 @@ public async Task CheckInstalledStatusArpVersionNotMatched() var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); } [Test] @@ -164,16 +166,16 @@ public async Task CheckInstalledStatusArpVersionMatchedFileNotFound() var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); } [Test] @@ -203,16 +205,16 @@ public async Task CheckInstalledStatusArpVersionMatchedFileHashMisMatch() var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[0].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(installDir, installerInstalledStatus.InstallerInstalledStatus[1].Path); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(installDir, "data.txt"), installerInstalledStatus.InstallerInstalledStatus[2].Path); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_HASH_MISMATCH, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_HASH_MISMATCH, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntryInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(installDir, "TestExeInstalled.txt"), installerInstalledStatus.InstallerInstalledStatus[3].Path); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); } [Test] @@ -236,16 +238,34 @@ public async Task CheckInstalledStatusArpNotFoundDefaultInstallLocationFound() var installerInstalledStatus = checkResult.PackageInstalledStatus[0]; Assert.AreEqual(InstalledStatusType.AppsAndFeaturesEntry, installerInstalledStatus.InstallerInstalledStatus[0].Type); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[0].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_ARP_ENTRY_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); Assert.AreEqual(defaultInstallDir, Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[1].Path)); - Assert.AreEqual(null, installerInstalledStatus.InstallerInstalledStatus[1].Status); + Assert.AreEqual(Constants.ErrorCode.S_OK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[1])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[2].Type); Assert.AreEqual(Path.Combine(defaultInstallDir, "data.txt"), Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[2].Path)); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, installerInstalledStatus.InstallerInstalledStatus[2].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_FOUND_WITHOUT_HASH_CHECK, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[2])); Assert.AreEqual(InstalledStatusType.DefaultInstallLocationFile, installerInstalledStatus.InstallerInstalledStatus[3].Type); Assert.AreEqual(Path.Combine(defaultInstallDir, "TestExeInstalled.txt"), Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[3].Path)); - Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, installerInstalledStatus.InstallerInstalledStatus[3].Status.HResult); + Assert.AreEqual(Constants.ErrorCode.INSTALLED_STATUS_FILE_NOT_FOUND, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); + } + + // CsWinrt maps success error codes(e.g. INSTALLED_STATUS_INSTALL_LOCATION_NOT_APPLICABLE) to null exception. + // In this case we cannot get the exact hresult by calling winrt projection api. + // This method is created to directly get hresult from the InstalledStatus object for the tests to compare. + unsafe private static int GetHResultFromInstalledStatus(InstalledStatus status) + { + if (status.Status != null) + { + return status.Status.HResult; + } + else + { + IObjectReference objRef = ((IWinRTObject)status).NativeObject; + ABI.System.Exception exception = default; + (*(delegate* unmanaged[Stdcall]**)objRef.ThisPtr)[8](objRef.ThisPtr, out exception); + return exception.hr; + } } } } \ No newline at end of file From 3d8e4e1d1afdac6a49ae1d9c61989f89dc565f1b Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Wed, 17 Aug 2022 14:15:03 -0700 Subject: [PATCH 16/17] fix spelling --- .github/actions/spelling/expect.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 75d1c18de9..4e2cd1aadb 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -193,6 +193,7 @@ installinprogress installshield insufficientmemory Intelli +IObject IPackage IPersist IRead @@ -207,6 +208,7 @@ itr IUninstall IVector IWeb +IWin IZone jdk jfearn From 26b5983de6a0bb8bdd59dc1f7b148a9b3523f866 Mon Sep 17 00:00:00 2001 From: Yao Sun Date: Mon, 19 Sep 2022 14:24:25 -0700 Subject: [PATCH 17/17] reuse existing code after merge --- src/AppInstallerCommonCore/Filesystem.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/AppInstallerCommonCore/Filesystem.cpp b/src/AppInstallerCommonCore/Filesystem.cpp index e5432090ba..652402aa5a 100644 --- a/src/AppInstallerCommonCore/Filesystem.cpp +++ b/src/AppInstallerCommonCore/Filesystem.cpp @@ -173,23 +173,13 @@ namespace AppInstaller::Filesystem std::string trimPath = path; Utility::Trim(trimPath); - if (trimPath.empty()) - { - return {}; - } - - std::wstring widePath = Utility::ConvertToUTF16(trimPath); - DWORD count = ExpandEnvironmentStrings(widePath.c_str(), nullptr, 0); - std::wstring buffer; - buffer.resize(count); - if (ExpandEnvironmentStrings(widePath.c_str(), buffer.data(), count) > 0) + try { - buffer.resize(count - 1); - return buffer; + return Utility::ExpandEnvironmentVariables(Utility::ConvertToUTF16(trimPath)); } - else + catch (...) { - return trimPath; + return path; } } } \ No newline at end of file