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
)
32 for (i
= 0; i
< elems
; i
++)
34 hr
= SafeArrayGetElement(sa
, &i
, &node
);
41 static void create_uia_node_safearray(VARIANT
*in
, VARIANT
*out
)
43 LONG i
, idx
, lbound
, ubound
, elems
;
49 dims
= SafeArrayGetDim(V_ARRAY(in
));
50 if (!dims
|| (dims
> 1))
52 WARN("Invalid dimensions %d for element safearray.\n", dims
);
56 hr
= SafeArrayGetLBound(V_ARRAY(in
), 1, &lbound
);
60 hr
= SafeArrayGetUBound(V_ARRAY(in
), 1, &ubound
);
64 elems
= (ubound
- lbound
) + 1;
65 if (!(sa
= SafeArrayCreateVector(VT_UINT_PTR
, 0, elems
)))
68 for (i
= 0; i
< elems
; i
++)
70 IRawElementProviderSimple
*elprov
;
74 hr
= SafeArrayGetElement(V_ARRAY(in
), &idx
, &unk
);
78 hr
= IUnknown_QueryInterface(unk
, &IID_IRawElementProviderSimple
, (void **)&elprov
);
79 IUnknown_Release(unk
);
83 hr
= UiaNodeFromProvider(elprov
, &node
);
87 IRawElementProviderSimple_Release(elprov
);
88 hr
= SafeArrayPutElement(sa
, &i
, &node
);
95 clear_uia_node_ptr_safearray(sa
, elems
);
100 V_VT(out
) = VT_UINT_PTR
| VT_ARRAY
;
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
;
112 hr
= SafeArrayGetUBound(V_ARRAY(in
), 1, &ubound
);
116 if (!(sa
= SafeArrayCreateVector(VT_UNKNOWN
, 0, ubound
+ 1)))
122 for (i
= 0; i
< (ubound
+ 1); i
++)
124 hr
= SafeArrayGetElement(V_ARRAY(in
), &i
, &node
);
128 hr
= SafeArrayPutElement(sa
, &i
, node
);
132 UiaNodeRelease(node
);
138 clear_uia_node_ptr_safearray(V_ARRAY(in
), ubound
+ 1);
140 SafeArrayDestroy(sa
);
146 V_VT(in
) = VT_UNKNOWN
| VT_ARRAY
;
151 static HRESULT
get_global_interface_table(IGlobalInterfaceTable
**git
)
155 hr
= CoCreateInstance(&CLSID_StdGlobalInterfaceTable
, NULL
,
156 CLSCTX_INPROC_SERVER
, &IID_IGlobalInterfaceTable
, (void **)git
);
158 WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr
);
163 static HWND
get_hwnd_from_provider(IRawElementProviderSimple
*elprov
)
165 IRawElementProviderSimple
*host_prov
;
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
));
180 IRawElementProviderSimple_Release(host_prov
);
185 hr
= IRawElementProviderSimple_GetPropertyValue(elprov
, UIA_NativeWindowHandlePropertyId
, &v
);
186 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
187 hwnd
= UlongToHandle(V_I4(&v
));
195 * IWineUiaNode interface.
198 IWineUiaNode IWineUiaNode_iface
;
201 IWineUiaProvider
*prov
;
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
)
215 if (IsEqualIID(riid
, &IID_IWineUiaNode
) || IsEqualIID(riid
, &IID_IUnknown
))
218 return E_NOINTERFACE
;
220 IWineUiaNode_AddRef(iface
);
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
);
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
);
241 if (node
->git_cookie
)
243 IGlobalInterfaceTable
*git
;
246 hr
= get_global_interface_table(&git
);
249 hr
= IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git
, node
->git_cookie
);
251 WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr
);
255 IWineUiaProvider_Release(node
->prov
);
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
;
272 hr
= get_global_interface_table(&git
);
276 hr
= IGlobalInterfaceTable_GetInterfaceFromGlobal(git
, node
->git_cookie
,
277 &IID_IWineUiaProvider
, (void **)&prov
);
280 ERR("Failed to get provider interface from GlobalInterfaceTable, hr %#lx\n", hr
);
287 *out_prov
= node
->prov
;
288 IWineUiaProvider_AddRef(node
->prov
);
294 static const IWineUiaNodeVtbl uia_node_vtbl
= {
295 uia_node_QueryInterface
,
298 uia_node_get_provider
,
301 static struct uia_node
*unsafe_impl_from_IWineUiaNode(IWineUiaNode
*iface
)
303 if (!iface
|| (iface
->lpVtbl
!= &uia_node_vtbl
))
306 return CONTAINING_RECORD(iface
, struct uia_node
, IWineUiaNode_iface
);
310 * IWineUiaProvider interface.
312 struct uia_provider
{
313 IWineUiaProvider IWineUiaProvider_iface
;
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
)
327 if (IsEqualIID(riid
, &IID_IWineUiaProvider
) || IsEqualIID(riid
, &IID_IUnknown
))
330 return E_NOINTERFACE
;
332 IWineUiaProvider_AddRef(iface
);
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
);
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
);
353 IRawElementProviderSimple_Release(prov
->elprov
);
360 static void get_variant_for_node(HUIANODE node
, VARIANT
*v
)
364 V_I8(v
) = (UINT64
)node
;
367 V_I4(v
) = (UINT32
)node
;
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
);
378 TRACE("%p, %p, %p\n", iface
, prop_info
, ret_val
);
381 hr
= IRawElementProviderSimple_GetPropertyValue(prov
->elprov
, prop_info
->prop_id
, &v
);
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
));
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
));
405 case UIAutomationType_Double
:
406 if (V_VT(&v
) != VT_R8
)
408 WARN("Invalid vt %d for UIAutomationType_Double\n", V_VT(&v
));
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
));
423 case UIAutomationType_Bool
:
424 if (V_VT(&v
) != VT_BOOL
)
426 WARN("Invalid vt %d for UIAutomationType_Bool\n", V_VT(&v
));
432 case UIAutomationType_String
:
433 if (V_VT(&v
) != VT_BSTR
)
435 WARN("Invalid vt %d for UIAutomationType_String\n", V_VT(&v
));
441 case UIAutomationType_Element
:
443 IRawElementProviderSimple
*elprov
;
446 if (V_VT(&v
) != VT_UNKNOWN
)
448 WARN("Invalid vt %d for UIAutomationType_Element\n", V_VT(&v
));
452 hr
= IUnknown_QueryInterface(V_UNKNOWN(&v
), &IID_IRawElementProviderSimple
,
457 hr
= UiaNodeFromProvider(elprov
, &node
);
460 get_variant_for_node(node
, ret_val
);
462 IRawElementProviderSimple_Release(elprov
);
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
));
473 create_uia_node_safearray(&v
, ret_val
);
474 if (V_VT(ret_val
) == (VT_UINT_PTR
| VT_ARRAY
))
483 if (V_VT(ret_val
) == VT_EMPTY
)
489 static const IWineUiaProviderVtbl uia_provider_vtbl
= {
490 uia_provider_QueryInterface
,
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
;
503 hr
= IRawElementProviderSimple_get_ProviderOptions(elprov
, &prov_opts
);
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
));
512 return E_OUTOFMEMORY
;
514 prov
->IWineUiaProvider_iface
.lpVtbl
= &uia_provider_vtbl
;
515 prov
->elprov
= elprov
;
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
);
537 hr
= IGlobalInterfaceTable_RegisterInterfaceInGlobal(git
, (IUnknown
*)&prov
->IWineUiaProvider_iface
,
538 &IID_IWineUiaProvider
, &node
->git_cookie
);
546 IRawElementProviderSimple_AddRef(elprov
);
550 /***********************************************************************
551 * UiaNodeFromProvider (uiautomationcore.@)
553 HRESULT WINAPI
UiaNodeFromProvider(IRawElementProviderSimple
*elprov
, HUIANODE
*huianode
)
555 struct uia_node
*node
;
558 TRACE("(%p, %p)\n", elprov
, huianode
);
560 if (!elprov
|| !huianode
)
565 node
= heap_alloc_zero(sizeof(*node
));
567 return E_OUTOFMEMORY
;
569 hr
= create_wine_uia_provider(node
, elprov
);
576 node
->IWineUiaNode_iface
.lpVtbl
= &uia_node_vtbl
;
579 *huianode
= (void *)&node
->IWineUiaNode_iface
;
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
);
596 IWineUiaNode_Release(&node
->IWineUiaNode_iface
);
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
;
611 TRACE("(%p, %d, %p)\n", huianode
, prop_id
, out_val
);
613 if (!node
|| !out_val
)
616 V_VT(out_val
) = VT_UNKNOWN
;
617 UiaGetReservedNotSupportedValue(&V_UNKNOWN(out_val
));
619 prop_info
= uia_prop_info_from_id(prop_id
);
623 if (!prop_info
->type
)
625 FIXME("No type info for prop_id %d\n", prop_id
);
629 hr
= IWineUiaNode_get_provider(&node
->IWineUiaNode_iface
, &prov
);
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
642 if (prop_info
->type
== UIAutomationType_ElementArray
)
644 uia_node_ptr_to_unk_safearray(&v
);
645 if (V_VT(&v
) != VT_EMPTY
)
652 IWineUiaProvider_Release(prov
);