From 4e24c79c4bb8bee4460977e53416392a14e9bd91 Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Thu, 9 Oct 2025 23:25:22 -0500 Subject: [PATCH 1/3] Get logs mostly clean --- .../Common/WinGetIntegrity.cs | 86 ++++++++++++++----- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs index 9669da8dbc..97f36f9806 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs @@ -7,8 +7,10 @@ namespace Microsoft.WinGet.Client.Engine.Common { using System; + using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; + using System.Linq; using System.Management.Automation; using Microsoft.WinGet.Client.Engine.Exceptions; using Microsoft.WinGet.Client.Engine.Helpers; @@ -39,29 +41,54 @@ public static void AssertWinGet(PowerShellCmdlet pwshCmdlet, string expectedVers return; } - try - { - // Start by calling winget without its WindowsApp PFN path. - // If it succeeds and the exit code is 0 then we are good. - var wingetCliWrapper = new WingetCLIWrapper(false); - var result = wingetCliWrapper.RunCommand(pwshCmdlet, "--version"); - result.VerifyExitCode(); - } - catch (Win32Exception e) - { - pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Win32Exception {e.Message}"); - throw new WinGetIntegrityException(GetReason(pwshCmdlet)); - } - catch (Exception e) when (e is WinGetCLIException || e is WinGetCLITimeoutException) - { - pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' WinGetCLIException {e.Message}"); - throw new WinGetIntegrityException(IntegrityCategory.Failure, e); - } - catch (Exception e) + pwshCmdlet.ExecuteInPowerShellThread(() => { - pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Exception {e.Message}"); - throw new WinGetIntegrityException(IntegrityCategory.Unknown, e); - } + try + { + // First check if winget is registered in the current session. + // If Get-Command doesn't find it, then winget is not registered in the current session. + var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); + { + ps.AddCommand("Get-Command").AddParameter("Name", "winget").AddParameter("ErrorAction", "Ignore"); + Collection results = ps.Invoke(); + ps.Dispose(); + if (results == null || + results.Count != 1 || + results.ElementAt(0).BaseObject is not CommandInfo) + { + // It's expected that the command is found, is the only command, and is a CommandInfo object. + pwshCmdlet.Write(StreamType.Verbose, $"'winget' was not found using Get-Command"); + throw new WinGetIntegrityException(GetReason(pwshCmdlet)); + } + } + + // Then try calling winget without its WindowsApp PFN path. + // If it succeeds and the exit code is 0 then we are good. + var wingetCliWrapper = new WingetCLIWrapper(false); + var result = wingetCliWrapper.RunCommand(pwshCmdlet, "--version"); + result.VerifyExitCode(); + } + catch (WinGetIntegrityException) + { + // We already know the reason why it failed. Just rethrow, but be sure to preserve the stack trace + throw; + } + catch (Win32Exception e) + { + pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Win32Exception {e.Message}"); + throw new WinGetIntegrityException(GetReason(pwshCmdlet)); + } + catch (Exception e) when (e is WinGetCLIException || e is WinGetCLITimeoutException) + { + pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' WinGetCLIException {e.Message}"); + throw new WinGetIntegrityException(IntegrityCategory.Failure, e); + } + catch (Exception e) + { + pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Exception {e.Message}"); + throw new WinGetIntegrityException(IntegrityCategory.Unknown, e); + } + }); // WinGet is installed. Verify version if needed. if (!string.IsNullOrEmpty(expectedVersion)) @@ -95,7 +122,20 @@ private static IntegrityCategory GetReason(PowerShellCmdlet pwshCmdlet) try { var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); - ps.AddCommand("winget").Invoke(); + ps.AddCommand("Get-Command").AddParameter("Name", "winget").AddParameter("ErrorAction", "Ignore"); + Collection results = ps.Invoke(); + if (results == null || + results.Count != 1 || + results.ElementAt(0).BaseObject is not CommandInfo) + { + // It's expected that the command is found, is the only command, and is a CommandInfo object. + pwshCmdlet.Write(StreamType.Verbose, $"'winget' was not found using Get-Command"); + } + else + { + ps.Commands.Clear(); + ps.AddCommand("winget").Invoke(); + } } catch (ApplicationFailedException e) { From d1beb853b3cad373b978d572d7b6b5fa80af55c6 Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Thu, 9 Oct 2025 23:48:08 -0500 Subject: [PATCH 2/3] Comment Cleanup --- .../Common/WinGetIntegrity.cs | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs index 97f36f9806..cf39ab2fd3 100644 --- a/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs +++ b/src/PowerShell/Microsoft.WinGet.Client.Engine/Common/WinGetIntegrity.cs @@ -45,10 +45,10 @@ public static void AssertWinGet(PowerShellCmdlet pwshCmdlet, string expectedVers { try { - // First check if winget is registered in the current session. - // If Get-Command doesn't find it, then winget is not registered in the current session. + // First check if winget is registered in the current session using Get-Command. var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); { + // Ignore the error, we will check the results instead ps.AddCommand("Get-Command").AddParameter("Name", "winget").AddParameter("ErrorAction", "Ignore"); Collection results = ps.Invoke(); ps.Dispose(); @@ -57,12 +57,13 @@ public static void AssertWinGet(PowerShellCmdlet pwshCmdlet, string expectedVers results.ElementAt(0).BaseObject is not CommandInfo) { // It's expected that the command is found, is the only command, and is a CommandInfo object. + // If it is not, then winget is not properly registered in the current session and we need to figure out why. pwshCmdlet.Write(StreamType.Verbose, $"'winget' was not found using Get-Command"); - throw new WinGetIntegrityException(GetReason(pwshCmdlet)); + throw new WinGetIntegrityException(GetReason(pwshCmdlet, false)); } } - // Then try calling winget without its WindowsApp PFN path. + // If the command is registered, try calling winget without its WindowsApp PFN path. // If it succeeds and the exit code is 0 then we are good. var wingetCliWrapper = new WingetCLIWrapper(false); var result = wingetCliWrapper.RunCommand(pwshCmdlet, "--version"); @@ -76,7 +77,7 @@ public static void AssertWinGet(PowerShellCmdlet pwshCmdlet, string expectedVers catch (Win32Exception e) { pwshCmdlet.Write(StreamType.Verbose, $"'winget.exe' Win32Exception {e.Message}"); - throw new WinGetIntegrityException(GetReason(pwshCmdlet)); + throw new WinGetIntegrityException(GetReason(pwshCmdlet, true)); } catch (Exception e) when (e is WinGetCLIException || e is WinGetCLITimeoutException) { @@ -108,44 +109,39 @@ public static void AssertWinGet(PowerShellCmdlet pwshCmdlet, string expectedVers } } - private static IntegrityCategory GetReason(PowerShellCmdlet pwshCmdlet) + private static IntegrityCategory GetReason(PowerShellCmdlet pwshCmdlet, bool commandIsRegistered) { // Ok, so you are here because calling winget --version failed. Lets try to figure out why. var category = IntegrityCategory.Unknown; - pwshCmdlet.ExecuteInPowerShellThread(() => + + // If the command is registered, then we can try to call it + // Otherwise we will skip this step since it will always result in category remaining unknown + if (commandIsRegistered) { + pwshCmdlet.ExecuteInPowerShellThread(() => + { // When running winget.exe on PowerShell the message of the Win32Exception will distinguish between // 'The system cannot find the file specified' and 'No applicable app licenses found' but of course // the HRESULT is the same (E_FAIL). // To not compare strings let Powershell handle it. If calling winget throws an // ApplicationFailedException then is most likely that the license is not there. - try - { - var ps = PowerShell.Create(RunspaceMode.CurrentRunspace); - ps.AddCommand("Get-Command").AddParameter("Name", "winget").AddParameter("ErrorAction", "Ignore"); - Collection results = ps.Invoke(); - if (results == null || - results.Count != 1 || - results.ElementAt(0).BaseObject is not CommandInfo) + try { - // It's expected that the command is found, is the only command, and is a CommandInfo object. - pwshCmdlet.Write(StreamType.Verbose, $"'winget' was not found using Get-Command"); + using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace)) + { + ps.AddCommand("winget").Invoke(); + } } - else + catch (ApplicationFailedException e) { - ps.Commands.Clear(); - ps.AddCommand("winget").Invoke(); + pwshCmdlet.Write(StreamType.Verbose, e.Message); + category = IntegrityCategory.AppInstallerNoLicense; } - } - catch (ApplicationFailedException e) - { - pwshCmdlet.Write(StreamType.Verbose, e.Message); - category = IntegrityCategory.AppInstallerNoLicense; - } - catch (Exception) - { - } - }); + catch (Exception) + { + } + }); + } if (category != IntegrityCategory.Unknown) { From ea68a44852aec8470293d0cf8eddcc734b3ccf0c Mon Sep 17 00:00:00 2001 From: Kaleb Luedtke Date: Tue, 14 Oct 2025 20:35:56 -0500 Subject: [PATCH 3/3] Update release notes --- doc/ReleaseNotes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/ReleaseNotes.md b/doc/ReleaseNotes.md index 8ec1e210c6..0816a7db29 100644 --- a/doc/ReleaseNotes.md +++ b/doc/ReleaseNotes.md @@ -1,3 +1,6 @@ ## New in v1.28 + +## Bug Fixes +* The PowerShell module now checks for the executable to be in the path before calling it when using `Repair-WinGetPackageManager`