commit a92346db8dce0ba7700ddb9490917a90309aad71 parent 28741001257faa4deaf0bda51b93af04c597f75f Author: yao-msft <50888816+yao-msft@users.noreply.github.com> Date: Mon, 19 Sep 2022 16:10:57 -0700 Expose deeper installation detection through Com (#2420) Diffstat:
40 files changed, 1213 insertions(+), 25 deletions(-)
diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt @@ -183,10 +183,12 @@ installinprogress installshield insufficientmemory Intelli +IObject ishelp ISQ ISVs itr +IWin jdk jfearn JObject diff --git a/src/AppInstallerCLIE2ETests/Constants.cs b/src/AppInstallerCLIE2ETests/Constants.cs @@ -215,6 +215,14 @@ namespace AppInstallerCLIE2ETests 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)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)0x0A150206); + 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 @@ -0,0 +1,271 @@ +// 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 WinRT; + 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 string defaultInstallDir = Path.Combine(Path.GetTempPath(), "TestInstalledStatus"); + 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(); + } + + [TearDown] + public async Task Cleanup() + { + // 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"); + if (searchResult.CatalogPackage.InstalledVersion != null) + { + var uninstallOptions = TestFactory.CreateUninstallOptions(); + var uninstallResult = await packageManager.UninstallPackageAsync(searchResult.CatalogPackage, uninstallOptions); + Assert.AreEqual(UninstallResultStatus.Ok, uninstallResult.Status); + } + + // Remove default install location + if (Directory.Exists(defaultInstallDir)) + { + Directory.Delete(defaultInstallDir, true); + } + } + + [Test] + public async Task CheckInstalledStatusArpVersionMatched() + { + // 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"), "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(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(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.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, 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, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[4])); + } + + [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(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(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, 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, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); + } + + [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(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(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, 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, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); + } + + [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(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(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, 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, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[3])); + } + + [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, GetHResultFromInstalledStatus(installerInstalledStatus.InstallerInstalledStatus[0])); + Assert.AreEqual(InstalledStatusType.DefaultInstallLocation, installerInstalledStatus.InstallerInstalledStatus[1].Type); + Assert.AreEqual(defaultInstallDir, Path.GetFullPath(installerInstalledStatus.InstallerInstalledStatus[1].Path)); + 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, 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, 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]<IntPtr, out global::ABI.System.Exception, int>**)objRef.ThisPtr)[8](objRef.ThisPtr, out exception); + return exception.hr; + } + } + } +}+ \ No newline at end of file diff --git a/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml b/src/AppInstallerCLIE2ETests/TestData/Manifests/TestInstalledStatus.yaml @@ -0,0 +1,24 @@ +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: <EXEHASH> + ProductCode: CheckInstalledStatusProductId + InstallationMetadata: + 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.4.0 diff --git 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 "winget/Filesystem.h" +#include "public/winget/Filesystem.h" namespace AppInstaller::Filesystem { @@ -167,4 +167,19 @@ namespace AppInstaller::Filesystem } } } + + std::filesystem::path GetExpandedPath(const std::string& path) + { + std::string trimPath = path; + Utility::Trim(trimPath); + + try + { + return Utility::ExpandEnvironmentVariables(Utility::ConvertToUTF16(trimPath)); + } + catch (...) + { + return path; + } + } } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp b/src/AppInstallerCommonCore/Manifest/ManifestValidation.cpp @@ -97,9 +97,17 @@ 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.BaseInstallerType)) + { + // Compare nested installer type if base 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/AppInstallerErrors.h b/src/AppInstallerCommonCore/Public/AppInstallerErrors.h @@ -128,6 +128,19 @@ #define APPINSTALLER_CLI_ERROR_INSTALL_DEPENDENCIES ((HRESULT)0x8A150110) #define APPINSTALLER_CLI_ERROR_INSTALL_PACKAGE_IN_USE_BY_APPLICATION ((HRESULT)0x8A150111) +// 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)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)0x0A150206) +#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 @@ -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/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 <filesystem> namespace AppInstaller::Filesystem { @@ -22,4 +22,7 @@ namespace AppInstaller::Filesystem // Creates a symlink that points to the target path. bool CreateSymlink(const std::filesystem::path& path, const std::filesystem::path& target); + + // Get expanded file system path. + std::filesystem::path GetExpandedPath(const std::string& path); } \ No newline at end of file diff --git a/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h b/src/AppInstallerCommonCore/Public/winget/ManifestCommon.h @@ -278,6 +278,9 @@ namespace AppInstaller::Manifest { string_t DefaultInstallLocation; std::vector<InstalledFile> 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 @@ -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/AppInstallerRepositoryCore.vcxproj b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj @@ -363,6 +363,7 @@ <ClCompile Include="Microsoft\SQLiteIndexSource.cpp" /> <ClCompile Include="Microsoft\SQLiteStorageBase.cpp" /> <ClCompile Include="PackageDependenciesValidation.cpp" /> + <ClCompile Include="PackageInstalledStatus.cpp" /> <ClCompile Include="PackageTrackingCatalog.cpp" /> <ClCompile Include="pch.cpp"> <PrecompiledHeader>Create</PrecompiledHeader> diff --git a/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters b/src/AppInstallerRepositoryCore/AppInstallerRepositoryCore.vcxproj.filters @@ -500,6 +500,9 @@ <ClCompile Include="Microsoft\SQLiteStorageBase.cpp"> <Filter>Microsoft</Filter> </ClCompile> + <ClCompile Include="PackageInstalledStatus.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <None Include="PropertySheet.props" /> diff --git a/src/AppInstallerRepositoryCore/CompositeSource.cpp b/src/AppInstallerRepositoryCore/CompositeSource.cpp @@ -417,17 +417,17 @@ namespace AppInstaller::Repository return true; } - const std::shared_ptr<IPackage>& GetInstalledPackage() + const std::shared_ptr<IPackage>& GetInstalledPackage() const { return m_installedPackage; } - const std::shared_ptr<IPackage>& GetAvailablePackage() + const std::shared_ptr<IPackage>& GetAvailablePackage() const { return m_availablePackage; } - const std::shared_ptr<IPackage>& GetTrackingPackage() + const std::shared_ptr<IPackage>& GetTrackingPackage() const { return m_trackingPackage; } diff --git a/src/AppInstallerRepositoryCore/PackageInstalledStatus.cpp 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 <winget/Filesystem.h> + +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<std::filesystem::path, Utility::SHA256::HashBuffer, FilePathComparator>; + + 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<InstallerInstalledStatus> CheckInstalledStatusInternal( + const std::shared_ptr<IPackage>& package, + InstalledStatusType checkTypes) + { + using namespace AppInstaller::Manifest; + + std::vector<InstallerInstalledStatus> result; + bool checkFileHash = false; + std::shared_ptr<IPackageVersion> installedVersion = package->GetInstalledVersion(); + std::shared_ptr<IPackageVersion> 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<std::string>(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<std::string>(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<InstallerInstalledStatus> CheckPackageInstalledStatus(const std::shared_ptr<IPackage>& package, InstalledStatusType checkTypes) + { + return CheckInstalledStatusInternal(package, checkTypes); + } +} diff --git a/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h b/src/AppInstallerRepositoryCore/Public/winget/RepositorySearch.h @@ -248,6 +248,54 @@ 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, + + // 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); + + // 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; + + 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> Status; + }; + // A package, potentially containing information about it's local state and the available versions. struct IPackage { @@ -332,4 +380,7 @@ namespace AppInstaller::Repository private: mutable std::string m_whatMessage; }; + + // Checks installed status of a package. + std::vector<InstallerInstalledStatus> CheckPackageInstalledStatus(const std::shared_ptr<IPackage>& package, InstalledStatusType checkTypes = InstalledStatusType::AllChecks); } diff --git 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/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 <wil\cppwinrt_wrl.h> namespace winrt::Microsoft::Management::Deployment::implementation @@ -96,6 +98,50 @@ namespace winrt::Microsoft::Management::Deployment::implementation { return m_package->IsUpdateAvailable(); } + Windows::Foundation::IAsyncOperation<winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult> CatalogPackage::CheckInstalledStatusAsync( + Microsoft::Management::Deployment::InstalledStatusType checkTypes) + { + co_return CheckInstalledStatus(checkTypes); + } + Microsoft::Management::Deployment::CheckInstalledStatusResult CatalogPackage::CheckInstalledStatus( + Microsoft::Management::Deployment::InstalledStatusType checkTypes) + { + Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::Ok; + Windows::Foundation::Collections::IVector<Microsoft::Management::Deployment::PackageInstallerInstalledStatus> installedStatus{ + winrt::single_threaded_vector<Microsoft::Management::Deployment::PackageInstallerInstalledStatus>() }; + + try + { + 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) + { + auto checkInstallerResult = winrt::make_self<wil::details::module_count_wrapper< + winrt::Microsoft::Management::Deployment::implementation::PackageInstallerInstalledStatus>>(); + checkInstallerResult->Initialize(entry); + + installedStatus.Append(*checkInstallerResult); + } + } + catch (...) + { + status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::InternalError; + } + + auto checkInstalledStatusResult = winrt::make_self<wil::details::module_count_wrapper< + winrt::Microsoft::Management::Deployment::implementation::CheckInstalledStatusResult>>(); + checkInstalledStatusResult->Initialize(status, installedStatus); + return *checkInstalledStatusResult; + } + winrt::Windows::Foundation::IAsyncOperation<winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult> 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() { return m_package; diff --git a/src/Microsoft.Management.Deployment/CatalogPackage.h b/src/Microsoft.Management.Deployment/CatalogPackage.h @@ -23,6 +23,13 @@ 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<winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult> CheckInstalledStatusAsync( + winrt::Microsoft::Management::Deployment::InstalledStatusType checkTypes); + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult CheckInstalledStatus( + winrt::Microsoft::Management::Deployment::InstalledStatusType checkTypes); + winrt::Windows::Foundation::IAsyncOperation<winrt::Microsoft::Management::Deployment::CheckInstalledStatusResult> 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 @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#include "pch.h" +#include "CheckInstalledStatusResult.h" +#include "CheckInstalledStatusResult.g.cpp" +#include <wil\cppwinrt_wrl.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void CheckInstalledStatusResult::Initialize( + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status, + Windows::Foundation::Collections::IVector<winrt::Microsoft::Management::Deployment::PackageInstallerInstalledStatus> installedStatus) + { + m_status = status; + m_installedStatus = installedStatus; + } + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus CheckInstalledStatusResult::Status() + { + return m_status; + } + winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::PackageInstallerInstalledStatus> CheckInstalledStatusResult::PackageInstalledStatus() + { + return m_installedStatus.GetView(); + } +} diff --git a/src/Microsoft.Management.Deployment/CheckInstalledStatusResult.h 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 <winrt/Windows.Foundation.Collections.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct CheckInstalledStatusResult : CheckInstalledStatusResultT<CheckInstalledStatusResult> + { + CheckInstalledStatusResult() = default; + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + void Initialize( + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus status, + Windows::Foundation::Collections::IVector<winrt::Microsoft::Management::Deployment::PackageInstallerInstalledStatus> installedStatus); +#endif + + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus Status(); + winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::PackageInstallerInstalledStatus> PackageInstalledStatus(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus m_status = winrt::Microsoft::Management::Deployment::CheckInstalledStatusResultStatus::Ok; + Windows::Foundation::Collections::IVector<winrt::Microsoft::Management::Deployment::PackageInstallerInstalledStatus> m_installedStatus{ + winrt::single_threaded_vector<winrt::Microsoft::Management::Deployment::PackageInstallerInstalledStatus>() }; +#endif + }; +} diff --git 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 @@ -18,6 +18,8 @@ namespace winrt::Microsoft::Management::Deployment::implementation std::optional<::AppInstaller::Utility::Architecture> GetUtilityArchitecture(winrt::Windows::System::ProcessorArchitecture architecture); std::optional<winrt::Windows::System::ProcessorArchitecture> 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<TStatus, winrt::Microsoft::Management::Deployment::InstallResultStatus>) \ diff --git 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 <winrt/Windows.Foundation.Collections.h> 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<winrt::Microsoft::Management::Deployment::MatchResult> matches); #endif diff --git a/src/Microsoft.Management.Deployment/InstalledStatus.cpp 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 <wil\cppwinrt_wrl.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void InstalledStatus::Initialize(const ::AppInstaller::Repository::InstalledStatus& installedStatus) + { + m_type = static_cast<Microsoft::Management::Deployment::InstalledStatusType>(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 @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "InstalledStatus.g.h" +#include <winget/RepositorySearch.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct InstalledStatus : InstalledStatusT<InstalledStatus> + { + 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 @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. #include "pch.h" -#include <mutex> #include <winget/RepositorySource.h> #include "MatchResult.h" #include "MatchResult.g.cpp" diff --git 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> { 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 @@ -137,18 +137,22 @@ </ItemDefinitionGroup> <ItemGroup> <ClInclude Include="CatalogPackage.h" /> + <ClInclude Include="CheckInstalledStatusResult.h" /> <ClInclude Include="ConnectResult.h" /> <ClInclude Include="Converters.h" /> <ClInclude Include="CreateCompositePackageCatalogOptions.h" /> <ClInclude Include="FindPackagesOptions.h" /> <ClInclude Include="FindPackagesResult.h" /> <ClInclude Include="Helpers.h" /> + <ClInclude Include="InstalledStatus.h" /> <ClInclude Include="InstallOptions.h" /> <ClInclude Include="InstallResult.h" /> <ClInclude Include="MatchResult.h" /> <ClInclude Include="PackageCatalog.h" /> <ClInclude Include="PackageCatalogInfo.h" /> <ClInclude Include="PackageCatalogReference.h" /> + <ClInclude Include="PackageInstallerInfo.h" /> + <ClInclude Include="PackageInstallerInstalledStatus.h" /> <ClInclude Include="PackageManager.h" /> <ClInclude Include="PackageManagerSettings.h" /> <ClInclude Include="PackageMatchFilter.h" /> @@ -161,6 +165,7 @@ </ItemGroup> <ItemGroup> <ClCompile Include="CatalogPackage.cpp" /> + <ClCompile Include="CheckInstalledStatusResult.cpp" /> <ClCompile Include="ComClsids.cpp" /> <ClCompile Include="ConnectResult.cpp" /> <ClCompile Include="Converters.cpp" /> @@ -168,12 +173,15 @@ <ClCompile Include="FindPackagesOptions.cpp" /> <ClCompile Include="FindPackagesResult.cpp" /> <ClCompile Include="Helpers.cpp" /> + <ClCompile Include="InstalledStatus.cpp" /> <ClCompile Include="InstallOptions.cpp" /> <ClCompile Include="InstallResult.cpp" /> <ClCompile Include="MatchResult.cpp" /> <ClCompile Include="PackageCatalog.cpp" /> <ClCompile Include="PackageCatalogInfo.cpp" /> <ClCompile Include="PackageCatalogReference.cpp" /> + <ClCompile Include="PackageInstallerInfo.cpp" /> + <ClCompile Include="PackageInstallerInstalledStatus.cpp" /> <ClCompile Include="PackageManager.cpp" /> <ClCompile Include="PackageManagerSettings.cpp" /> <ClCompile Include="PackageMatchFilter.cpp" /> diff --git a/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj.filters b/src/Microsoft.Management.Deployment/Microsoft.Management.Deployment.vcxproj.filters @@ -24,6 +24,10 @@ <ClCompile Include="UninstallResult.cpp" /> <ClCompile Include="ComClsids.cpp" /> <ClCompile Include="PackageManagerSettings.cpp" /> + <ClCompile Include="PackageInstallerInfo.cpp" /> + <ClCompile Include="InstalledStatus.cpp" /> + <ClCompile Include="PackageInstallerInstalledStatus.cpp" /> + <ClCompile Include="CheckInstalledStatusResult.cpp" /> </ItemGroup> <ItemGroup> <ClInclude Include="CatalogPackage.h" /> @@ -50,6 +54,10 @@ <Filter>Public</Filter> </ClInclude> <ClInclude Include="PackageManagerSettings.h" /> + <ClInclude Include="PackageInstallerInfo.h" /> + <ClInclude Include="InstalledStatus.h" /> + <ClInclude Include="PackageInstallerInstalledStatus.h" /> + <ClInclude Include="CheckInstalledStatusResult.h" /> </ItemGroup> <ItemGroup> <Midl Include="PackageManager.idl" /> diff --git 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<Microsoft::Management::Deployment::MatchResult> matches{ winrt::single_threaded_vector<Microsoft::Management::Deployment::MatchResult>() }; ::AppInstaller::Repository::SearchRequest searchRequest; diff --git 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 <winget/RepositorySource.h> #include "PackageCatalogInfo.h" #include "PackageCatalogInfo.g.cpp" #include <wil\cppwinrt_wrl.h> diff --git 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 <winget/RepositorySource.h> namespace winrt::Microsoft::Management::Deployment::implementation { diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInfo.cpp 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 <wil\cppwinrt_wrl.h> + +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.BaseInstallerType); + } + 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 @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once +#include "PackageInstallerInfo.g.h" +#include <winget/ManifestInstaller.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageInstallerInfo : PackageInstallerInfoT<PackageInstallerInfo> + { + 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 @@ -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 <wil\cppwinrt_wrl.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + void PackageInstallerInstalledStatus::Initialize(const ::AppInstaller::Repository::InstallerInstalledStatus& installerInstalledStatus) + { + // Initialize m_installerInfo + auto installerInfo = winrt::make_self<wil::details::module_count_wrapper< + winrt::Microsoft::Management::Deployment::implementation::PackageInstallerInfo>>(); + installerInfo->Initialize(installerInstalledStatus.Installer); + m_installerInfo = *installerInfo; + + // Initialize m_installedStatus + for (auto const& entry : installerInstalledStatus.Status) + { + auto status = winrt::make_self<wil::details::module_count_wrapper< + winrt::Microsoft::Management::Deployment::implementation::InstalledStatus>>(); + status->Initialize(entry); + + m_installedStatus.Append(*status); + } + } + winrt::Microsoft::Management::Deployment::PackageInstallerInfo PackageInstallerInstalledStatus::InstallerInfo() + { + return m_installerInfo; + } + winrt::Windows::Foundation::Collections::IVectorView<winrt::Microsoft::Management::Deployment::InstalledStatus> PackageInstallerInstalledStatus::InstallerInstalledStatus() + { + return m_installedStatus.GetView(); + } +} diff --git a/src/Microsoft.Management.Deployment/PackageInstallerInstalledStatus.h 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 <winget/RepositorySearch.h> +#include <winrt/Windows.Foundation.Collections.h> + +namespace winrt::Microsoft::Management::Deployment::implementation +{ + struct PackageInstallerInstalledStatus : PackageInstallerInstalledStatusT<PackageInstallerInstalledStatus> + { + 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<winrt::Microsoft::Management::Deployment::InstalledStatus> InstallerInstalledStatus(); + +#if !defined(INCLUDE_ONLY_INTERFACE_METHODS) + private: + winrt::Microsoft::Management::Deployment::PackageInstallerInfo m_installerInfo{ nullptr }; + winrt::Windows::Foundation::Collections::IVector<winrt::Microsoft::Management::Deployment::InstalledStatus> m_installedStatus{ + winrt::single_threaded_vector<winrt::Microsoft::Management::Deployment::InstalledStatus>() }; +#endif + }; +} diff --git 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,130 @@ 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. The values need to match InstalledStatusType from winget/RepositorySearch.h. + [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, + + /// 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. + [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> InstallerInstalledStatus { 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 CheckInstalledStatusResult + { + /// Status of the check installed status call. + CheckInstalledStatusResultStatus Status { get; }; + + /// A list of package installer installed status. + Windows.Foundation.Collections.IVectorView<PackageInstallerInstalledStatus> PackageInstalledStatus { 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 +430,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<PackageVersionId> AvailableVersions { get; }; @@ -320,6 +444,17 @@ namespace Microsoft.Management.Deployment /// Gets a value indicating whether an available version is newer than the installed version. Boolean IsUpdateAvailable { get; }; + [contract(Microsoft.Management.Deployment.WindowsPackageManagerContract, 5)] + { + /// 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<CheckInstalledStatusResult> CheckInstalledStatusAsync(InstalledStatusType checkTypes); + CheckInstalledStatusResult CheckInstalledStatus(InstalledStatusType checkTypes); + Windows.Foundation.IAsyncOperation<CheckInstalledStatusResult> CheckInstalledStatusAsync(); + CheckInstalledStatusResult 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. @@ -753,5 +888,13 @@ namespace Microsoft.Management.Deployment interface Windows.Foundation.Collections.IVectorView<PackageVersionId>; interface Windows.Foundation.Collections.IVector<PackageVersionInfo>; interface Windows.Foundation.Collections.IVectorView<PackageVersionInfo>; + interface Windows.Foundation.Collections.IVector<PackageInstallerInfo>; + interface Windows.Foundation.Collections.IVectorView<PackageInstallerInfo>; + interface Windows.Foundation.Collections.IVector<InstalledStatus>; + interface Windows.Foundation.Collections.IVectorView<InstalledStatus>; + interface Windows.Foundation.Collections.IVector<PackageInstallerInstalledStatus>; + interface Windows.Foundation.Collections.IVectorView<PackageInstallerInstalledStatus>; + interface Windows.Foundation.Collections.IVector<CheckInstalledStatusResult>; + interface Windows.Foundation.Collections.IVectorView<CheckInstalledStatusResult>; } } diff --git 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<IStream> xmlStream = nullptr; - xmlStream.Attach(SHCreateMemStream(reinterpret_cast<const BYTE*>(xmlStringValue.data()), strlen(xmlStringValue.data()) * sizeof(CHAR))); + xmlStream.Attach(SHCreateMemStream(reinterpret_cast<const BYTE*>(xmlStringValue.data()), static_cast<UINT>(strlen(xmlStringValue.data()) * sizeof(CHAR)))); RETURN_HR_IF_NULL(E_OUTOFMEMORY, xmlStream); ComPtr<IXmlReaderInput> xmlReaderInput; RETURN_IF_FAILED(CreateXmlReaderInputWithEncodingName(xmlStream.Get(), nullptr, L"utf-8", FALSE, nullptr, &xmlReaderInput)); diff --git a/src/WinGetUtil/Exports.cpp b/src/WinGetUtil/Exports.cpp @@ -299,6 +299,11 @@ extern "C" catch (const ManifestException& e) { *succeeded = e.IsWarningOnly(); + if (*succeeded) + { + std::unique_ptr<Manifest> result = std::make_unique<Manifest>(YamlParser::CreateFromPath(inputPath)); + *manifest = static_cast<WINGET_MANIFEST_HANDLE>(result.release()); + } if (message) { *message = ::SysAllocString(ConvertToUTF16(e.GetManifestErrorMessage()).c_str());