Skip to content

fix: handle AttachThreadInput Access Denied for elevated processes#123

Merged
Jeomon merged 1 commit intoCursorTouch:mainfrom
JezaChen:fix/handle-elevated-process-attach
Mar 25, 2026
Merged

fix: handle AttachThreadInput Access Denied for elevated processes#123
Jeomon merged 1 commit intoCursorTouch:mainfrom
JezaChen:fix/handle-elevated-process-attach

Conversation

@JezaChen
Copy link
Copy Markdown
Collaborator

Summary

  • bring_window_to_top now gracefully handles AttachThreadInput failures when targeting elevated (high-integrity) processes such as Settings and Task Manager
  • Instead of aborting the entire focus-switch flow, the error is caught and logged, and the method continues to attempt SetForegroundWindow / BringWindowToTop / SetWindowPos

Problem

When bring_window_to_top is called with a window handle belonging to an elevated process (e.g. Windows Settings, Task Manager), win32process.AttachThreadInput raises an Access Denied exception due to Windows UIPI (User Interface Privilege Isolation). This causes the entire window-switching operation to fail, even though the subsequent SetForegroundWindow call can still bring the window to the foreground without the thread attach.

Solution

Wrap the AttachThreadInput call in a try/except block so that:

  1. If attach succeeds (normal-privilege windows), behavior is unchanged.
  2. If attach fails (elevated windows), the failure is logged at DEBUG level with the exception details, and the flow continues to call SetForegroundWindow + BringWindowToTop + SetWindowPos.

Note: Without a successful AttachThreadInput, the target window will be brought to the foreground visually, but keyboard focus may not transfer. Full control of elevated windows requires running the MCP server itself with administrator privileges.

Changed files

  • src/windows_mcp/desktop/service.pybring_window_to_top: added per-thread try/except around AttachThreadInput

AttachThreadInput raises Access Denied if targeting elevated processes
(e.g. Settings, Task Manager) due to Windows UIPI. Catch the exception
per-thread and continue with SetForegroundWindow so the window can still
be brought to the foreground.
Copilot AI review requested due to automatic review settings March 25, 2026 01:14
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Windows desktop focus-switching flow to tolerate win32process.AttachThreadInput failures (notably “Access Denied” when the target window belongs to an elevated/high-integrity process), allowing the function to continue attempting SetForegroundWindow/BringWindowToTop/SetWindowPos.

Changes:

  • Wrap AttachThreadInput in a per-thread try/except to avoid aborting the entire focus-switch flow on failure.
  • Add debug logging when the thread attach fails.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +668 to +678
try:
win32process.AttachThreadInput(current_tid, thread, True)
attached_threads.append(thread)
except Exception as e:
# AttachThreadInput fails with Access Denied for elevated
# processes (e.g. Settings, Task Manager). Skip the attach
# and still attempt SetForegroundWindow below.
logger.debug(
f"AttachThreadInput failed for thread {thread} "
f"(likely elevated process), skipping: {e}"
)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new handler catches all Exceptions but logs it as "likely elevated process". That message can be misleading for unexpected failures (e.g., invalid TID) and it also risks masking real bugs. Consider catching the specific win32/pywintypes error and only continuing when the failure is Access Denied (winerror==5); otherwise re-raise or log at a higher severity.

Copilot uses AI. Check for mistakes.
# AttachThreadInput fails with Access Denied for elevated
# processes (e.g. Settings, Task Manager). Skip the attach
# and still attempt SetForegroundWindow below.
logger.debug(
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is logged at DEBUG, but this module sets logger.setLevel(logging.INFO) at import time, which will suppress this message in most configurations. If you want this failure to be observable when it happens, either avoid hard-setting the module logger level, or log this at INFO/WARNING (keeping elevated-window noise in mind).

Suggested change
logger.debug(
logger.info(

Copilot uses AI. Check for mistakes.
Comment on lines +668 to 681
try:
win32process.AttachThreadInput(current_tid, thread, True)
attached_threads.append(thread)
except Exception as e:
# AttachThreadInput fails with Access Denied for elevated
# processes (e.g. Settings, Task Manager). Skip the attach
# and still attempt SetForegroundWindow below.
logger.debug(
f"AttachThreadInput failed for thread {thread} "
f"(likely elevated process), skipping: {e}"
)

win32gui.SetForegroundWindow(target_handle)
win32gui.BringWindowToTop(target_handle)
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are unit tests for windows_mcp.desktop.service.Desktop that rely on mocking (e.g., in tests/test_snapshot_display_filter.py), but this new error-handling path isn’t covered. Add a test that mocks win32process.AttachThreadInput to raise Access Denied and asserts the function still calls SetForegroundWindow/BringWindowToTop and doesn’t propagate the exception.

Copilot uses AI. Check for mistakes.
@Jeomon Jeomon merged commit 467bfd3 into CursorTouch:main Mar 25, 2026
3 of 4 checks passed
@Jeomon
Copy link
Copy Markdown
Member

Jeomon commented Mar 25, 2026

In that case, if we run the MCP server in admin mode, we shouldn't encounter any issues. I had a similar experience when I tried to access the elements in the Task Manager; it failed, but it worked in admin mode.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants