uiautomationcore: Retrieve HWND from HUIANODE provider.
[wine.git] / dlls / uiautomationcore / uia_client.c
blobb7e8f45636ac96a9dc633337337e48f82537fc14
1 /*
2 * Copyright 2022 Connor McAdams for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "uia_private.h"
21 #include "wine/debug.h"
22 #include "wine/heap.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
26 static void clear_uia_node_ptr_safearray(SAFEARRAY *sa, LONG elems)
28 HUIANODE node;
29 HRESULT hr;
30 LONG i;
32 for (i = 0; i < elems; i++)
34 hr = SafeArrayGetElement(sa, &i, &node);
35 if (FAILED(hr))
36 break;
37 UiaNodeRelease(node);
41 static void create_uia_node_safearray(VARIANT *in, VARIANT *out)
43 LONG i, idx, lbound, ubound, elems;
44 HUIANODE node;
45 SAFEARRAY *sa;
46 HRESULT hr;
47 UINT dims;
49 dims = SafeArrayGetDim(V_ARRAY(in));
50 if (!dims || (dims > 1))
52 WARN("Invalid dimensions %d for element safearray.\n", dims);
53 return;
56 hr = SafeArrayGetLBound(V_ARRAY(in), 1, &lbound);
57 if (FAILED(hr))
58 return;
60 hr = SafeArrayGetUBound(V_ARRAY(in), 1, &ubound);
61 if (FAILED(hr))
62 return;
64 elems = (ubound - lbound) + 1;
65 if (!(sa = SafeArrayCreateVector(VT_UINT_PTR, 0, elems)))
66 return;
68 for (i = 0; i < elems; i++)
70 IRawElementProviderSimple *elprov;
71 IUnknown *unk;
73 idx = lbound + i;
74 hr = SafeArrayGetElement(V_ARRAY(in), &idx, &unk);
75 if (FAILED(hr))
76 break;
78 hr = IUnknown_QueryInterface(unk, &IID_IRawElementProviderSimple, (void **)&elprov);
79 IUnknown_Release(unk);
80 if (FAILED(hr))
81 break;
83 hr = UiaNodeFromProvider(elprov, &node);
84 if (FAILED(hr))
85 break;
87 IRawElementProviderSimple_Release(elprov);
88 hr = SafeArrayPutElement(sa, &i, &node);
89 if (FAILED(hr))
90 break;
93 if (FAILED(hr))
95 clear_uia_node_ptr_safearray(sa, elems);
96 SafeArrayDestroy(sa);
97 return;
100 V_VT(out) = VT_UINT_PTR | VT_ARRAY;
101 V_ARRAY(out) = sa;
104 /* Convert a VT_UINT_PTR SAFEARRAY to VT_UNKNOWN. */
105 static void uia_node_ptr_to_unk_safearray(VARIANT *in)
107 SAFEARRAY *sa = NULL;
108 LONG ubound, i;
109 HUIANODE node;
110 HRESULT hr;
112 hr = SafeArrayGetUBound(V_ARRAY(in), 1, &ubound);
113 if (FAILED(hr))
114 goto exit;
116 if (!(sa = SafeArrayCreateVector(VT_UNKNOWN, 0, ubound + 1)))
118 hr = E_FAIL;
119 goto exit;
122 for (i = 0; i < (ubound + 1); i++)
124 hr = SafeArrayGetElement(V_ARRAY(in), &i, &node);
125 if (FAILED(hr))
126 break;
128 hr = SafeArrayPutElement(sa, &i, node);
129 if (FAILED(hr))
130 break;
132 UiaNodeRelease(node);
135 exit:
136 if (FAILED(hr))
138 clear_uia_node_ptr_safearray(V_ARRAY(in), ubound + 1);
139 if (sa)
140 SafeArrayDestroy(sa);
143 VariantClear(in);
144 if (SUCCEEDED(hr))
146 V_VT(in) = VT_UNKNOWN | VT_ARRAY;
147 V_ARRAY(in) = sa;
151 static HRESULT get_global_interface_table(IGlobalInterfaceTable **git)
153 HRESULT hr;
155 hr = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, NULL,
156 CLSCTX_INPROC_SERVER, &IID_IGlobalInterfaceTable, (void **)git);
157 if (FAILED(hr))
158 WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr);
160 return hr;
163 static HWND get_hwnd_from_provider(IRawElementProviderSimple *elprov)
165 IRawElementProviderSimple *host_prov;
166 HRESULT hr;
167 VARIANT v;
168 HWND hwnd;
170 hwnd = NULL;
171 VariantInit(&v);
172 hr = IRawElementProviderSimple_get_HostRawElementProvider(elprov, &host_prov);
173 if (SUCCEEDED(hr) && host_prov)
175 hr = IRawElementProviderSimple_GetPropertyValue(host_prov, UIA_NativeWindowHandlePropertyId, &v);
176 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
177 hwnd = UlongToHandle(V_I4(&v));
179 VariantClear(&v);
180 IRawElementProviderSimple_Release(host_prov);
183 if (!IsWindow(hwnd))
185 hr = IRawElementProviderSimple_GetPropertyValue(elprov, UIA_NativeWindowHandlePropertyId, &v);
186 if (SUCCEEDED(hr) && (V_VT(&v) == VT_I4))
187 hwnd = UlongToHandle(V_I4(&v));
188 VariantClear(&v);
191 return hwnd;
195 * IWineUiaNode interface.
197 struct uia_node {
198 IWineUiaNode IWineUiaNode_iface;
199 LONG ref;
201 IWineUiaProvider *prov;
202 DWORD git_cookie;
204 HWND hwnd;
207 static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface)
209 return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface);
212 static HRESULT WINAPI uia_node_QueryInterface(IWineUiaNode *iface, REFIID riid, void **ppv)
214 *ppv = NULL;
215 if (IsEqualIID(riid, &IID_IWineUiaNode) || IsEqualIID(riid, &IID_IUnknown))
216 *ppv = iface;
217 else
218 return E_NOINTERFACE;
220 IWineUiaNode_AddRef(iface);
221 return S_OK;
224 static ULONG WINAPI uia_node_AddRef(IWineUiaNode *iface)
226 struct uia_node *node = impl_from_IWineUiaNode(iface);
227 ULONG ref = InterlockedIncrement(&node->ref);
229 TRACE("%p, refcount %ld\n", node, ref);
230 return ref;
233 static ULONG WINAPI uia_node_Release(IWineUiaNode *iface)
235 struct uia_node *node = impl_from_IWineUiaNode(iface);
236 ULONG ref = InterlockedDecrement(&node->ref);
238 TRACE("%p, refcount %ld\n", node, ref);
239 if (!ref)
241 if (node->git_cookie)
243 IGlobalInterfaceTable *git;
244 HRESULT hr;
246 hr = get_global_interface_table(&git);
247 if (SUCCEEDED(hr))
249 hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie);
250 if (FAILED(hr))
251 WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr);
255 IWineUiaProvider_Release(node->prov);
256 heap_free(node);
259 return ref;
262 static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvider **out_prov)
264 struct uia_node *node = impl_from_IWineUiaNode(iface);
266 if (node->git_cookie)
268 IGlobalInterfaceTable *git;
269 IWineUiaProvider *prov;
270 HRESULT hr;
272 hr = get_global_interface_table(&git);
273 if (FAILED(hr))
274 return hr;
276 hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, node->git_cookie,
277 &IID_IWineUiaProvider, (void **)&prov);
278 if (FAILED(hr))
280 ERR("Failed to get provider interface from GlobalInterfaceTable, hr %#lx\n", hr);
281 return hr;
283 *out_prov = prov;
285 else
287 *out_prov = node->prov;
288 IWineUiaProvider_AddRef(node->prov);
291 return S_OK;
294 static const IWineUiaNodeVtbl uia_node_vtbl = {
295 uia_node_QueryInterface,
296 uia_node_AddRef,
297 uia_node_Release,
298 uia_node_get_provider,
301 static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface)
303 if (!iface || (iface->lpVtbl != &uia_node_vtbl))
304 return NULL;
306 return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface);
310 * IWineUiaProvider interface.
312 struct uia_provider {
313 IWineUiaProvider IWineUiaProvider_iface;
314 LONG ref;
316 IRawElementProviderSimple *elprov;
319 static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *iface)
321 return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface);
324 static HRESULT WINAPI uia_provider_QueryInterface(IWineUiaProvider *iface, REFIID riid, void **ppv)
326 *ppv = NULL;
327 if (IsEqualIID(riid, &IID_IWineUiaProvider) || IsEqualIID(riid, &IID_IUnknown))
328 *ppv = iface;
329 else
330 return E_NOINTERFACE;
332 IWineUiaProvider_AddRef(iface);
333 return S_OK;
336 static ULONG WINAPI uia_provider_AddRef(IWineUiaProvider *iface)
338 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
339 ULONG ref = InterlockedIncrement(&prov->ref);
341 TRACE("%p, refcount %ld\n", prov, ref);
342 return ref;
345 static ULONG WINAPI uia_provider_Release(IWineUiaProvider *iface)
347 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
348 ULONG ref = InterlockedDecrement(&prov->ref);
350 TRACE("%p, refcount %ld\n", prov, ref);
351 if (!ref)
353 IRawElementProviderSimple_Release(prov->elprov);
354 heap_free(prov);
357 return ref;
360 static void get_variant_for_node(HUIANODE node, VARIANT *v)
362 #ifdef _WIN64
363 V_VT(v) = VT_I8;
364 V_I8(v) = (UINT64)node;
365 #else
366 V_VT(v) = VT_I4;
367 V_I4(v) = (UINT32)node;
368 #endif
371 static HRESULT WINAPI uia_provider_get_prop_val(IWineUiaProvider *iface,
372 const struct uia_prop_info *prop_info, VARIANT *ret_val)
374 struct uia_provider *prov = impl_from_IWineUiaProvider(iface);
375 HRESULT hr;
376 VARIANT v;
378 TRACE("%p, %p, %p\n", iface, prop_info, ret_val);
380 VariantInit(&v);
381 hr = IRawElementProviderSimple_GetPropertyValue(prov->elprov, prop_info->prop_id, &v);
382 if (FAILED(hr))
383 goto exit;
385 switch (prop_info->type)
387 case UIAutomationType_Int:
388 if (V_VT(&v) != VT_I4)
390 WARN("Invalid vt %d for UIAutomationType_Int\n", V_VT(&v));
391 goto exit;
393 *ret_val = v;
394 break;
396 case UIAutomationType_IntArray:
397 if (V_VT(&v) != (VT_I4 | VT_ARRAY))
399 WARN("Invalid vt %d for UIAutomationType_IntArray\n", V_VT(&v));
400 goto exit;
402 *ret_val = v;
403 break;
405 case UIAutomationType_Double:
406 if (V_VT(&v) != VT_R8)
408 WARN("Invalid vt %d for UIAutomationType_Double\n", V_VT(&v));
409 goto exit;
411 *ret_val = v;
412 break;
414 case UIAutomationType_DoubleArray:
415 if (V_VT(&v) != (VT_R8 | VT_ARRAY))
417 WARN("Invalid vt %d for UIAutomationType_DoubleArray\n", V_VT(&v));
418 goto exit;
420 *ret_val = v;
421 break;
423 case UIAutomationType_Bool:
424 if (V_VT(&v) != VT_BOOL)
426 WARN("Invalid vt %d for UIAutomationType_Bool\n", V_VT(&v));
427 goto exit;
429 *ret_val = v;
430 break;
432 case UIAutomationType_String:
433 if (V_VT(&v) != VT_BSTR)
435 WARN("Invalid vt %d for UIAutomationType_String\n", V_VT(&v));
436 goto exit;
438 *ret_val = v;
439 break;
441 case UIAutomationType_Element:
443 IRawElementProviderSimple *elprov;
444 HUIANODE node;
446 if (V_VT(&v) != VT_UNKNOWN)
448 WARN("Invalid vt %d for UIAutomationType_Element\n", V_VT(&v));
449 goto exit;
452 hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IRawElementProviderSimple,
453 (void **)&elprov);
454 if (FAILED(hr))
455 goto exit;
457 hr = UiaNodeFromProvider(elprov, &node);
458 if (SUCCEEDED(hr))
460 get_variant_for_node(node, ret_val);
461 VariantClear(&v);
462 IRawElementProviderSimple_Release(elprov);
464 break;
467 case UIAutomationType_ElementArray:
468 if (V_VT(&v) != (VT_UNKNOWN | VT_ARRAY))
470 WARN("Invalid vt %d for UIAutomationType_ElementArray\n", V_VT(&v));
471 goto exit;
473 create_uia_node_safearray(&v, ret_val);
474 if (V_VT(ret_val) == (VT_UINT_PTR | VT_ARRAY))
475 VariantClear(&v);
476 break;
478 default:
479 break;
482 exit:
483 if (V_VT(ret_val) == VT_EMPTY)
484 VariantClear(&v);
486 return S_OK;
489 static const IWineUiaProviderVtbl uia_provider_vtbl = {
490 uia_provider_QueryInterface,
491 uia_provider_AddRef,
492 uia_provider_Release,
493 uia_provider_get_prop_val,
496 static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov)
498 static const int supported_prov_opts = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
499 enum ProviderOptions prov_opts;
500 struct uia_provider *prov;
501 HRESULT hr;
503 hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
504 if (FAILED(hr))
505 return hr;
507 if (prov_opts & ~supported_prov_opts)
508 FIXME("Ignoring unsupported ProviderOption(s) %#x\n", prov_opts & ~supported_prov_opts);
510 prov = heap_alloc_zero(sizeof(*prov));
511 if (!prov)
512 return E_OUTOFMEMORY;
514 prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl;
515 prov->elprov = elprov;
516 prov->ref = 1;
517 node->prov = &prov->IWineUiaProvider_iface;
518 node->hwnd = get_hwnd_from_provider(elprov);
521 * If the UseComThreading ProviderOption is specified, all calls to the
522 * provided IRawElementProviderSimple need to respect the apartment type
523 * of the thread that creates the HUIANODE. i.e, if it's created in an
524 * STA, and the HUIANODE is used in an MTA, we need to provide a proxy.
526 if (prov_opts & ProviderOptions_UseComThreading)
528 IGlobalInterfaceTable *git;
530 hr = get_global_interface_table(&git);
531 if (FAILED(hr))
533 heap_free(prov);
534 return hr;
537 hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface,
538 &IID_IWineUiaProvider, &node->git_cookie);
539 if (FAILED(hr))
541 heap_free(prov);
542 return hr;
546 IRawElementProviderSimple_AddRef(elprov);
547 return S_OK;
550 /***********************************************************************
551 * UiaNodeFromProvider (uiautomationcore.@)
553 HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE *huianode)
555 struct uia_node *node;
556 HRESULT hr;
558 TRACE("(%p, %p)\n", elprov, huianode);
560 if (!elprov || !huianode)
561 return E_INVALIDARG;
563 *huianode = NULL;
565 node = heap_alloc_zero(sizeof(*node));
566 if (!node)
567 return E_OUTOFMEMORY;
569 hr = create_wine_uia_provider(node, elprov);
570 if (FAILED(hr))
572 heap_free(node);
573 return hr;
576 node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl;
577 node->ref = 1;
579 *huianode = (void *)&node->IWineUiaNode_iface;
581 return hr;
584 /***********************************************************************
585 * UiaNodeRelease (uiautomationcore.@)
587 BOOL WINAPI UiaNodeRelease(HUIANODE huianode)
589 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
591 TRACE("(%p)\n", huianode);
593 if (!node)
594 return FALSE;
596 IWineUiaNode_Release(&node->IWineUiaNode_iface);
597 return TRUE;
600 /***********************************************************************
601 * UiaGetPropertyValue (uiautomationcore.@)
603 HRESULT WINAPI UiaGetPropertyValue(HUIANODE huianode, PROPERTYID prop_id, VARIANT *out_val)
605 struct uia_node *node = unsafe_impl_from_IWineUiaNode((IWineUiaNode *)huianode);
606 const struct uia_prop_info *prop_info;
607 IWineUiaProvider *prov;
608 HRESULT hr;
609 VARIANT v;
611 TRACE("(%p, %d, %p)\n", huianode, prop_id, out_val);
613 if (!node || !out_val)
614 return E_INVALIDARG;
616 V_VT(out_val) = VT_UNKNOWN;
617 UiaGetReservedNotSupportedValue(&V_UNKNOWN(out_val));
619 prop_info = uia_prop_info_from_id(prop_id);
620 if (!prop_info)
621 return E_INVALIDARG;
623 if (!prop_info->type)
625 FIXME("No type info for prop_id %d\n", prop_id);
626 return E_NOTIMPL;
629 hr = IWineUiaNode_get_provider(&node->IWineUiaNode_iface, &prov);
630 if (FAILED(hr))
631 return hr;
633 VariantInit(&v);
634 hr = IWineUiaProvider_get_prop_val(prov, prop_info, &v);
635 if (SUCCEEDED(hr) && V_VT(&v) != VT_EMPTY)
638 * ElementArray types come back as an array of pointers to prevent the
639 * HUIANODEs from getting marshaled. We need to convert them to
640 * VT_UNKNOWN here.
642 if (prop_info->type == UIAutomationType_ElementArray)
644 uia_node_ptr_to_unk_safearray(&v);
645 if (V_VT(&v) != VT_EMPTY)
646 *out_val = v;
648 else
649 *out_val = v;
652 IWineUiaProvider_Release(prov);
654 return S_OK;