Search guides and the APICtrl K
  1. API reference
  2. Native GUI

gfx/uia_win32.h

The Windows end of accessibility: handing our component tree to UI Automation.

#include <sonore/gfx/uia_win32.h>
On this page

Design notes

What this is for

accessible_info.h gives every component a role, a name, the value it reads and a range. That is what a screen reader would want; this is what actually hands it over. Without this file the description layer is a well-tested object nobody outside the process can see, and NVDA, JAWS and Narrator all say nothing about a plugin built with this SDK.

Why it is written by hand

UIA is COM, and the usual way to write a provider is ATL. ATL is a Visual C++ library: depending on it would make this header MSVC-only inside a file that is already Windows-only, and would put a second toolchain requirement on anyone building a generated plugin. IUnknown is three methods. They are written out below and that is the whole of the cost.

The shape

Windows asks a window for its provider with WM_GETOBJECT, and everything else follows from the object returned:

RootProvider      -- the window itself, and the fragment root
ElementProvider   -- one per accessible component

Both read a SNAPSHOT of collectAccessible, and only the window's own thread takes it: when UIA calls the root there, and on the editor's 33 ms clock while any client is listening. UIA calls in on its OWN threads as well -- measured: an element's properties, values and navigation arrive on a UIA worker while the root's arrive on the window's thread -- and the first version walked the live component tree from whichever thread asked, while the clock was rewriting the very strings it read, and rebuilt the snapshot under elements still reading it. Now a walk off the window's thread keeps the last snapshot, and every read copies what it needs under a lock, so no pointer into the snapshot ever leaves it.

Events

A reader is told, without asking, when the focus moves to another control and when the focused control's value changes -- which is what makes automation audible while a knob moves under the host's hand. Raised from the editor's clock (announce(), on the window's thread, never from inside a call UIA made), against the element's index, which is its runtime id. Only the FOCUSED control's value, and at most four times a second with the last value always said: every value of every meter at 30 Hz is a reader that will not stop talking.

Types 2

class ElementProvider : public IRawElementProviderSimple, public IRawElementProviderFragment, public IValueProvider, public IRangeValueProvider

One accessible component, as UIA sees it.

Holds an ID rather than a Component*, for the reason in the header: UIA calls on its own thread and the editor is repainting on another. The id names the CONTROL, carried across snapshots for as long as its component and role last (detail::matchAccessibleNodes, as the macOS bridge carries its elements). It was the control's position in the snapshot, so a reader holding the third control heard the fourth once a panel above it closed -- its name, its value and its runtime id -- and an element whose control had gone answered for whatever came next. Now it answers UIA_E_ELEMENTNOTAVAILABLE, which is how a provider says "that one is gone".

class RootProvider : public IRawElementProviderSimple, public IRawElementProviderFragment, public IRawElementProviderFragmentRoot

The window, and the root of the fragment tree.

Owns the snapshot every ElementProvider reads through. Its lifetime is the window's: the peer holds one reference and releases it on close, and UIA may hold others for as long as it likes -- which is why the snapshot is copied data rather than pointers into a tree that is about to be destroyed.

Functions 2

↑↓ to move↵ to openEvery public name in the headers, and every guide