Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions doc/ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## New in v1.28

<!-- Nothing yet! -->

## Bug Fixes
* The PowerShell module now checks for the executable to be in the path before calling it when using `Repair-WinGetPackageManager`
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -39,29 +41,55 @@ 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 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<PSObject> 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.
// 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, false));
}
}

// 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");
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, true));
}
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))
Expand All @@ -81,31 +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("winget").Invoke();
}
catch (ApplicationFailedException e)
{
pwshCmdlet.Write(StreamType.Verbose, e.Message);
category = IntegrityCategory.AppInstallerNoLicense;
}
catch (Exception)
{
}
});
try
{
using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace))
{
ps.AddCommand("winget").Invoke();
}
}
catch (ApplicationFailedException e)
{
pwshCmdlet.Write(StreamType.Verbose, e.Message);
category = IntegrityCategory.AppInstallerNoLicense;
}
catch (Exception)
{
}
});
}

if (category != IntegrityCategory.Unknown)
{
Expand Down