/ src / modules / cmdpal / ext / Microsoft.CmdPal.Ext.WinGet / WinGetStatics.cs
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  }