From f6ca17b8bb0057952711b61a23684ca02df22138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=AD=20Climent?= Date: Mon, 25 Mar 2024 15:38:19 +0100 Subject: [PATCH] Add code --- Demo Console App/Demo Console App.csproj | 14 +++ Demo Console App/Program.cs | 61 +++++++++ Solution.sln | 61 +++++++++ .../WinGetConfigurationException.cs | 39 ++++++ .../NativeMethods.txt | 7 ++ .../WindowsPackageManager Interop.csproj | 67 ++++++++++ .../WindowsPackageManager/ClassModel.cs | 50 ++++++++ .../ClassesDefinition.cs | 118 ++++++++++++++++++ .../WindowsPackageManager/ClsidContext.cs | 13 ++ .../WindowsPackageManagerElevatedFactory.cs | 60 +++++++++ .../WindowsPackageManagerFactory.cs | 58 +++++++++ .../WindowsPackageManagerStandardFactory.cs | 39 ++++++ 12 files changed, 587 insertions(+) create mode 100644 Demo Console App/Demo Console App.csproj create mode 100644 Demo Console App/Program.cs create mode 100644 Solution.sln create mode 100644 WindowsPackageManager Interop/Exceptions/WinGetConfigurationException.cs create mode 100644 WindowsPackageManager Interop/NativeMethods.txt create mode 100644 WindowsPackageManager Interop/WindowsPackageManager Interop.csproj create mode 100644 WindowsPackageManager Interop/WindowsPackageManager/ClassModel.cs create mode 100644 WindowsPackageManager Interop/WindowsPackageManager/ClassesDefinition.cs create mode 100644 WindowsPackageManager Interop/WindowsPackageManager/ClsidContext.cs create mode 100644 WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerElevatedFactory.cs create mode 100644 WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerFactory.cs create mode 100644 WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerStandardFactory.cs diff --git a/Demo Console App/Demo Console App.csproj b/Demo Console App/Demo Console App.csproj new file mode 100644 index 0000000..99f3cd5 --- /dev/null +++ b/Demo Console App/Demo Console App.csproj @@ -0,0 +1,14 @@ + + + + Exe + net8.0-windows10.0.19041.0 + enable + enable + + + + + + + diff --git a/Demo Console App/Program.cs b/Demo Console App/Program.cs new file mode 100644 index 0000000..088b740 --- /dev/null +++ b/Demo Console App/Program.cs @@ -0,0 +1,61 @@ +using System.Security.Principal; + +// Include WinGet Namespace +using WindowsPackageManager.Interop; + +namespace WingetTest +{ + internal class Program + { + static public void Main(string[] args) + { + while(true) + { + Console.WriteLine(" "); + Console.Write("Enter search query: "); + string? Query = Console.ReadLine(); + if(Query == null || Query == "") + break; + + FindPackagesForQuery(Query).Wait(); + } + } + + private static async Task FindPackagesForQuery(string Query) + { + WindowsPackageManagerFactory WinGetFactory; + bool IsAdministrator = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); + + // If the user is an administrator, use the elevated factory. Otherwhise COM will crash + if(IsAdministrator) + WinGetFactory = new WindowsPackageManagerElevatedFactory(); + else + WinGetFactory = new WindowsPackageManagerStandardFactory(); + + // Create Package Manager and get available catalogs + var Manager = WinGetFactory.CreatePackageManager(); + var AvailableCatalogs = Manager.GetPackageCatalogs(); + + foreach (var Catalog in AvailableCatalogs.ToArray()) + { + // Create a filter to search for packages + var FilterList = WinGetFactory.CreateFindPackagesOptions(); + + // Add the query to the filter + var NameFilter = WinGetFactory.CreatePackageMatchFilter(); + NameFilter.Field = Microsoft.Management.Deployment.PackageMatchField.Name; + NameFilter.Value = Query; + FilterList.Filters.Add(NameFilter); + + // Find the packages with the filters + var SearchResults = await Catalog.Connect().PackageCatalog.FindPackagesAsync(FilterList); + foreach (var Match in SearchResults.Matches.ToArray()) + { + // Print the packages + var Package = Match.CatalogPackage; + Console.WriteLine(Package.Name); + } + } + } + } +} diff --git a/Solution.sln b/Solution.sln new file mode 100644 index 0000000..6cfe7dd --- /dev/null +++ b/Solution.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.10.34707.107 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsPackageManager Interop", "WindowsPackageManager Interop\WindowsPackageManager Interop.csproj", "{39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Demo Console App", "Demo Console App\Demo Console App.csproj", "{D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|arm64 = Debug|arm64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|arm64 = Release|arm64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|Any CPU.ActiveCfg = Debug|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|Any CPU.Build.0 = Debug|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|arm64.ActiveCfg = Debug|arm64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|arm64.Build.0 = Debug|arm64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|x64.ActiveCfg = Debug|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|x64.Build.0 = Debug|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|x86.ActiveCfg = Debug|x86 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Debug|x86.Build.0 = Debug|x86 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|Any CPU.ActiveCfg = Release|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|Any CPU.Build.0 = Release|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|arm64.ActiveCfg = Release|arm64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|arm64.Build.0 = Release|arm64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|x64.ActiveCfg = Release|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|x64.Build.0 = Release|x64 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|x86.ActiveCfg = Release|x86 + {39D02ACA-38AC-41B4-8ED4-DC591AAC5B1F}.Release|x86.Build.0 = Release|x86 + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|arm64.ActiveCfg = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|arm64.Build.0 = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|x64.ActiveCfg = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|x64.Build.0 = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|x86.ActiveCfg = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Debug|x86.Build.0 = Debug|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|Any CPU.Build.0 = Release|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|arm64.ActiveCfg = Release|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|arm64.Build.0 = Release|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|x64.ActiveCfg = Release|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|x64.Build.0 = Release|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|x86.ActiveCfg = Release|Any CPU + {D30DB5C6-8D6E-4EA9-99C5-C3B495C0109C}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E855C1C8-5A5C-45A1-BBD0-5253A0E190F2} + EndGlobalSection +EndGlobal diff --git a/WindowsPackageManager Interop/Exceptions/WinGetConfigurationException.cs b/WindowsPackageManager Interop/Exceptions/WinGetConfigurationException.cs new file mode 100644 index 0000000..88c5408 --- /dev/null +++ b/WindowsPackageManager Interop/Exceptions/WinGetConfigurationException.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace WindowsPackageManager.Interop; + +public class WinGetConfigurationException : Exception +{ + // WinGet Configuration error codes: + // https://github.com/microsoft/winget-cli/blob/master/src/PowerShell/Microsoft.WinGet.Configuration.Engine/Exceptions/ErrorCodes.cs + public const int WingetConfigErrorInvalidConfigurationFile = unchecked((int)0x8A15C001); + public const int WingetConfigErrorInvalidYaml = unchecked((int)0x8A15C002); + public const int WingetConfigErrorInvalidFieldType = unchecked((int)0x8A15C003); + public const int WingetConfigErrorUnknownConfigurationFileVersion = unchecked((int)0x8A15C004); + public const int WingetConfigErrorSetApplyFailed = unchecked((int)0x8A15C005); + public const int WingetConfigErrorDuplicateIdentifier = unchecked((int)0x8A15C006); + public const int WingetConfigErrorMissingDependency = unchecked((int)0x8A15C007); + public const int WingetConfigErrorDependencyUnsatisfied = unchecked((int)0x8A15C008); + public const int WingetConfigErrorAssertionFailed = unchecked((int)0x8A15C009); + public const int WingetConfigErrorManuallySkipped = unchecked((int)0x8A15C00A); + public const int WingetConfigErrorWarningNotAccepted = unchecked((int)0x8A15C00B); + public const int WingetConfigErrorSetDependencyCycle = unchecked((int)0x8A15C00C); + public const int WingetConfigErrorInvalidFieldValue = unchecked((int)0x8A15C00D); + public const int WingetConfigErrorMissingField = unchecked((int)0x8A15C00E); + + // WinGet Configuration unit error codes: + public const int WinGetConfigUnitNotFound = unchecked((int)0x8A15C101); + public const int WinGetConfigUnitNotFoundRepository = unchecked((int)0x8A15C102); + public const int WinGetConfigUnitMultipleMatches = unchecked((int)0x8A15C103); + public const int WinGetConfigUnitInvokeGet = unchecked((int)0x8A15C104); + public const int WinGetConfigUnitInvokeTest = unchecked((int)0x8A15C105); + public const int WinGetConfigUnitInvokeSet = unchecked((int)0x8A15C106); + public const int WinGetConfigUnitModuleConflict = unchecked((int)0x8A15C107); + public const int WinGetConfigUnitImportModule = unchecked((int)0x8A15C108); + public const int WinGetConfigUnitInvokeInvalidResult = unchecked((int)0x8A15C109); + public const int WinGetConfigUnitSettingConfigRoot = unchecked((int)0x8A15C110); + public const int WinGetConfigUnitImportModuleAdmin = unchecked((int)0x8A15C111); +} diff --git a/WindowsPackageManager Interop/NativeMethods.txt b/WindowsPackageManager Interop/NativeMethods.txt new file mode 100644 index 0000000..a7650f2 --- /dev/null +++ b/WindowsPackageManager Interop/NativeMethods.txt @@ -0,0 +1,7 @@ +CoCreateInstance +CoMarshalInterface +CoUnmarshalInterface +CreateStreamOnHGlobal +HRESULT_FROM_WIN32 +MSHCTX +MSHLFLAGS diff --git a/WindowsPackageManager Interop/WindowsPackageManager Interop.csproj b/WindowsPackageManager Interop/WindowsPackageManager Interop.csproj new file mode 100644 index 0000000..72791c1 --- /dev/null +++ b/WindowsPackageManager Interop/WindowsPackageManager Interop.csproj @@ -0,0 +1,67 @@ + + + + net8.0-windows10.0.19041.0 + 10.0.19041.0 + 10.0.19041.0 + DevHome.SetupFlow.Common + x86;x64;arm64 + win-x86;win-x64;win-arm64 + disable + + + + + 10.0.19041.0 + Microsoft.Management.Deployment + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + NU1701 + true + none + + + True + + + + + + + + + + diff --git a/WindowsPackageManager Interop/WindowsPackageManager/ClassModel.cs b/WindowsPackageManager Interop/WindowsPackageManager/ClassModel.cs new file mode 100644 index 0000000..c7d8a3b --- /dev/null +++ b/WindowsPackageManager Interop/WindowsPackageManager/ClassModel.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace WindowsPackageManager.Interop; + +internal sealed class ClassModel +{ + /// + /// Gets the interface for the projected class type generated by CsWinRT + /// + public Type InterfaceType { get; init; } + + /// + /// Gets the projected class type generated by CsWinRT + /// + public Type ProjectedClassType { get; init; } + + /// + /// Gets the clsids for each context (e.g. OutOfProcProd, OutOfProcDev) + /// + public IReadOnlyDictionary Clsids { get; init; } + + /// + /// Get CLSID based on the provided context + /// + /// Context + /// CLSID for the provided context. + /// Throw an exception if the clsid context is not available for the current instance. + public Guid GetClsid(ClsidContext context) + { + if (!Clsids.TryGetValue(context, out var clsid)) + { + throw new InvalidOperationException($"{ProjectedClassType.FullName} is not implemented in context {context}"); + } + + return clsid; + } + + /// + /// Get IID corresponding to the COM object + /// + /// IID. + public Guid GetIid() + { + return InterfaceType.GUID; + } +} diff --git a/WindowsPackageManager Interop/WindowsPackageManager/ClassesDefinition.cs b/WindowsPackageManager Interop/WindowsPackageManager/ClassesDefinition.cs new file mode 100644 index 0000000..0af9b92 --- /dev/null +++ b/WindowsPackageManager Interop/WindowsPackageManager/ClassesDefinition.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Microsoft.Management.Deployment; + +namespace WindowsPackageManager.Interop; + +internal static class ClassesDefinition +{ + private static Dictionary Classes { get; } = new() + { + [typeof(PackageManager)] = new() + { + ProjectedClassType = typeof(PackageManager), + InterfaceType = typeof(IPackageManager), + Clsids = new Dictionary() + { + [ClsidContext.Prod] = new Guid("C53A4F16-787E-42A4-B304-29EFFB4BF597"), + [ClsidContext.Dev] = new Guid("74CB3139-B7C5-4B9E-9388-E6616DEA288C"), + }, + }, + + [typeof(FindPackagesOptions)] = new() + { + ProjectedClassType = typeof(FindPackagesOptions), + InterfaceType = typeof(IFindPackagesOptions), + Clsids = new Dictionary() + { + [ClsidContext.Prod] = new Guid("572DED96-9C60-4526-8F92-EE7D91D38C1A"), + [ClsidContext.Dev] = new Guid("1BD8FF3A-EC50-4F69-AEEE-DF4C9D3BAA96"), + }, + }, + + [typeof(CreateCompositePackageCatalogOptions)] = new() + { + ProjectedClassType = typeof(CreateCompositePackageCatalogOptions), + InterfaceType = typeof(ICreateCompositePackageCatalogOptions), + Clsids = new Dictionary() + { + [ClsidContext.Prod] = new Guid("526534B8-7E46-47C8-8416-B1685C327D37"), + [ClsidContext.Dev] = new Guid("EE160901-B317-4EA7-9CC6-5355C6D7D8A7"), + }, + }, + + [typeof(InstallOptions)] = new() + { + ProjectedClassType = typeof(InstallOptions), + InterfaceType = typeof(IInstallOptions), + Clsids = new Dictionary() + { + [ClsidContext.Prod] = new Guid("1095F097-EB96-453B-B4E6-1613637F3B14"), + [ClsidContext.Dev] = new Guid("44FE0580-62F7-44D4-9E91-AA9614AB3E86"), + }, + }, + + [typeof(UninstallOptions)] = new() + { + ProjectedClassType = typeof(UninstallOptions), + InterfaceType = typeof(IUninstallOptions), + Clsids = new Dictionary() + { + [ClsidContext.Prod] = new Guid("E1D9A11E-9F85-4D87-9C17-2B93143ADB8D"), + [ClsidContext.Dev] = new Guid("AA2A5C04-1AD9-46C4-B74F-6B334AD7EB8C"), + }, + }, + + [typeof(PackageMatchFilter)] = new() + { + ProjectedClassType = typeof(PackageMatchFilter), + InterfaceType = typeof(IPackageMatchFilter), + Clsids = new Dictionary() + { + [ClsidContext.Prod] = new Guid("D02C9DAF-99DC-429C-B503-4E504E4AB000"), + [ClsidContext.Dev] = new Guid("3F85B9F4-487A-4C48-9035-2903F8A6D9E8"), + }, + }, + }; + + /// + /// Get CLSID based on the provided context for the specified type + /// + /// Projected class type + /// Context + /// CLSID for the provided context and type, or throw an exception if not found. + /// Throws an exception if type is not a project class. + public static Guid GetClsid(ClsidContext context) + { + ValidateType(); + return Classes[typeof(T)].GetClsid(context); + } + + /// + /// Get IID corresponding to the COM object + /// + /// Projected class type + /// IID or throw an exception if not found. + /// Throws an exception if type is not a project class. + public static Guid GetIid() + { + ValidateType(); + return Classes[typeof(T)].GetIid(); + } + + /// + /// Validate that the provided type is defined. + /// + /// Projected class type + /// Throws an exception if type is not a project class. + private static void ValidateType() + { + if (!Classes.ContainsKey(typeof(TType))) + { + throw new InvalidOperationException($"{typeof(TType).Name} is not a projected class type."); + } + } +} diff --git a/WindowsPackageManager Interop/WindowsPackageManager/ClsidContext.cs b/WindowsPackageManager Interop/WindowsPackageManager/ClsidContext.cs new file mode 100644 index 0000000..93425c1 --- /dev/null +++ b/WindowsPackageManager Interop/WindowsPackageManager/ClsidContext.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace WindowsPackageManager.Interop; + +public enum ClsidContext +{ + // Production CLSID Guids + Prod, + + // Development CLSID Guids + Dev, +} diff --git a/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerElevatedFactory.cs b/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerElevatedFactory.cs new file mode 100644 index 0000000..14346cd --- /dev/null +++ b/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerElevatedFactory.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using WinRT; + +namespace WindowsPackageManager.Interop; + +/// +/// Factory for creating winget COM objects using manual activation +/// to have them in an elevated context. +/// +/// +/// This needs to be called from an elevated context, or the winget +/// server will reject the connection. +/// +/// The WinGetServerManualActivation_CreateInstance function used here is defined in +/// https://github.com/microsoft/winget-cli/blob/master/src/WinGetServer/WinGetServerManualActivation_Client.cpp +/// +/// This class is based on what the winget cmdlets do. See +/// https://github.com/microsoft/winget-cli/blob/master/src/PowerShell/Microsoft.WinGet.Client/Helpers/ComObjectFactory.cs +/// +public class WindowsPackageManagerElevatedFactory : WindowsPackageManagerFactory +{ + // The only CLSID context supported by the DLL we call is Prod. + // If we want to use Dev classes we have to use a Dev version of the DLL. + public WindowsPackageManagerElevatedFactory() + : base(ClsidContext.Prod) + { + } + + protected override unsafe T CreateInstance(Guid clsid, Guid iid) + { + void* pUnknown = null; + + try + { + var hr = WinGetServerManualActivation_CreateInstance(in clsid, in iid, 0, out pUnknown); + Marshal.ThrowExceptionForHR(hr); + return MarshalInterface.FromAbi((IntPtr)pUnknown); + } + finally + { + // CoCreateInstance and FromAbi both AddRef on the native object. + // Release once to prevent memory leak. + if (pUnknown is not null) + { + Marshal.Release((IntPtr)pUnknown); + } + } + } + + [DllImport("winrtact.dll", ExactSpelling = true)] + private static unsafe extern int WinGetServerManualActivation_CreateInstance( + in Guid clsid, + in Guid iid, + uint flags, + out void* instance); +} diff --git a/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerFactory.cs b/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerFactory.cs new file mode 100644 index 0000000..4c4e3e4 --- /dev/null +++ b/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerFactory.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using Microsoft.Management.Deployment; + +namespace WindowsPackageManager.Interop; + +/// +/// Factory class for creating WinGet COM objects. +/// Details about each method can be found in the source IDL: +/// https://github.com/microsoft/winget-cli/blob/master/src/Microsoft.Management.Deployment/PackageManager.idl +/// +public abstract class WindowsPackageManagerFactory +{ + private readonly ClsidContext _clsidContext; + + public WindowsPackageManagerFactory(ClsidContext clsidContext) + { + _clsidContext = clsidContext; + } + + /// + /// Creates an instance of the class . + /// + /// + /// Type must be one of the types defined in the winget COM API. + /// Implementations of this method can assume that and + /// are the right GUIDs for the class in the given context. + /// + protected abstract T CreateInstance(Guid clsid, Guid iid); + + public PackageManager CreatePackageManager() => CreateInstance(); + + public FindPackagesOptions CreateFindPackagesOptions() => CreateInstance(); + + public CreateCompositePackageCatalogOptions CreateCreateCompositePackageCatalogOptions() => CreateInstance(); + + public InstallOptions CreateInstallOptions() => CreateInstance(); + + public UninstallOptions CreateUninstallOptions() => CreateInstance(); + + public PackageMatchFilter CreatePackageMatchFilter() => CreateInstance(); + + /// + /// Creates an instance of the class . + /// + /// + /// This is a helper for calling the derived class's + /// method with the appropriate GUIDs. + /// + private T CreateInstance() + { + var clsid = ClassesDefinition.GetClsid(_clsidContext); + var iid = ClassesDefinition.GetIid(); + return CreateInstance(clsid, iid); + } +} diff --git a/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerStandardFactory.cs b/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerStandardFactory.cs new file mode 100644 index 0000000..d9efacd --- /dev/null +++ b/WindowsPackageManager Interop/WindowsPackageManager/WindowsPackageManagerStandardFactory.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using Windows.Win32; +using Windows.Win32.System.Com; +using WinRT; + +namespace WindowsPackageManager.Interop; + +public class WindowsPackageManagerStandardFactory : WindowsPackageManagerFactory +{ + public WindowsPackageManagerStandardFactory(ClsidContext clsidContext = ClsidContext.Prod) + : base(clsidContext) + { + } + + protected override T CreateInstance(Guid clsid, Guid iid) + { + var pUnknown = IntPtr.Zero; + try + { + var hr = PInvoke.CoCreateInstance(clsid, null, CLSCTX.CLSCTX_LOCAL_SERVER, iid, out var result); + Marshal.ThrowExceptionForHR(hr); + pUnknown = Marshal.GetIUnknownForObject(result); + return MarshalGeneric.FromAbi(pUnknown); + } + finally + { + // CoCreateInstance and FromAbi both AddRef on the native object. + // Release once to prevent memory leak. + if (pUnknown != IntPtr.Zero) + { + Marshal.Release(pUnknown); + } + } + } +}