2 * Copyright 2023 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"
23 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation
);
26 * Global interface table helper functions.
28 static HRESULT
get_global_interface_table(IGlobalInterfaceTable
**git
)
32 hr
= CoCreateInstance(&CLSID_StdGlobalInterfaceTable
, NULL
,
33 CLSCTX_INPROC_SERVER
, &IID_IGlobalInterfaceTable
, (void **)git
);
35 WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr
);
40 HRESULT
register_interface_in_git(IUnknown
*iface
, REFIID riid
, DWORD
*ret_cookie
)
42 IGlobalInterfaceTable
*git
;
47 hr
= get_global_interface_table(&git
);
51 hr
= IGlobalInterfaceTable_RegisterInterfaceInGlobal(git
, iface
, riid
, &git_cookie
);
54 WARN("Failed to register interface in GlobalInterfaceTable, hr %#lx\n", hr
);
58 *ret_cookie
= git_cookie
;
63 HRESULT
unregister_interface_in_git(DWORD git_cookie
)
65 IGlobalInterfaceTable
*git
;
68 hr
= get_global_interface_table(&git
);
72 hr
= IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git
, git_cookie
);
74 WARN("Failed to revoke interface from GlobalInterfaceTable, hr %#lx\n", hr
);
79 HRESULT
get_interface_in_git(REFIID riid
, DWORD git_cookie
, IUnknown
**ret_iface
)
81 IGlobalInterfaceTable
*git
;
85 hr
= get_global_interface_table(&git
);
89 hr
= IGlobalInterfaceTable_GetInterfaceFromGlobal(git
, git_cookie
, riid
, (void **)&iface
);
92 ERR("Failed to get interface from Global Interface Table, hr %#lx\n", hr
);
101 #define UIA_RUNTIME_ID_PREFIX 42
102 HRESULT
write_runtime_id_base(SAFEARRAY
*sa
, HWND hwnd
)
104 const int rt_id
[2] = { UIA_RUNTIME_ID_PREFIX
, HandleToUlong(hwnd
) };
108 for (idx
= 0; idx
< ARRAY_SIZE(rt_id
); idx
++)
110 hr
= SafeArrayPutElement(sa
, &idx
, (void *)&rt_id
[idx
]);
119 * UiaCondition cloning functions.
121 static void uia_condition_destroy(struct UiaCondition
*cond
)
126 switch (cond
->ConditionType
)
128 case ConditionType_Property
:
130 struct UiaPropertyCondition
*prop_cond
= (struct UiaPropertyCondition
*)cond
;
132 VariantClear(&prop_cond
->Value
);
136 case ConditionType_Not
:
138 struct UiaNotCondition
*not_cond
= (struct UiaNotCondition
*)cond
;
140 uia_condition_destroy(not_cond
->pConditions
);
144 case ConditionType_And
:
145 case ConditionType_Or
:
147 struct UiaAndOrCondition
*and_or_cond
= (struct UiaAndOrCondition
*)cond
;
150 for (i
= 0; i
< and_or_cond
->cConditions
; i
++)
151 uia_condition_destroy(and_or_cond
->ppConditions
[i
]);
152 free(and_or_cond
->ppConditions
);
163 static HRESULT
uia_condition_clone(struct UiaCondition
**dst
, struct UiaCondition
*src
)
168 switch (src
->ConditionType
)
170 case ConditionType_True
:
171 case ConditionType_False
:
172 if (!(*dst
= calloc(1, sizeof(**dst
))))
173 return E_OUTOFMEMORY
;
175 (*dst
)->ConditionType
= src
->ConditionType
;
178 case ConditionType_Property
:
180 struct UiaPropertyCondition
*prop_cond
= calloc(1, sizeof(*prop_cond
));
181 struct UiaPropertyCondition
*src_cond
= (struct UiaPropertyCondition
*)src
;
184 return E_OUTOFMEMORY
;
186 *dst
= (struct UiaCondition
*)prop_cond
;
187 prop_cond
->ConditionType
= ConditionType_Property
;
188 prop_cond
->PropertyId
= src_cond
->PropertyId
;
189 prop_cond
->Flags
= src_cond
->Flags
;
190 VariantInit(&prop_cond
->Value
);
191 hr
= VariantCopy(&prop_cond
->Value
, &src_cond
->Value
);
195 case ConditionType_Not
:
197 struct UiaNotCondition
*not_cond
= calloc(1, sizeof(*not_cond
));
198 struct UiaNotCondition
*src_cond
= (struct UiaNotCondition
*)src
;
201 return E_OUTOFMEMORY
;
203 *dst
= (struct UiaCondition
*)not_cond
;
204 not_cond
->ConditionType
= ConditionType_Not
;
205 hr
= uia_condition_clone(¬_cond
->pConditions
, src_cond
->pConditions
);
209 case ConditionType_And
:
210 case ConditionType_Or
:
212 struct UiaAndOrCondition
*and_or_cond
= calloc(1, sizeof(*and_or_cond
));
213 struct UiaAndOrCondition
*src_cond
= (struct UiaAndOrCondition
*)src
;
217 return E_OUTOFMEMORY
;
219 *dst
= (struct UiaCondition
*)and_or_cond
;
220 and_or_cond
->ConditionType
= src_cond
->ConditionType
;
221 and_or_cond
->ppConditions
= calloc(src_cond
->cConditions
, sizeof(*and_or_cond
->ppConditions
));
222 if (!and_or_cond
->ppConditions
)
228 and_or_cond
->cConditions
= src_cond
->cConditions
;
229 for (i
= 0; i
< src_cond
->cConditions
; i
++)
231 hr
= uia_condition_clone(&and_or_cond
->ppConditions
[i
], src_cond
->ppConditions
[i
]);
240 WARN("Tried to clone condition with invalid type %d\n", src
->ConditionType
);
247 uia_condition_destroy(*dst
);
255 * UiaCacheRequest cloning functions.
257 void uia_cache_request_destroy(struct UiaCacheRequest
*cache_req
)
259 uia_condition_destroy(cache_req
->pViewCondition
);
260 free(cache_req
->pProperties
);
261 free(cache_req
->pPatterns
);
264 HRESULT
uia_cache_request_clone(struct UiaCacheRequest
*dst
, struct UiaCacheRequest
*src
)
268 hr
= uia_condition_clone(&dst
->pViewCondition
, src
->pViewCondition
);
272 dst
->Scope
= src
->Scope
;
273 dst
->automationElementMode
= src
->automationElementMode
;
274 if (src
->cProperties
)
276 if (!(dst
->pProperties
= calloc(src
->cProperties
, sizeof(*dst
->pProperties
))))
278 uia_cache_request_destroy(dst
);
279 return E_OUTOFMEMORY
;
282 dst
->cProperties
= src
->cProperties
;
283 memcpy(dst
->pProperties
, src
->pProperties
, sizeof(*dst
->pProperties
) * dst
->cProperties
);
288 if (!(dst
->pPatterns
= calloc(src
->cPatterns
, sizeof(*dst
->pPatterns
))))
290 uia_cache_request_destroy(dst
);
291 return E_OUTOFMEMORY
;
294 dst
->cPatterns
= src
->cPatterns
;
295 memcpy(dst
->pPatterns
, src
->pPatterns
, sizeof(*dst
->pPatterns
) * dst
->cPatterns
);
301 HRESULT
get_safearray_dim_bounds(SAFEARRAY
*sa
, UINT dim
, LONG
*lbound
, LONG
*elems
)
306 *lbound
= *elems
= 0;
307 hr
= SafeArrayGetLBound(sa
, dim
, lbound
);
311 hr
= SafeArrayGetUBound(sa
, dim
, &ubound
);
315 *elems
= (ubound
- (*lbound
)) + 1;
319 HRESULT
get_safearray_bounds(SAFEARRAY
*sa
, LONG
*lbound
, LONG
*elems
)
323 *lbound
= *elems
= 0;
324 dims
= SafeArrayGetDim(sa
);
327 WARN("Invalid dimensions %d for safearray.\n", dims
);
331 return get_safearray_dim_bounds(sa
, 1, lbound
, elems
);
334 int uia_compare_safearrays(SAFEARRAY
*sa1
, SAFEARRAY
*sa2
, int prop_type
)
336 LONG i
, idx
, lbound
[2], elems
[2];
340 hr
= get_safearray_bounds(sa1
, &lbound
[0], &elems
[0]);
343 ERR("Failed to get safearray bounds from sa1 with hr %#lx\n", hr
);
347 hr
= get_safearray_bounds(sa2
, &lbound
[1], &elems
[1]);
350 ERR("Failed to get safearray bounds from sa2 with hr %#lx\n", hr
);
354 if (elems
[0] != elems
[1])
355 return (elems
[0] > elems
[1]) - (elems
[0] < elems
[1]);
357 if (prop_type
!= UIAutomationType_IntArray
)
359 FIXME("Array type %#x value comparison currently unimplemented.\n", prop_type
);
363 for (i
= 0; i
< elems
[0]; i
++)
366 hr
= SafeArrayGetElement(sa1
, &idx
, &val
[0]);
369 ERR("Failed to get element from sa1 with hr %#lx\n", hr
);
374 hr
= SafeArrayGetElement(sa2
, &idx
, &val
[1]);
377 ERR("Failed to get element from sa2 with hr %#lx\n", hr
);
381 if (val
[0] != val
[1])
382 return (val
[0] > val
[1]) - (val
[0] < val
[1]);
389 * HWND related helper functions.
391 BOOL
uia_hwnd_is_visible(HWND hwnd
)
395 if (!IsWindowVisible(hwnd
))
398 if (!GetWindowRect(hwnd
, &rect
))
401 if ((rect
.right
- rect
.left
) <= 0 || (rect
.bottom
- rect
.top
) <= 0)
407 BOOL
uia_is_top_level_hwnd(HWND hwnd
)
409 return GetAncestor(hwnd
, GA_PARENT
) == GetDesktopWindow();
413 * rbtree to efficiently store a collection of HWNDs.
415 struct uia_hwnd_map_entry
417 struct rb_entry entry
;
421 static int uia_hwnd_map_hwnd_compare(const void *key
, const struct rb_entry
*entry
)
423 struct uia_hwnd_map_entry
*hwnd_entry
= RB_ENTRY_VALUE(entry
, struct uia_hwnd_map_entry
, entry
);
424 HWND hwnd
= (HWND
)key
;
426 return (hwnd_entry
->hwnd
> hwnd
) - (hwnd_entry
->hwnd
< hwnd
);
429 static void uia_hwnd_map_free(struct rb_entry
*entry
, void *context
)
431 struct uia_hwnd_map_entry
*hwnd_entry
= RB_ENTRY_VALUE(entry
, struct uia_hwnd_map_entry
, entry
);
433 TRACE("Removing hwnd %p from map %p\n", hwnd_entry
->hwnd
, context
);
437 BOOL
uia_hwnd_map_check_hwnd(struct rb_tree
*hwnd_map
, HWND hwnd
)
439 return !!rb_get(hwnd_map
, hwnd
);
442 HRESULT
uia_hwnd_map_add_hwnd(struct rb_tree
*hwnd_map
, HWND hwnd
)
444 struct uia_hwnd_map_entry
*entry
;
446 if (uia_hwnd_map_check_hwnd(hwnd_map
, hwnd
))
448 TRACE("hwnd %p already in map %p\n", hwnd
, hwnd_map
);
452 if (!(entry
= calloc(1, sizeof(*entry
))))
453 return E_OUTOFMEMORY
;
455 TRACE("Adding hwnd %p to map %p\n", hwnd
, hwnd_map
);
457 rb_put(hwnd_map
, hwnd
, &entry
->entry
);
462 void uia_hwnd_map_init(struct rb_tree
*hwnd_map
)
464 rb_init(hwnd_map
, uia_hwnd_map_hwnd_compare
);
467 void uia_hwnd_map_destroy(struct rb_tree
*hwnd_map
)
469 rb_destroy(hwnd_map
, uia_hwnd_map_free
, hwnd_map
);