Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display the context menu (right click menu) of a hidden system tray icon in the new xaml overflow window programmatically (Windows 11)?

I need to display the context menu obtained through right clicking an icon in the overflow tray on Windows 11 build 26100.1:

enter image description here

I know this is possible and that too very cleanly as can be seen in the program below:

enter image description here

However since this is a closed source program I am unaware of how they are doing it. I also dont think that there is some hooking or dll injection going on because the program is not being run with any admin privileges. There has to be something that I am fundamentally missing on.

I however am able to do do it very inefficiently and also in an ugly fashion as follows:

using System.Runtime.InteropServices;
using FlaUI.Core;
using FlaUI.UIA3;


[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowName);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string className, string windowName);

void Main()
{
    IntPtr hWnd_Outer = FindWindow("TopLevelWindowForOverflowXamlIsland", null);
    IntPtr hWnd_Inner = FindWindowEx(hWnd_Outer, IntPtr.Zero, "Windows.UI.Composition.DesktopWindowContentBridge", null);
    
    var automation = new UIA3Automation();
    var Inner = automation.FromHandle(hWnd_Inner);
    var TrayIcons = Inner.FindAllChildren();

    // right click [inneficient]
    ShowWindow(hWnd_Outer, 0);
    ShowWindow(hWnd_Outer, 1);
    ShowWindow(hWnd_Outer, 3);
    TrayIcons[2].RightClick();
    ShowWindow(hWnd_Outer, 0);
    
}

The result is:

enter image description here

The tray window flashes and only disappears after a substantial delay leaving the desired context menu alone as can be seen. Also the TrayIcons[2].RightClick() is not very reliable as sometimes it throws the NoClickablePointException and as can be seen I need to do a lot of ShowWindow() calls to even make it work (why the particular sequence works i have no idea either).

AutomationElement also has a Pattern.Invoke() which works with elements that support that pattern, an in this case the tray icon does but it only works for the left click.

I tried SendMessage() but they fail to interact with the newer xaml controls:

[DllImport("user32.dll")]
public static extern IntPtr WindowFromPoint(POINT Point);

[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);

GetCursorPos(out POINT pt);
IntPtr hWnd = WindowFromPoint(pt);
SendMessage(hWnd, WM_CONTEXTMENU, hWnd, MakeLParam(pt.X, pt.Y));

I get the context menu when I hover over win32 windows like linqpad or firefox but when I hover over the taskbar or the task manager I get nothing.

Things like SendInput() are out of consideration because it requires the window to both be focused and visible (not even minimized).

I feel like xaml windows have a substantially different means of receiving messages unlike the usual elements.

Appreciate any help.

like image 723
Ajaykrishnan R Avatar asked Dec 06 '25 03:12

Ajaykrishnan R


1 Answers

Happy to announce that I have figured it out !

Key insight came from : https://github.com/eythaann/Seelen-UI/blob/b5581ab5e714b4e69e7d6fb8b871ab37e094dea3/src/background/modules/tray/application.rs#L442

Where I observed that the IUIAutomationElement3 had a method named ShowContextMenu(). I had seen this before but having never dealt with COM objects before I didnt know how to use it. Also I noticed in their implementation that they are using ShowWindowAsync(hWnd_Overflow, 5) once and that is enough for ShowContextMenu() to work. Turns out you dont need the window to be actually visible inorder for ShowContextMenu(). And then they are hiding it just like in the question.

A nice blog on UIAutomationClient : https://scorpiosoftware.net/2024/03/26/the-power-of-ui-automation/

And the interop library on nuget (You can also do without it by adding a COM reference using Visual Studio) : https://www.nuget.org/packages/Interop.UIAutomationClient.Signed#versions-body-tab

So here's my implementation:

using System.Runtime.InteropServices;
using Interop.UIAutomationClient;

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string className, string windowName);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hWndParent, IntPtr hWndChildAfter, string className, string windowName);

[DllImport("user32.dll", SetLastError = true)]
public static extern int ShowWindowAsync(IntPtr hWnd, int nCmdShow)

void Main() 
{
    IntPtr hWnd_Outer = FindWindow("TopLevelWindowForOverflowXamlIsland", null);
    IntPtr hWnd_Inner = FindWindowEx(hWnd_Outer, IntPtr.Zero, "Windows.UI.Composition.DesktopWindowContentBridge", null);

    CUIAutomation ui = new();
    const int classPropertyId = 30012;
    var innerIconContainer = ui.ElementFromHandle(hWnd_Inner);
    var icons = innerIconContainer.FindAll(TreeScope.TreeScope_Children, ui.CreateTrueCondition());

    var icon = icons.GetElement(6) as IUIAutomationElement3;
    ShowWindowAsync(hWnd_Outer, 5);
    icon.ShowContextMenu();
    ShowWindowAsync(hWnd_Outer, 0)
}

And the result:

Invoking the right click context menu of a hidden tray icon in the overflow menu

like image 73
Ajaykrishnan R Avatar answered Dec 08 '25 16:12

Ajaykrishnan R