microsoft / winget-cli

WinGet is the Windows Package Manager. This project includes a CLI (Command Line Interface), PowerShell modules, and a COM (Component Object Model) API (Application Programming Interface).
https://learn.microsoft.com/windows/package-manager/
MIT License
23.08k stars 1.44k forks source link

Issues with COM API and retrieving installed packages #4320

Closed marticliment closed 3 months ago

marticliment commented 6 months ago

Brief description of your issue

I have been trying to implement native WinGet support (WinGet COM API) on a NET/C# App (WingetUI), and I am experiencing issues when fetching installed packages. More specifically, WinGet does not identify which packages are available on WinGet repositories, and all of the packages are listed as local only.

Running winget list shows the correct package sources on the right of package (nothing is shown if package is local, winget or msstore otherwhise).

Note: Packages do load and the list of installed packages is correct; and searching on remote catalogs works flawlessly.

Steps to reproduce

  1. Clone the repository https://github.com/marticliment/WinGet-API-from-CSharp and open the solution.
  2. On demo console app, replace Program.cs file contents by the following:
    
    using Microsoft.Management.Deployment;
    using System.Security.Principal;
    using Windows.ApplicationModel;

// Include WinGet Namespace using WindowsPackageManager.Interop;

namespace WingetTest { internal class Program { static public void Main(string[] args) { var WinGetFactory = new WindowsPackageManagerStandardFactory(); var WinGetManager = WinGetFactory.CreatePackageManager();

        // Tested all of them, none of them worked
        PackageCatalogReference selectedRemoteCatalogRef = WinGetManager.GetPackageCatalogByName("winget");
        // PackageCatalogReference selectedRemoteCatalogRef = WinGetManager.GetPackageCatalogByName("msstore");
        // PackageCatalogReference selectedRemoteCatalogRef = WinGetManager.GetLocalPackageCatalog(LocalPackageCatalog.InstalledPackages);
        // PackageCatalogReference selectedRemoteCatalogRef = WinGetManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog.OpenWindowsCatalog);
        // PackageCatalogReference selectedRemoteCatalogRef = WinGetManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog.MicrosoftStore);
        // PackageCatalogReference selectedRemoteCatalogRef = WinGetManager.GetPredefinedPackageCatalog(PredefinedPackageCatalog.DesktopFrameworks);

        CreateCompositePackageCatalogOptions createCompositePackageCatalogOptions = WinGetFactory.CreateCreateCompositePackageCatalogOptions();
        createCompositePackageCatalogOptions.Catalogs.Append(selectedRemoteCatalogRef);

        createCompositePackageCatalogOptions.CompositeSearchBehavior = CompositeSearchBehavior.LocalCatalogs;
        var LocalCatalogRef = WinGetManager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions);

        var ConnectResult = LocalCatalogRef.Connect();
        if (ConnectResult.Status != ConnectResultStatus.Ok)
        {
            throw new Exception("WinGet: Failed to connect to local catalog.");
        }

        FindPackagesOptions findPackagesOptions = WinGetFactory.CreateFindPackagesOptions();
        PackageMatchFilter filter = WinGetFactory.CreatePackageMatchFilter();
        filter.Field = PackageMatchField.Id;
        filter.Option = PackageFieldMatchOption.Equals;
        filter.Value = "";
        findPackagesOptions.Filters.Append(filter);

        var TaskResult = ConnectResult.PackageCatalog.FindPackages(findPackagesOptions);

        foreach (var match in TaskResult.Matches.ToArray())
        {
            if (match.CatalogPackage.DefaultInstallVersion != null)
                Console.WriteLine("Package is available Online: " + match.CatalogPackage.DefaultInstallVersion.PackageCatalog.Info.Name);
            else
                Console.WriteLine("Package is local only: " + match.CatalogPackage.Id);
        }
    }
}

}


### Expected behavior

WinGet identifying the online packages as such when loading a local composite catalog, so they have a valid `DefaultInstallVersion` and online information can be retieved (update date, origin, newer versions, etc.) 

### Actual behavior

WinGet does not detect online packages as such. IDs are not reported properly and packages do have a null `DefaultInstallVersion` property.

**Interestingly**, the same code works flawlessly on a WinRT/C++ environment (this is from SampleWinGetCaller, tested and working flawlessly):
```c++
 int32_t selectedIndex = catalogsListBox().SelectedIndex();
 co_await winrt::resume_background();

 PackageManager packageManager = CreatePackageManager();

 PackageCatalogReference installedSearchCatalogRef{ nullptr };

 // PackageCatalogReference selectedRemoteCatalogRef = packageManager.GetPackageCatalogs().GetAt(0); //msstore
 PackageCatalogReference selectedRemoteCatalogRef = packageManager.GetPackageCatalogs().GetAt(1); //winget
 CreateCompositePackageCatalogOptions createCompositePackageCatalogOptions = CreateCreateCompositePackageCatalogOptions();
 createCompositePackageCatalogOptions.Catalogs().Append(selectedRemoteCatalogRef);

 createCompositePackageCatalogOptions.CompositeSearchBehavior(CompositeSearchBehavior::LocalCatalogs);
 installedSearchCatalogRef = packageManager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions);

 ConnectResult connectResult{ co_await installedSearchCatalogRef.ConnectAsync() };
 PackageCatalog installedCatalog = connectResult.PackageCatalog();
 if (!installedCatalog)
 {
     // Connect Error.
     co_await winrt::resume_foreground(statusText.Dispatcher());
     statusText.Text(L"Failed to connect to catalog.");
     co_return;
 }

 FindPackagesOptions findPackagesOptions = CreateFindPackagesOptions();
 auto value = m_installAppId;
 FindPackagesResult findResult{ TryFindPackageInCatalogAsync(installedCatalog, m_installAppId).get() };
 auto matches = findResult.Matches();

 co_await winrt::resume_foreground(statusText.Dispatcher());
 m_installedPackages.Clear();
 for (auto const match : matches)
 {
     // Filter to only packages that match the selectedCatalogRef
     auto version = match.CatalogPackage().DefaultInstallVersion();
     if (selectedIndex < 0 || (version && version.PackageCatalog().Info().Id() == m_packageCatalogs.GetAt(selectedIndex).Info().Id()))
     {
         m_installedPackages.Append(match.CatalogPackage());
     }
 }

 statusText.Text(L"");

Environment

OS:
 - Windows 11 10.0.22635.3350

Tested .NET versions:
 - NET6.0
 - NET8.0

Tested WinGet versions:
 - 1.7.10661
 - 1.8.xxxxx-preview

Tested `Microsoft.WindowsPackageManager.ComInterop` versions:
 - 1.7.10091-preview
 - 1.5.1572
 - 1.4.10052

Microsoft.Management.Deployment.WINMD files provided on:
 - Microsoft.WindowsPackageManager.ComInterop package (all versions above)
 - The latest WinGet release (extracted from the MSIX installer)
 - The WinMD used by SampleWinGetCaller

All of the combinations above do behave as detailed above.
github-actions[bot] commented 6 months ago

Hi I'm an AI powered bot that finds similar issues based off the issue title.

Please view the issues below to see if they solve your problem, and if the issue describes your problem please consider closing this one and thumbs upping the other issue to help us prioritize it. Thank you!

Open similar issues:

Closed similar issues:

Note: You can give me feedback by thumbs upping or thumbs downing this comment.

PatrickSchmidtSE commented 3 months ago

@marticliment if you debug, you will see the problem:

its not added to the composit catalog option sadly. BUT it is because you use "append" instead of "add". Add adds the catalog to the composite options. But i tested, does not change anything. Still i get packages shown which are not from the "winget" catalog , but have a empty source. We need a working --source winget option for the COM API image

marticliment commented 3 months ago

As of version 1.8.1791, this code seems to be working. Thanks @PatrickSchmidtSE for pointing into the right direction

// var WinGetFactory = new WindowsPackageManagerElevatedFactory();
var WinGetFactory = new WindowsPackageManagerStandardFactory();
var WinGetManager = WinGetFactory.CreatePackageManager();

// CHANGE THIS INDEX
int selectedIndex = 0;

PackageCatalogReference installedSearchCatalogRef;
if (selectedIndex < 0)
{
    installedSearchCatalogRef = WinGetManager.GetLocalPackageCatalog(LocalPackageCatalog.InstalledPackages);
}
else
{
    PackageCatalogReference selectedRemoteCatalogRef = WinGetManager.GetPackageCatalogs().ToArray().ElementAt(selectedIndex);

    Console.WriteLine($"Searching on package catalog {selectedRemoteCatalogRef.Info.Name} ");
    CreateCompositePackageCatalogOptions createCompositePackageCatalogOptions = WinGetFactory.CreateCreateCompositePackageCatalogOptions();
    createCompositePackageCatalogOptions.Catalogs.Add(selectedRemoteCatalogRef);
    createCompositePackageCatalogOptions.CompositeSearchBehavior = CompositeSearchBehavior.LocalCatalogs;
    installedSearchCatalogRef = WinGetManager.CreateCompositePackageCatalog(createCompositePackageCatalogOptions);
}

var ConnectResult = installedSearchCatalogRef.Connect();
if (ConnectResult.Status != ConnectResultStatus.Ok)
{
    throw new Exception("WinGet: Failed to connect to local catalog.");
}

FindPackagesOptions findPackagesOptions = WinGetFactory.CreateFindPackagesOptions();
PackageMatchFilter filter = WinGetFactory.CreatePackageMatchFilter();
filter.Field = PackageMatchField.Id;
filter.Option = PackageFieldMatchOption.ContainsCaseInsensitive;
filter.Value = "";
findPackagesOptions.Filters.Add(filter);

var TaskResult = ConnectResult.PackageCatalog.FindPackages(findPackagesOptions);

Console.WriteLine("Begin enumeration");
foreach (var match in TaskResult.Matches.ToArray())
{
    if (match.CatalogPackage.DefaultInstallVersion != null)
        Console.WriteLine($"Package {match.CatalogPackage.Name} is available Online: " + match.CatalogPackage.DefaultInstallVersion.PackageCatalog.Info.Name);
    //else
        //Console.WriteLine("Package is local only: " + match.CatalogPackage.Id);
}
Console.WriteLine("End enumeration");