PackageManagerWrapper.cs (7335B)
1 // ----------------------------------------------------------------------------- 2 // <copyright file="PackageManagerWrapper.cs" company="Microsoft Corporation"> 3 // Copyright (c) Microsoft Corporation. Licensed under the MIT License. 4 // </copyright> 5 // ----------------------------------------------------------------------------- 6 7 namespace Microsoft.WinGet.Client.Engine.Helpers 8 { 9 using System; 10 using System.Collections.Generic; 11 using System.Runtime.InteropServices; 12 using Microsoft.Management.Deployment; 13 using Microsoft.WinGet.Client.Engine.Common; 14 using Microsoft.WinGet.Client.Engine.Exceptions; 15 using Windows.Foundation; 16 17 /// <summary> 18 /// Wrapper for PackageManager that handles rpc disconnections. 19 /// The object is disconnected when the server is killed or on an update. 20 /// </summary> 21 internal sealed class PackageManagerWrapper 22 { 23 private static readonly Lazy<PackageManagerWrapper> Lazy = new (() => new PackageManagerWrapper()); 24 25 private PackageManager packageManager = null!; 26 27 private PackageManagerWrapper() 28 { 29 } 30 31 /// <summary> 32 /// Gets the instance object. 33 /// </summary> 34 public static PackageManagerWrapper Instance 35 { 36 get { return Lazy.Value; } 37 } 38 39 /// <summary> 40 /// Wrapper for InstallPackageAsync. 41 /// </summary> 42 /// <param name="package">The package to install.</param> 43 /// <param name="options">The install options.</param> 44 /// <returns>An async operation with progress.</returns> 45 public IAsyncOperationWithProgress<InstallResult, InstallProgress> InstallPackageAsync(CatalogPackage package, InstallOptions options) 46 { 47 return this.Execute( 48 () => this.packageManager.InstallPackageAsync(package, options), 49 false); 50 } 51 52 /// <summary> 53 /// Wrapper for UpgradePackageAsync. 54 /// </summary> 55 /// <param name="package">The package to upgrade.</param> 56 /// <param name="options">The install options.</param> 57 /// <returns>An async operation with progress.</returns> 58 public IAsyncOperationWithProgress<InstallResult, InstallProgress> UpgradePackageAsync(CatalogPackage package, InstallOptions options) 59 { 60 return this.Execute( 61 () => this.packageManager.UpgradePackageAsync(package, options), 62 false); 63 } 64 65 /// <summary> 66 /// Wrapper for UninstallPackageAsync. 67 /// </summary> 68 /// <param name="package">The package to uninstall.</param> 69 /// <param name="options">The uninstall options.</param> 70 /// <returns>An async operation with progress.</returns> 71 public IAsyncOperationWithProgress<UninstallResult, UninstallProgress> UninstallPackageAsync(CatalogPackage package, UninstallOptions options) 72 { 73 return this.Execute( 74 () => this.packageManager.UninstallPackageAsync(package, options), 75 false); 76 } 77 78 /// <summary> 79 /// Wrapper for DownloadPackageAsync. 80 /// </summary> 81 /// <param name="package">The package to download.</param> 82 /// <param name="options">The download options.</param> 83 /// <returns>An async operation with progress.</returns> 84 public IAsyncOperationWithProgress<DownloadResult, PackageDownloadProgress> DownloadPackageAsync(CatalogPackage package, DownloadOptions options) 85 { 86 return this.Execute( 87 () => this.packageManager.DownloadPackageAsync(package, options), 88 false); 89 } 90 91 /// <summary> 92 /// Wrapper for RepairPackagesAsync. 93 /// </summary> 94 /// <param name="package">The package to repair.</param> 95 /// <param name="options">The repair options.</param> 96 /// <returns>An async operation with progress.</returns> 97 public IAsyncOperationWithProgress<RepairResult, RepairProgress> RepairPackageAsync(CatalogPackage package, RepairOptions options) 98 { 99 return this.Execute( 100 () => this.packageManager.RepairPackageAsync(package, options), 101 false); 102 } 103 104 /// <summary> 105 /// Wrapper for GetPackageCatalogs. 106 /// </summary> 107 /// <returns>A list of PackageCatalogReferences.</returns> 108 public IReadOnlyList<PackageCatalogReference> GetPackageCatalogs() 109 { 110 return this.Execute( 111 () => this.packageManager.GetPackageCatalogs(), 112 true); 113 } 114 115 /// <summary> 116 /// Wrapper for GetPackageCatalogByName. 117 /// </summary> 118 /// <param name="source">The name of the source.</param> 119 /// <returns>A PackageCatalogReference.</returns> 120 public PackageCatalogReference GetPackageCatalogByName(string source) 121 { 122 return this.Execute( 123 () => this.packageManager.GetPackageCatalogByName(source), 124 true); 125 } 126 127 /// <summary> 128 /// Wrapper for CreateCompositePackageCatalog. 129 /// </summary> 130 /// <param name="options">CreateCompositePackageCatalogOptions.</param> 131 /// <returns>A PackageCatalogReference.</returns> 132 public PackageCatalogReference CreateCompositePackageCatalog(CreateCompositePackageCatalogOptions options) 133 { 134 return this.Execute( 135 () => this.packageManager.CreateCompositePackageCatalog(options), 136 false); 137 } 138 139 /// <summary> 140 /// Gets the version of the package manager that is running. 141 /// </summary> 142 /// <returns>The version string.</returns> 143 public string? GetVersion() 144 { 145 try 146 { 147 return this.Execute(() => this.packageManager.Version, true); 148 } 149 catch 150 { 151 return null; 152 } 153 } 154 155 private TReturn Execute<TReturn>(Func<TReturn> func, bool canRetry) 156 { 157 if (Utilities.UsesInProcWinget && Utilities.ThreadIsSTA) 158 { 159 // If you failed here, then you didn't wrap your call in ManagementDeploymentCommand.Execute 160 throw new SingleThreadedApartmentException(); 161 } 162 163 bool stopRetry = false; 164 while (true) 165 { 166 if (this.packageManager == null) 167 { 168 this.packageManager = ManagementDeploymentFactory.Instance.CreatePackageManager(); 169 } 170 171 try 172 { 173 return func(); 174 } 175 catch (COMException ex) when (ex.HResult == ErrorCode.RpcServerUnavailable || ex.HResult == ErrorCode.RpcCallFailed) 176 { 177 this.packageManager = null!; 178 179 if (stopRetry || !canRetry) 180 { 181 throw; 182 } 183 184 stopRetry = true; 185 } 186 } 187 } 188 } 189 }