WinGetStatics.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System; 6 using System.Collections.Generic; 7 using System.Diagnostics; 8 using System.Globalization; 9 using System.Linq; 10 using System.Text; 11 using System.Threading.Tasks; 12 using Microsoft.CommandPalette.Extensions; 13 using Microsoft.CommandPalette.Extensions.Toolkit; 14 using Microsoft.Management.Deployment; 15 using Windows.Foundation.Metadata; 16 using WindowsPackageManager.Interop; 17 18 namespace Microsoft.CmdPal.Ext.WinGet; 19 20 internal static class WinGetStatics 21 { 22 public static WindowsPackageManagerStandardFactory WinGetFactory { get; private set; } 23 24 public static PackageManager Manager { get; private set; } 25 26 public static IReadOnlyList<PackageCatalogReference> AvailableCatalogs { get; private set; } 27 28 private static readonly PackageCatalogReference _wingetCatalog; 29 private static readonly PackageCatalogReference _storeCatalog; 30 31 public static Lazy<Task<PackageCatalog>> CompositeAllCatalog { get; } = new(() => GetCompositeCatalog(true)); 32 33 public static Lazy<Task<PackageCatalog>> CompositeWingetCatalog { get; } = new(() => GetCompositeCatalog(false)); 34 35 private static readonly StatusMessage _errorMessage = new() { State = MessageState.Error }; 36 37 public static Func<string, ICommandItem?>? AppSearchByPackageFamilyNameCallback { get; set; } 38 39 public static Func<string, ICommandItem?>? AppSearchByProductCodeCallback { get; set; } 40 41 private static readonly CompositeFormat CreateCatalogErrorMessage = System.Text.CompositeFormat.Parse(Properties.Resources.winget_create_catalog_error); 42 43 static WinGetStatics() 44 { 45 WinGetFactory = new WindowsPackageManagerStandardFactory(); 46 47 // Create Package Manager and get available catalogs 48 Manager = WinGetFactory.CreatePackageManager(); 49 50 _wingetCatalog = Manager.GetPredefinedPackageCatalog(PredefinedPackageCatalog.OpenWindowsCatalog); 51 _storeCatalog = Manager.GetPredefinedPackageCatalog(PredefinedPackageCatalog.MicrosoftStore); 52 AvailableCatalogs = [ 53 _wingetCatalog, 54 _storeCatalog, 55 ]; 56 57 if (ApiInformation.IsApiContractPresent("Microsoft.Management.Deployment.WindowsPackageManagerContract", 8)) 58 { 59 foreach (var catalogReference in AvailableCatalogs) 60 { 61 catalogReference.PackageCatalogBackgroundUpdateInterval = new(0); 62 } 63 } 64 65 // Immediately start the lazy-init of the all packages catalog, but 66 // leave the winget one to be initialized as needed 67 _ = Task.Run(() => 68 { 69 _ = CompositeAllCatalog.Value; 70 71 // _ = CompositeWingetCatalog.Value; 72 }); 73 } 74 75 internal static async Task<PackageCatalog> GetCompositeCatalog(bool all) 76 { 77 Stopwatch stopwatch = new(); 78 Debug.WriteLine($"Starting GetCompositeCatalog({all}) fetch"); 79 stopwatch.Start(); 80 81 // Create the composite catalog 82 var createCompositePackageCatalogOptions = WinGetFactory.CreateCreateCompositePackageCatalogOptions(); 83 84 if (all) 85 { 86 // Add winget and the store to this catalog 87 foreach (var catalogReference in AvailableCatalogs.ToArray()) 88 { 89 createCompositePackageCatalogOptions.Catalogs.Add(catalogReference); 90 } 91 } 92 else 93 { 94 createCompositePackageCatalogOptions.Catalogs.Add(_wingetCatalog); 95 } 96 97 // Searches only the catalogs provided, but will correlated with installed items 98 createCompositePackageCatalogOptions.CompositeSearchBehavior = CompositeSearchBehavior.RemotePackagesFromAllCatalogs; 99 100 var catalogRef = WinGetStatics.Manager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions); 101 102 var connectResult = await catalogRef.ConnectAsync(); 103 var compositeCatalog = connectResult.PackageCatalog; 104 105 stopwatch.Stop(); 106 Debug.WriteLine($"GetCompositeCatalog({all}) fetch took {stopwatch.ElapsedMilliseconds}ms"); 107 108 if (connectResult.Status == ConnectResultStatus.CatalogError) 109 { 110 _errorMessage.Message = string.Format(CultureInfo.CurrentCulture, CreateCatalogErrorMessage, connectResult.ExtendedErrorCode.HResult); 111 WinGetExtensionHost.Instance.ShowStatus(_errorMessage, StatusContext.Extension); 112 } 113 114 return compositeCatalog; 115 } 116 }