1# Runtime two way IAccessible2 to UI Automation elements look up via unique id
2Assistive technologies (ATs) who currently rely on IAccessible2 (IA2) that want
3to take advantage of the UI Automation (UIA) features at runtime can convert an
4IA2 element to an UIA element via a unique id and directly access UIA's API.
5This enables ATs who want to gradually transition from IA2 to UIA to experiment
6with individual UIA elements at runtime without switching entirely to UIA.
7
8
9To look up an UIA element through a unique id, an AT can utilize
10`IUIAutomationItemContainerPattern::FindItemByProperty()` with the custom UIA
11unique id property and the element's unique id as parameters.
12To look up an IA2 element from an UIA element, an AT can simply
13utilize IUIAutomationLegacyIAccessiblePattern::GetIAccessible() and
14then query for IAccessible2 interface. The unique id is not needed to
15look up the IA2 element from UIA element.
16
17## Convert an IA2 element to UIA element via unique id
18An IA2 element can be converted to an UIA element at runtime via a unique id
19that is shared between the two APIs.
20
21*Note: For the purpose of brevity and clarity, the code snippets below do not
22include clean-up of COM references neither does it have error handling.*
23
24~~~c++
25  #include <uiautomation.h>
26  #include <uiautomationclient.h>
27
28  // Consider the following HTML:
29  // <html>
30  //  <button>button</button>
31  // </html>
32
33  // Register custom UIA property for retrieving the unique id of IA2 object.
34  // {cc7eeb32-4b62-4f4c-aff6-1c2e5752ad8e}
35  GUID UiaPropertyUniqueIdGuid = {
36      0xcc7eeb32,
37      0x4b62,
38      0x4f4c,
39      {0xaf, 0xf6, 0x1c, 0x2e, 0x57, 0x52, 0xad, 0x8e}};
40
41  // Create the registrar object and get the IUIAutomationRegistrar
42  // interface pointer.
43  IUIAutomationRegistrar* registrar;
44  CoCreateInstance(CLSID_CUIAutomationRegistrar, nullptr, CLSCTX_INPROC_SERVER,
45                   IID_PPV_ARGS(&registrar));
46
47  // Custom UIA property id used to retrieve the unique id between UIA/IA2.
48  PROPERTYID uia_unique_id_property_id;
49
50  // Register the custom UIA property that represents the unique id of an UIA
51  // element which also matches its corresponding IA2 element's unique id.
52  // Custom property registration only needs to be done once per process
53  // lifetime.
54  UIAutomationPropertyInfo unique_id_property_info = {
55      UiaPropertyUniqueIdGuid, L"UniqueId", UIAutomationType_String};
56  registrar->RegisterProperty(&unique_id_property_info,
57                              &uia_unique_id_property_id);
58
59  // Assume we are given the IAccessible2 element for button, and we want to
60  // retrieve its corresponding UIA element for the final result.
61  IAccessible2* button_ia2; /* Initialized */
62
63  // Retrieve button IA2 element's unique id, which will be used to look up the
64  // corresponding UIA element later.
65  LONG unique_id_long;
66  button_ia2->get_uniqueID(&unique_id_long);
67
68  // Assume we are given the Window Handle hwnd for the root.
69  UIA_HWND hwnd; /* Initialized */
70
71  // Instantiating an IUIAutomation object.
72  IUIAutomation* ui_automation;
73  CoCreateInstance(CLSID_CUIAutomation, NULL, CLSCTX_INPROC_SERVER,
74                   IID_PPV_ARGS(&ui_automation));
75
76  // Retrieve the root element from the window handle.
77  IUIAutomationElement* root_element;
78  ui_automation->ElementFromHandle(hwnd, &root_element);
79
80  // Retrieve the ItemContainerPattern of the root element.
81  IUIAutomationItemContainerPattern* item_container_pattern;
82  root_element->GetCurrentPatternAs(UIA_ItemContainerPatternId,
83                                    IID_PPV_ARGS(&item_container_pattern));
84
85  // We also need to convert the retrieved IA2 element unique id from long to
86  // VARIANT.VT_BSTR to be consumed by UIA. For demo purpose, I utilize
87  // std::string here as an intermediary step to convert to VARIANT.VT_BSTR.
88  std::string unique_id_str = std::to_string(unique_id_long);
89
90  VARIANT unique_id_variant;
91  unique_id_variant.vt = VT_BSTR;
92  unique_id_variant.bstrVal = SysAllocString(unique_id_str.c_str());
93
94  // Retrieving the corresponding UIAutomation element from the unique id of IA2
95  // object.
96  IUIAutomationElement* button_uia;
97  item_container_pattern->FindItemByProperty(nullptr, uia_unique_id_property_id,
98                                             unique_id_variant,
99                                             &button_uia /* final result */);
100~~~
101
102## Convert an UIA element to IA2 element.
103Converting an UIA element to an IA2 element is a lot more straightforward and
104does not require the shared unique id. Consider the same example above.
105~~~c++
106  #include <uiautomationclient.h>
107
108  // Assume we are given the UIAutomation element for button, and we want to
109  // retrieve its corresponding IAccessible2 element for the final result.
110  IUIAutomationElement* button_uia; /* Initialized */
111
112  // Retrieve the LegacyIAccessiblePattern of the button UIA element.
113  IUIAutomationLegacyIAccessiblePattern* legacy_iaccessible_pattern;
114  legacy_iaccessible_pattern->GetCurrentPatternAs(
115      UIA_LegacyIAccessiblePatternId,
116      IID_PPV_ARGS(&legacy_iaccessible_pattern));
117
118  // Retrieve the IAccessible element from button UIA element.
119  IAccessible* button_iaccessible;
120  legacy_iaccessible_pattern->GetIAccessible(&iaccessible);
121
122  // Use QueryService to retrieve button's IAccessible2 element from IAccessible
123  // element.
124  IServiceProvider* service_provider;
125  IAccessible2* button_ia2;
126  if (SUCCEEDED(button_iaccessible->QueryInterface(
127          IID_PPV_ARGS(&service_provider)))) {
128    service_provider->QueryService(
129        IID_PPV_ARGS(&button_ia2 /* final result */));
130  }
131~~~
132
133## Docs & References:
134[Custom UIA Property and Pattern registration in Chromium](https://chromium.googlesource.com/chromium/src/+/master/ui/accessibility/platform/uia_registrar_win.h)
135
136[UI Automation IItemContainerPattern. It is used to look up IAccessible2 element
137via a unique id](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/nn-uiautomationclient-iuiautomationitemcontainerpattern)
138
139[UI Automation Client](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationclient/)
140