Windows 11 UI Automation: Reliable way to locate and highlight the Start button from a WinUI 3 app?

Perry Jones 45 Reputation points
2026-08-13T03:01:01.4533333+00:00

I have a WinUI 3 desktop app that provides guidance-only assistance. The app highlights external Windows controls with a transparent click-through overlay, speaks the instruction, waits for the user to act, then verifies the resulting UI state before advancing.

We recently changed the guidance engine to use UI Automation events as triggers, with post-condition validation and bounded polling as fallback.

The app now builds and launches successfully on a real Windows 11 self-hosted runner. The first real behavior test reaches the printer-guidance flow, but fails at the first step:

Start-button highlight was not observed.

The current Start-button selector is roughly:


The resolver enumerates UIA top-level windows, finds the taskbar window, then searches descendants for the Start control and uses its bounding rectangle for the highlight overlay.

The problem is that on the actual Windows 11 desktop this selector is apparently not resolving the Start button reliably enough for the highlight step to succeed.

What is the most reliable way to locate the Windows 11 Start button through UI Automation from another desktop process?

In particular:

  • Is Shell_TrayWnd + explorer.exe still the correct UIA root for the Start button on current Windows 11?
  • Is AutomationId = StartButton reliable across Windows 11 builds?
  • Should the Start button be located from the desktop root instead of through Shell_TrayWnd?
  • Are there cases where the Start button appears under a different UIA subtree, process, control type, or view?
  • Is Raw View preferable to Control View for this control?
  • Is there a recommended fallback strategy if the automation ID or taskbar hierarchy changes?

The app is guidance-only and does not programmatically click the Start button in production. It only needs to locate the control, obtain its bounding rectangle, highlight it, observe the user action, and then verify that the Start menu opened.

Any guidance on the most stable Windows 11 UIA pattern for resolving the Start button would be appreciated.

Windows development | Windows API - Win32
0 comments No comments

Answer accepted by question author
Taki Ly (WICLOUD CORPORATION) 4,440 Reputation points Microsoft External Staff Moderator
2026-08-13T05:42:12.2533333+00:00

Hello @Perry Jones ,

Thanks for the clear write-up. I reproduced this on a Windows 11 desktop (build 26200) and enumerated the taskbar via UI Automation. A few points that may help:

1. It's still under Shell_TrayWnd (explorer.exe), but it is not a direct child. It sits two levels deep: Shell_TrayWnd > Windows.UI.Input.InputSite.WindowClass >Taskbar.TaskbarFrameAutomationPeer > the Start element (AutomationId="StartButton", Name="Start", ControlType.Button, ClassName="ToggleButton"). So, a TreeScope.Children or shallow search will miss it, you likely need TreeScope.Descendants.

2. On a multi-monitor setup I saw two matches for AutomationId="StartButton" , one under Shell_TrayWnd (primary) and one under Shell_SecondaryTrayWnd (secondary). If you search from the desktop root with FindFirst, you may get the button on the wrong monitor, so the overlay ends up off the observed screen. Anchoring to a specific taskbar window instead of the root should avoid this.

Answers to your specific questions

  • Shell_TrayWnd + explorer.exe: still the correct anchor on current builds.
  • AutomationId="StartButton": reliable on this build, but nested inside the XAML island, so search descendants.
  • Search from root: not recommended with multiple monitors (duplicate matches) — anchor to the tray window.
  • Raw vs Control View: Control View was sufficient here.
  • Fallback: if the AutomationId changes, Name="Start" + ControlType.Button within Taskbar.TaskbarFrameAutomationPeer works as a secondary match.

Minimal resolver

static AutomationElement ResolveStartButton()
{
    var tray = AutomationElement.RootElement.FindFirst(
        TreeScope.Children,
        new PropertyCondition(AutomationElement.ClassNameProperty, "Shell_TrayWnd"));
    if (tray is null) return null;

    var start = tray.FindFirst(TreeScope.Descendants,
        new PropertyCondition(AutomationElement.AutomationIdProperty, "StartButton"))
        ?? tray.FindFirst(TreeScope.Descendants, new AndCondition(
            new PropertyCondition(AutomationElement.NameProperty, "Start"),
            new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));

    if (start is null || start.Current.IsOffscreen) return null;
    var r = start.Current.BoundingRectangle;
    if (r.IsEmpty || r.Width <= 0 || r.Height <= 0) return null;
    return start;
}

Two things that would help me confirm: is the failing runner using more than one display (or a virtual/headless one)? And what exact build is it on (winver)? The taskbar tree has shifted across 22H2/23H2/24H2, so the build matters.

If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide for your confirmation.

Thank you.

Was this answer helpful?

1 person found this answer helpful.

1 additional answer

Sort by: Most helpful
  1. Taki Ly (WICLOUD CORPORATION) 4,440 Reputation points Microsoft External Staff Moderator
    2026-08-13T03:36:34.3+00:00

    Hi @Perry Jones ,

    I'm working on this issue and will try to get back to you soon.

    Thank you for your patience.

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.