Compare commits

..

3 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
d6d5967b69 Use $conditionalImport variable consistently in EnableModule.ps1 Add-Content
Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/95c97d85-c99b-4437-95df-c971b16491cd

Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-18 17:44:33 +00:00
copilot-swe-agent[bot]
129f0a33a0 Fix Command Not Found crash in non-interactive PowerShell sessions (e.g. VS Code tasks)
Only import the Microsoft.WinGet.CommandNotFound module in interactive
PowerShell sessions. The module's WarmUp background task can crash with
PipelineStoppedException when PowerShell exits quickly (e.g. when running
VS Code shell tasks with -Command). Since Command Not Found is a feedback
provider only useful in interactive sessions, skip loading it when
PowerShell is invoked with -Command, -File, or -NonInteractive flags.

Also auto-upgrade existing unconditional imports when the Settings page
checks requirements.

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/95c97d85-c99b-4437-95df-c971b16491cd

Co-authored-by: niels9001 <9866362+niels9001@users.noreply.github.com>
2026-04-18 17:43:42 +00:00
copilot-swe-agent[bot]
2cea99965b Initial plan 2026-04-18 17:32:34 +00:00
7 changed files with 41 additions and 46 deletions

View File

@@ -34,7 +34,6 @@ namespace Awake
private static readonly string[] _aliasesPidOption = ["--pid", "-p"];
private static readonly string[] _aliasesExpireAtOption = ["--expire-at", "-e"];
private static readonly string[] _aliasesParentPidOption = ["--use-parent-pid", "-u"];
private static readonly string[] _aliasesNoConsoleOption = ["--no-console", "-n"];
private static readonly JsonSerializerOptions _serializerOptions = new() { IncludeFields = true };
private static readonly ETWTrace _etwTrace = new();
@@ -177,12 +176,6 @@ namespace Awake
IsRequired = false,
};
Option<bool> noConsoleOption = new(_aliasesNoConsoleOption, () => false, Resources.AWAKE_CMD_HELP_NO_CONSOLE_OPTION)
{
Arity = ArgumentArity.ZeroOrOne,
IsRequired = false,
};
timeOption.AddValidator(result =>
{
if (result.Tokens.Count != 0 && !uint.TryParse(result.Tokens[0].Value, out _))
@@ -221,11 +214,10 @@ namespace Awake
pidOption,
expireAtOption,
parentPidOption,
noConsoleOption,
];
rootCommand.Description = Core.Constants.AppName;
rootCommand.SetHandler(HandleCommandLineArguments, configOption, displayOption, timeOption, pidOption, expireAtOption, parentPidOption, noConsoleOption);
rootCommand.SetHandler(HandleCommandLineArguments, configOption, displayOption, timeOption, pidOption, expireAtOption, parentPidOption);
return rootCommand;
}
@@ -308,21 +300,13 @@ namespace Awake
}
}
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid, string expireAt, bool useParentPid, bool noConsole)
private static void HandleCommandLineArguments(bool usePtConfig, bool displayOn, uint timeLimit, int pid, string expireAt, bool useParentPid)
{
if (pid == 0 && !useParentPid)
{
if (!noConsole)
{
Logger.LogInfo("No PID specified. Allocating console...");
Bridge.FreeConsole();
AllocateLocalConsole();
}
else
{
Logger.LogInfo("No PID specified. Running without console (--no-console).");
Bridge.FreeConsole();
}
Logger.LogInfo("No PID specified. Allocating console...");
Bridge.FreeConsole();
AllocateLocalConsole();
}
else
{
@@ -336,7 +320,6 @@ namespace Awake
Logger.LogInfo($"The value for --pid is: {pid}");
Logger.LogInfo($"The value for --expire-at is: {expireAt}");
Logger.LogInfo($"The value for --use-parent-pid is: {useParentPid}");
Logger.LogInfo($"The value for --no-console is: {noConsole}");
// Start the monitor thread that will be used to track the current state.
Manager.StartMonitor();

View File

@@ -105,15 +105,6 @@ namespace Awake.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to When set, the console window will not be shown and Awake will only appear in the system tray..
/// </summary>
internal static string AWAKE_CMD_HELP_NO_CONSOLE_OPTION {
get {
return ResourceManager.GetString("AWAKE_CMD_HELP_NO_CONSOLE_OPTION", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Uses the parent process as the bound target - once the process terminates, Awake stops..
/// </summary>

View File

@@ -194,9 +194,6 @@
<data name="AWAKE_CMD_PARENT_PID_OPTION" xml:space="preserve">
<value>Uses the parent process as the bound target - once the process terminates, Awake stops.</value>
</data>
<data name="AWAKE_CMD_HELP_NO_CONSOLE_OPTION" xml:space="preserve">
<value>When set, the console window will not be shown and Awake will only appear in the system tray.</value>
</data>
<data name="AWAKE_SCREEN_ON" xml:space="preserve">
<value>On</value>
</data>

View File

@@ -51,7 +51,6 @@ Options:
-p, --pid Process ID to bind to
-e, --expire-at Expiration date/time
-u, --use-parent-pid Bind to parent process
-n, --no-console Hide the console window (tray only)
```
### Examples
@@ -76,11 +75,6 @@ Keep awake while another process is running:
PowerToys.Awake.exe --pid 1234
```
Keep awake indefinitely without showing the console window:
```powershell
PowerToys.Awake.exe --no-console
```
## Architecture
### Design Highlights

View File

@@ -51,6 +51,15 @@ if ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contai
}
elseif ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contains("f45873b3-b655-43a6-b217-97c00aa0db58")))
{
# Upgrade unconditional import to conditional to avoid crashes in non-interactive sessions (e.g. VS Code tasks)
if ($profileContent.Contains("Import-Module -Name Microsoft.WinGet.CommandNotFound") -and (-not $profileContent.Contains("GetCommandLineArgs")))
{
$conditionalImport = @'
if (-not ([Environment]::GetCommandLineArgs() | Where-Object { $_ -in @('-Command','-c','-EncodedCommand','-e','-ec','-File','-f','-NonInteractive') })) { Import-Module -Name Microsoft.WinGet.CommandNotFound }
'@
$profileContent = $profileContent.Replace("Import-Module -Name Microsoft.WinGet.CommandNotFound", $conditionalImport)
Set-Content -Path $PROFILE -Value $profileContent
}
Write-Host "Command Not Found module is registered in the profile file."
# This message will be compared against in Command Not Found Settings page code behind. Take care when changing it.
}

View File

@@ -55,12 +55,18 @@ if (!(Test-Path $PROFILE))
$profileContent = Get-Content -Path $PROFILE -Raw
# Only import in interactive sessions to avoid crashes when PowerShell exits quickly (e.g. VS Code tasks).
# Command Not Found is a feedback provider only useful in interactive sessions.
$conditionalImport = @'
if (-not ([Environment]::GetCommandLineArgs() | Where-Object { $_ -in @('-Command','-c','-EncodedCommand','-e','-ec','-File','-f','-NonInteractive') })) { Import-Module -Name Microsoft.WinGet.CommandNotFound }
'@
if ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contains("34de4b3d-13a8-4540-b76d-b9e8d3851756")))
{
if ($profileContent.Contains("Import-Module `"$scriptPath\WinGetCommandNotFound.psd1`""))
{
$profileContent = $profileContent.Replace("Import-Module `"$scriptPath\WinGetCommandNotFound.psd1`"",
"Import-Module -Name Microsoft.WinGet.CommandNotFound")
$conditionalImport)
$profileContent = $profileContent.Replace("34de4b3d-13a8-4540-b76d-b9e8d3851756",
"f45873b3-b655-43a6-b217-97c00aa0db58")
Set-Content -Path $PROFILE -Value $profileContent
@@ -70,13 +76,23 @@ if ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contai
}
elseif ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contains("f45873b3-b655-43a6-b217-97c00aa0db58")))
{
Write-Host "Module is already registered in the profile file."
# This message will be compared against in Command Not Found Settings page code behind. Take care when changing it.
if ($profileContent.Contains("Import-Module -Name Microsoft.WinGet.CommandNotFound") -and (-not $profileContent.Contains("GetCommandLineArgs")))
{
$profileContent = $profileContent.Replace("Import-Module -Name Microsoft.WinGet.CommandNotFound", $conditionalImport)
Set-Content -Path $PROFILE -Value $profileContent
Write-Host "Module was successfully upgraded in the profile file."
# This message will be compared against in Command Not Found Settings page code behind. Take care when changing it.
}
else
{
Write-Host "Module is already registered in the profile file."
# This message will be compared against in Command Not Found Settings page code behind. Take care when changing it.
}
}
else
{
Add-Content -Path $PROFILE -Value "`r`n#f45873b3-b655-43a6-b217-97c00aa0db58 PowerToys CommandNotFound module"
Add-Content -Path $PROFILE -Value "`r`nImport-Module -Name Microsoft.WinGet.CommandNotFound"
Add-Content -Path $PROFILE -Value "`r`n$conditionalImport"
Add-Content -Path $PROFILE -Value "#f45873b3-b655-43a6-b217-97c00aa0db58"
Write-Host "Module was successfully registered in the profile file."
# This message will be compared against in Command Not Found Settings page code behind. Take care when changing it.

View File

@@ -19,10 +19,15 @@ if ((-not [string]::IsNullOrEmpty($profileContent)) -and ($profileContent.Contai
}
# Replace old module with new one (and new GUID comment)
# Use conditional import to avoid crashes when PowerShell exits quickly (e.g. VS Code tasks)
$conditionalImport = @'
if (-not ([Environment]::GetCommandLineArgs() | Where-Object { $_ -in @('-Command','-c','-EncodedCommand','-e','-ec','-File','-f','-NonInteractive') })) { Import-Module -Name Microsoft.WinGet.CommandNotFound }
'@
$regex = "Import-Module .*WinGetCommandNotFound.psd1`""
if ($profileContent -match $regex)
$match = [regex]::Match($profileContent, $regex)
if ($match.Success)
{
$profileContent = $profileContent -replace $regex, "Import-Module -Name Microsoft.WinGet.CommandNotFound"
$profileContent = $profileContent.Replace($match.Value, $conditionalImport)
$profileContent = $profileContent -replace $legacyGuid, "f45873b3-b655-43a6-b217-97c00aa0db58"
Set-Content -Path $PROFILE -Value $profileContent
}