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.37k stars 1.45k forks source link

COM API client FindPackages function fails in CreateInstance method when run under LOCAL SYSTEM Context. Unable to find WindowsPackageManagerServer.exe #4944

Open dave-patchblox opened 3 weeks ago

dave-patchblox commented 3 weeks ago

Brief description of your issue

COM Client seems to have an issue when run under the SYSTEM context. I do not believe this is the same issue folks have described where they cannot launch winget that is located in %PROGRAMFILES%\windowsapps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe. On my Windows 10 machine, the native wingetcli runs as expected as either a normal user, administrator equivalent OR SYSTEM. IOW, the failure does not repro on native winget client.

The issue I am seeing has to do with the calling of the 'FindPackages' API under the SYSTEM context. This API appears to use WindowsPackageManagerFactory under the hood. This API attempts to instantiate the WindowsPackageManagerServer COM server. The code for WindowsPackageManagerServer found here https://github.com/microsoft/winget-cli/tree/master/src/WinGetServer /WinGetServerManualActivation_Client.cpp seems to lookup the path to the EXE by calling GetPackageLocation. This function calls FindPackagesByPackageFamily (for a PROD build the "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe" directory and exe name as "WindowsPackageManagerServer.exe" are used). On my machine, the path it comes up with is...

%WINDIR%\System32\config\systemprofile\AppData\Local\Microsoft\windowsapps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe

That directory doesnt exist by default and the COM invocation fails with a ERROR_FILE_NOT_FOUND. FWIW, if I copy the actual folder and it contents where the server actually exists (%PROGRAMFILES%\windowsapps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.) to that directory, then the COM server is happily created. Obviously that is just a test to triage the issue. So, the question I have is WHY is FindPackagesByPackageFamily returning %WINDIR%\System32\config\systemprofile\AppData\Local\Microsoft\windowsapps?

FWIW, as a logged in user, I seem to have a symlink/alias directory under my user profile at C:\Users\\AppData\Local\Microsoft\WindowsApps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe that was created by something. I am wondering if the code that creates that symlink/alias hasnt been run for the SYSTEM profile.

Steps to reproduce

Create a sample C# application that uses WindowsPackageManager Interop assembly with some code like this...

WindowsPackageManagerFactory WinGetFactory;

bool IsAdministrator = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); Console.WriteLine("Is User Admin: " + IsAdministrator + " and the user is: " + WindowsIdentity.GetCurrent().Name);

// If the user is an administrator, use the elevated factory. Otherwhise COM will crash if (IsAdministrator) WinGetFactory = new WindowsPackageManagerElevatedFactory(); else WinGetFactory = new WindowsPackageManagerStandardFactory();

var WinGetManager = WinGetFactory.CreatePackageManager(); PackageCatalogReference installedSearchCatalogRef; installedSearchCatalogRef = WinGetManager.GetLocalPackageCatalog(LocalPackageCatalog.InstalledPackages);

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

FindPackagesOptions findPackagesOptions = WinGetFactory.CreateFindPackagesOptions(); PackageMatchFilter filter = WinGetFactory.CreatePackageMatchFilter(); filter.Field = Microsoft.Management.Deployment.PackageMatchField.Name; filter.Value = null; findPackagesOptions.Filters.Add(filter);

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

The call to FindPackages throws an exception with ERROR_FILE_NOT_FOUND.

Expected behavior

COM invocation of WingetServer succeeds and doesnt return ERROR_FILE_NOT_FOUND

Actual behavior

COM invocation of WingetServer FAILS to find the executable and launch and instead throws exception with ERROR_FILE_NOT_FOUND.

Environment

Windows Package Manager v1.9.25180 Copyright (c) Microsoft Corporation. All rights reserved.

Windows: Windows.Desktop v10.0.19045.5011 System Architecture: X64 Package: Microsoft.DesktopAppInstaller v1.24.25180.0

Winget Directories

Logs %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\DiagOutputDir User Settings %LOCALAPPDATA%\Packages\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe\LocalState\settings.json Portable Links Directory (User) %LOCALAPPDATA%\Microsoft\WinGet\Links Portable Links Directory (Machine) C:\Program Files\WinGet\Links Portable Package Root (User) %LOCALAPPDATA%\Microsoft\WinGet\Packages Portable Package Root C:\Program Files\WinGet\Packages Portable Package Root (x86) C:\Program Files (x86)\WinGet\Packages Installer Downloads %USERPROFILE%\Downloads

Links

Privacy Statement https://aka.ms/winget-privacy License Agreement https://aka.ms/winget-license Third Party Notices https://aka.ms/winget-3rdPartyNotice Homepage https://aka.ms/winget Windows Store Terms https://www.microsoft.com/en-us/storedocs/terms-of-sale

Admin Setting State

LocalManifestFiles Disabled BypassCertificatePinningForMicrosoftStore Disabled InstallerHashOverride Disabled LocalArchiveMalwareScanOverride Disabled ProxyCommandLineOptions Disabled DefaultProxy Disabled

stephengillie commented 3 weeks ago

FWIW, as a logged in user, I seem to have a symlink/alias directory under my user profile at C:\Users\AppData\Local\Microsoft\WindowsApps\Microsoft.DesktopAppInstaller_8wekyb3d8bbwe that was created by something. I am wondering if the code that creates that symlink/alias hasnt been run for the SYSTEM profile.

For this part, the package manager creates symlinks for Portable type packages, I believe in this folder. I do not know where these go in the SYSTEM context.

dave-patchblox commented 3 weeks ago

Thanks Stephen. I did some more reading here around SYSTEM context issues with various folks. Issue #4589 goes into some different approaches. @JohnMcPMS talks about using the INPROC COM classes instead of the out of proc COM classes. There is another reference to AppIInstallerCaller C++ sample app in tools as a reference for using the COM API. The problem with that sample is it does out of proc too, so it isnt going to work for SYSTEM context. It would be great if the MS devs would create a quick C# CLI sample app that uses the IN PROC COM calls instead of having developers wasting time/fumbling around trying to understand how to make this work reliably.