Compare commits

...

3 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
bef5a3635d Address review: handle process-exit race and check elevation result
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-14 16:18:56 +08:00
copilot-swe-agent[bot]
8107e05ad7 Fix access denied crash when killing windows in WindowWalker
When killing an unresponsive window whose process was not flagged as
IsFullAccessDenied (because TestProcessAccessUsingAllAccessFlag passed),
Process.Kill() could still throw Win32Exception (error 5) for certain
protected/elevated processes. This caused PowerToys Run to crash.

- WindowProcess.KillThisProcess() (both launcher and CmdPal): catch
  Win32Exception with NativeErrorCode == 5 and fall back to elevated
  taskkill.exe, matching the existing IsFullAccessDenied path. Also
  properly dispose the Process object via `using`.
- ContextMenuHelper.KillProcessCommand() (launcher): catch Win32Exception
  and InvalidOperationException around KillThisProcess so any remaining
  failures are logged without crashing PowerToys Run.
- EndTaskCommand.KillProcess() (CmdPal): same protective error handling.

Agent-Logs-Url: https://github.com/microsoft/PowerToys/sessions/df39f01b-d1e2-413b-9154-0d402084933d

Co-authored-by: MuyuanMS <116717757+MuyuanMS@users.noreply.github.com>
2026-04-29 09:24:00 +00:00
copilot-swe-agent[bot]
bc662edad4 Initial plan 2026-04-29 08:51:20 +00:00
4 changed files with 83 additions and 8 deletions

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Ext.WindowWalker.Components;
using Microsoft.CmdPal.Ext.WindowWalker.Helpers;
@@ -61,7 +62,25 @@ internal sealed partial class EndTaskCommand : InvokableCommand
}
// Kill process
window.Process.KillThisProcess(SettingsManager.Instance.KillProcessTree);
try
{
window.Process.KillThisProcess(SettingsManager.Instance.KillProcessTree);
}
catch (ArgumentException)
{
// The process already exited between the existence check and the kill attempt.
}
catch (Win32Exception ex)
{
ExtensionHost.LogMessage(new LogMessage { Message = $"Failed to kill process '{window.Process.Name}' ({window.Process.ProcessID}) of the window '{window.Title}' ({window.Hwnd}): {ex.Message}" });
return false;
}
catch (InvalidOperationException ex)
{
ExtensionHost.LogMessage(new LogMessage { Message = $"Failed to kill process '{window.Process.Name}' ({window.Process.ProcessID}) of the window '{window.Title}' ({window.Hwnd}): {ex.Message}" });
return false;
}
return !SettingsManager.Instance.KeepOpenAfterKillAndClose;
}

View File

@@ -2,6 +2,8 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Diagnostics;
using Microsoft.CmdPal.Ext.WindowWalker.Commands;
@@ -219,14 +221,31 @@ internal sealed class WindowProcess
/// <param name="killProcessTree">Kill process and sub processes.</param>
internal void KillThisProcess(bool killProcessTree)
{
var killTree = killProcessTree ? " /t" : string.Empty;
if (IsFullAccessDenied)
{
var killTree = killProcessTree ? " /t" : string.Empty;
ExplorerInfoResultCommand.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, ExplorerInfoResultCommand.ShellRunAsType.Administrator, true);
if (!ExplorerInfoResultCommand.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, ExplorerInfoResultCommand.ShellRunAsType.Administrator, true))
{
throw new InvalidOperationException($"Failed to start elevated taskkill for process ID {ProcessID}.");
}
}
else
{
Process.GetProcessById((int)ProcessID).Kill(killProcessTree);
try
{
using var process = Process.GetProcessById((int)ProcessID);
process.Kill(killProcessTree);
}
catch (Win32Exception ex) when (ex.NativeErrorCode == 5)
{
// Error 5 = ERROR_ACCESS_DENIED
// Fall back to elevated taskkill when direct kill is denied
if (!ExplorerInfoResultCommand.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, ExplorerInfoResultCommand.ShellRunAsType.Administrator, true))
{
throw new InvalidOperationException($"Failed to start elevated taskkill for process ID {ProcessID}.", ex);
}
}
}
}

View File

@@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
@@ -102,7 +103,25 @@ namespace Microsoft.Plugin.WindowWalker.Components
}
// Kill process
window.Process.KillThisProcess(WindowWalkerSettings.Instance.KillProcessTree);
try
{
window.Process.KillThisProcess(WindowWalkerSettings.Instance.KillProcessTree);
}
catch (ArgumentException)
{
// The process already exited between the existence check and the kill attempt.
}
catch (Win32Exception ex)
{
Log.Exception($"Failed to kill process '{window.Process.Name}' ({window.Process.ProcessID}) of the window '{window.Title}' ({window.Hwnd}).", ex, typeof(ContextMenuHelper));
return false;
}
catch (InvalidOperationException ex)
{
Log.Exception($"Failed to kill process '{window.Process.Name}' ({window.Process.ProcessID}) of the window '{window.Title}' ({window.Hwnd}).", ex, typeof(ContextMenuHelper));
return false;
}
return !WindowWalkerSettings.Instance.OpenAfterKillAndClose;
}
}

View File

@@ -2,6 +2,7 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
@@ -208,14 +209,31 @@ namespace Microsoft.Plugin.WindowWalker.Components
/// <param name="killProcessTree">Kill process and sub processes.</param>
internal void KillThisProcess(bool killProcessTree)
{
string killTree = killProcessTree ? " /t" : string.Empty;
if (IsFullAccessDenied)
{
string killTree = killProcessTree ? " /t" : string.Empty;
Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, Helper.ShellRunAsType.Administrator, true);
if (!Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, Helper.ShellRunAsType.Administrator, true))
{
throw new InvalidOperationException($"Failed to start elevated taskkill for process ID {ProcessID}.");
}
}
else
{
Process.GetProcessById((int)ProcessID).Kill(killProcessTree);
try
{
using Process process = Process.GetProcessById((int)ProcessID);
process.Kill(killProcessTree);
}
catch (Win32Exception ex) when (ex.NativeErrorCode == 5)
{
// Error 5 = ERROR_ACCESS_DENIED
// Fall back to elevated taskkill when direct kill is denied
if (!Helper.OpenInShell("taskkill.exe", $"/pid {(int)ProcessID} /f{killTree}", null, Helper.ShellRunAsType.Administrator, true))
{
throw new InvalidOperationException($"Failed to start elevated taskkill for process ID {ProcessID}.", ex);
}
}
}
}