A colleague asked me recently, “Why do I have to implement a bounding rectangle for each of my UIA fragments, but not for the fragment that corresponds to the whole HWND?” In other words, why does a fragment root (the root element in a UI Automation tree that corresponds to the HWND that contains the UI ) get a bounding rectangle for free, but ordinary fragments do not? This was a great question.
To answer it, I need to explain that there can be more than one data provider for a given UI Automation element. As a matter of fact, on Windows 7, there could be five:
- The Override provider: a UIA provider that co-exists with a proxy provider, but overrides some properties and patterns selectively. These are relatively rare.
- The Annotation provider: a UIA provider that overrides properties when they are set using the UIA Dynamic Annotation API.
- The Main provider: the UIA provider created by the control developer, or a proxy provider acting on behalf of the control. This is the one we would usually call simply “the” provider.
- The Non-Client Area provider: a UIA provider that describes the non-client area (frame, title-bar, title-bar buttons). Windows provides this.
- The Host HWND provider: a UIA provider that gathers data from the hosting HWND. Windows provides this, too.
When a client asks for a property or pattern, the providers are generally polled in order, with the last respondent winning. [I write ‘”generally” because this is a description of how this works in Windows 7, but the details may vary in past and future releases.]
If you want to see which providers are active for a given element, you can look at the ProviderDescription property:
This can be a long string, but it describes in detail which providers are active for a given element.
A fragment root — the root element in a UI Automation tree that corresponds to the HWND that contains the UI – can have all five kinds of provider. The Host HWND provider gathers data from the hosting HWND and the non-client provider gathers data from the non-client area of the HWND: its title bar, frame, minimize/maximiaze buttons, etc. Override providers (which I’m not going to cover in depth here) also apply only to HWNDs.
On the other hand, only Main and Annotation providers can exist for fragments that do not correspond to a full HWND, and unless the app has used the Annotation API, the Annotation provider is not usually providing anything. That means that ordinary UIA fragments need very comprehensive Main providers, since Main providers do all of the work for these fragments.
The practical consequence here is that if you are implementing a UIA provider for a custom control with an HWND, like a simple button, you will find that UIA provides a number of properties for free for you, such as ClassName, Name, NativeWindowHandle, RuntimeID, and BoundingRectangle. I’m not trying to provide an exhaustive list here – you can try this out with the Inspect tool, inspecting your custom control, to see what you get for free. These properties all come from the Host HWND provider, since they are properties that are readily available from every HWND. It would be silly, for example, for Windows to expect every UIA provider to respond to a query about NativeHwndHandle, since Windows had to know the HWND in order to send WM_GETOBJECT to the custom control in the first place. Similarly, the bounding rectangle of a given HWND is common knowledge.
But once you create a fragment, such as a tree node in a tree control, and implement a UIA provider for that fragment, Windows cannot get to any of this free data. Windows has no way of knowing where your tree nodes are in a custom control – only the custom control knows that. So, for a fragment, the Main provider must implement virtually all of the properties itself. And if you look at the ProviderDescription property for a fragment, it is usually much shorter – with just 1 or 2 providers – than it would be for a fragment root.
So, the answer to my colleague’s question was, “You do need to respond to IRawElementProviderFragment::get_BoundingRectangle() for every provider except the fragment root”
His immediate follow-up was, “But the fragment root has to implement the IRawElementProviderFragment interface also – what is it supposed to do to implement get_BoundingRectangle?”
Answer: “Just set the output rectangle to [0, 0, 0, 0] and return – Windows will realize that you are not overriding the default value, which is the HWND’s own screen rectangle, and will return the default for you.” That’s how you do it for get_BoundingRectangle(), and other methods are similar: when implementing IRawElementProviderSimple::GetPropertyValue(), just leave the output parameter set to VT_EMPTY and return S_OK to let the system fill in a default. When implementing IRawElementProviderSimple::GetPatternProvider(), just leave the output parameter set to NULL and return S_OK to let the system fill in a default.
One other follow-up question that comes up here: How would I tell UIA that I do not want to support a certain property and pattern for my element and I do not want Windows to provide a default answer, either? In this case, return UIA_E_NOTSUPPORTED as an error code. Windows will break out of the fallback sequence and return that error code directly.