Just out of curiosity I decided to write a simple tool with the functionality similar to UI Spy. Basically it displays a tree of controls and allows seeing each control’s property. Now I’ve started implementing patterns interaction and ran into the following issue: as soon as user clicks for example ‘InvokePatter.Invoke’ in my application UI automation switches focus to the application I’m targeting. The same happens for other patterns. And it also behaves in the same way in the original UI Spy application.
This behavior makes impossible manipulating menus with my application because as I soon I click again on my application the tested application loses focus and menu is being closed. What I’d like to do is interacting with the application using UI automation but keeping my (UI Spy) application focused. Any ideas how to achieve it? Or at least how to achieve desired functionality – allow users interacting with menus?
UIAutomation does this on purpose: applications generally only expect to receive input when they have the focus. In order to send keyboard input, you have to focus it first. Or if you click on a control to interact with it – it usually gets the focus as a result of the click, and then does the action. There are some apps that get very confused (sometimes to the point of crashing) if you send them input without sending them focus first.
(These apps might, for example, initialize some internal state in WM_SETFOCUS and rely on that state being ready when they receive input. This can’t really be considered a bug, because Windows essentially promises to send WM_SETFOCUS before sending input, so it would really be the tool that’s sending the ‘faked’ input that’s breaking the contract here.)
Menus are a trickier case: first, in Windows, menus are only ever present on an app that has the focus. So to display a menu, the app with the menu must have the focus, so focus must switch away from UISpy: no way around that. But the real problem with menus isn’t UIAutomation switching focus to the app: it’s that clicking on the tool (UISpy) will cause the menu to be dismissed. This isn’t a UIAutomation issue – it’s just how Win32 handles menus. So the real question here is: how do I use a tool to explorer or otherwise manipulate menus, when clicking on the tool will dismiss the very menu I’m trying to work with?
There’s a couple of ways around this – both of which Inspect Objects tool (inspect.exe) uses. Inspect.exe was the old MSAA predecessor of UISpy, but the updated version – available as part of the SDK – now supports both MSAA and UIAutomation. The following techniques are only implemented in a couple of places (eg. SetFocus, navigation commands, but not Invoke.Invoke()), but you could use the techniques in your own tool as appropriate.
It gets around this issue using two approaches:
Hotkeys – Hotkeys by themselves don’t change focus or dismiss menus – so use RegisterHotKey() to assign a hotkey (eg. Ctrl-Shift-X) for each action you might want to carry out on the current object. Now when the target app is focused and the menu is present, you can use the hotkey combo to signal to the tool to carry out the appropriate action.
Creative use of the mouse: you can’t use the mouse to click the UI, but you can still take advantage of the mouse position. Inspect has an ‘Active Hover Toolbar’ option (under Options menu): when selected, if you hover the mouse over a toolbar item for a few seconds, it will treat it as though it were clicked. This allows you to navigate through the menu items, for example, without actually having to click on any Inspect UI. Internally, it’s perhaps using some combination of polling and TB_HITTEST to figure out which button the pointer is over.
Or you could roll a combination of these: use a hotkey to trigger the command in the tool that the mouse pointer is over – whichever works for you.