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"
22 #include "wine/debug.h"
24 #include "wine/iaccessible2.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation
);
28 DEFINE_GUID(SID_AccFromDAWrapper
, 0x33f139ee, 0xe509, 0x47f7, 0xbf,0x39, 0x83,0x76,0x44,0xf7,0x45,0x76);
30 static BOOL
msaa_check_acc_state(IAccessible
*acc
, VARIANT cid
, ULONG flag
)
36 hr
= IAccessible_get_accState(acc
, cid
, &v
);
37 if (SUCCEEDED(hr
) && V_VT(&v
) == VT_I4
&& (V_I4(&v
) & flag
))
43 static IAccessible2
*msaa_acc_get_ia2(IAccessible
*acc
)
45 IServiceProvider
*serv_prov
;
46 IAccessible2
*ia2
= NULL
;
49 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void **)&serv_prov
);
52 hr
= IServiceProvider_QueryService(serv_prov
, &IID_IAccessible2
, &IID_IAccessible2
, (void **)&ia2
);
53 IServiceProvider_Release(serv_prov
);
54 if (SUCCEEDED(hr
) && ia2
)
58 hr
= IAccessible_QueryInterface(acc
, &IID_IAccessible2
, (void **)&ia2
);
59 if (SUCCEEDED(hr
) && ia2
)
65 static IAccessible
*msaa_acc_da_unwrap(IAccessible
*acc
)
71 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void**)&sp
);
74 hr
= IServiceProvider_QueryService(sp
, &SID_AccFromDAWrapper
, &IID_IAccessible
, (void**)&acc2
);
75 IServiceProvider_Release(sp
);
78 if (SUCCEEDED(hr
) && acc2
)
81 IAccessible_AddRef(acc
);
86 * Compare role, state, child count, and location properties of the two
87 * IAccessibles. If all four are successfully retrieved and are equal, this is
90 static HRESULT
msaa_acc_prop_match(IAccessible
*acc
, IAccessible
*acc2
)
92 BOOL role_match
, state_match
, child_count_match
, location_match
;
93 LONG child_count
[2], left
[2], top
[2], width
[2], height
[2];
97 role_match
= state_match
= child_count_match
= location_match
= FALSE
;
98 variant_init_i4(&cid
, CHILDID_SELF
);
99 hr
= IAccessible_get_accRole(acc
, cid
, &v
);
100 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
103 hr
= IAccessible_get_accRole(acc2
, cid
, &v2
);
104 if (SUCCEEDED(hr
) && (V_VT(&v2
) == VT_I4
))
106 if (V_I4(&v
) != V_I4(&v2
))
114 hr
= IAccessible_get_accState(acc
, cid
, &v
);
115 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
118 hr
= IAccessible_get_accState(acc2
, cid
, &v2
);
119 if (SUCCEEDED(hr
) && (V_VT(&v2
) == VT_I4
))
121 if (V_I4(&v
) != V_I4(&v2
))
128 hr
= IAccessible_get_accChildCount(acc
, &child_count
[0]);
129 hr2
= IAccessible_get_accChildCount(acc2
, &child_count
[1]);
130 if (SUCCEEDED(hr
) && SUCCEEDED(hr2
))
132 if (child_count
[0] != child_count
[1])
135 child_count_match
= TRUE
;
138 hr
= IAccessible_accLocation(acc
, &left
[0], &top
[0], &width
[0], &height
[0], cid
);
141 hr
= IAccessible_accLocation(acc2
, &left
[1], &top
[1], &width
[1], &height
[1], cid
);
144 if ((left
[0] != left
[1]) || (top
[0] != top
[1]) || (width
[0] != width
[1]) ||
145 (height
[0] != height
[1]))
148 location_match
= TRUE
;
152 if (role_match
&& state_match
&& child_count_match
&& location_match
)
158 static BOOL
msaa_acc_compare(IAccessible
*acc
, IAccessible
*acc2
)
160 IAccessible2
*ia2
[2] = { NULL
, NULL
};
161 IUnknown
*unk
, *unk2
;
162 BOOL matched
= FALSE
;
168 acc
= msaa_acc_da_unwrap(acc
);
169 acc2
= msaa_acc_da_unwrap(acc2
);
170 IAccessible_QueryInterface(acc
, &IID_IUnknown
, (void**)&unk
);
171 IAccessible_QueryInterface(acc2
, &IID_IUnknown
, (void**)&unk2
);
178 ia2
[0] = msaa_acc_get_ia2(acc
);
179 ia2
[1] = msaa_acc_get_ia2(acc2
);
180 if (!ia2
[0] != !ia2
[1])
184 hr
= IAccessible2_get_uniqueID(ia2
[0], &unique_id
[0]);
187 hr
= IAccessible2_get_uniqueID(ia2
[1], &unique_id
[1]);
190 if (unique_id
[0] == unique_id
[1])
198 hr
= msaa_acc_prop_match(acc
, acc2
);
204 variant_init_i4(&cid
, CHILDID_SELF
);
205 hr
= IAccessible_get_accName(acc
, cid
, &name
[0]);
208 hr
= IAccessible_get_accName(acc2
, cid
, &name
[1]);
211 if (!name
[0] && !name
[1])
213 else if (!name
[0] || !name
[1])
217 if (!wcscmp(name
[0], name
[1]))
223 SysFreeString(name
[1]);
226 SysFreeString(name
[0]);
230 IUnknown_Release(unk
);
231 IUnknown_Release(unk2
);
232 IAccessible_Release(acc
);
233 IAccessible_Release(acc2
);
235 IAccessible2_Release(ia2
[0]);
237 IAccessible2_Release(ia2
[1]);
242 static HRESULT
msaa_acc_get_parent(IAccessible
*acc
, IAccessible
**parent
)
244 IDispatch
*disp
= NULL
;
248 hr
= IAccessible_get_accParent(acc
, &disp
);
249 if (FAILED(hr
) || !disp
)
252 hr
= IDispatch_QueryInterface(disp
, &IID_IAccessible
, (void**)parent
);
253 IDispatch_Release(disp
);
257 #define DIR_FORWARD 0
258 #define DIR_REVERSE 1
259 static HRESULT
msaa_acc_get_next_child(IAccessible
*acc
, LONG start_pos
, LONG direction
,
260 IAccessible
**child
, LONG
*child_id
, LONG
*child_pos
, BOOL check_visible
)
262 LONG child_count
, cur_pos
;
272 hr
= IAccessible_get_accChildCount(acc
, &child_count
);
273 if (FAILED(hr
) || (cur_pos
> child_count
))
276 variant_init_i4(&cid
, cur_pos
);
277 hr
= IAccessible_get_accChild(acc
, cid
, &disp
);
283 if (!check_visible
|| !msaa_check_acc_state(acc
, cid
, STATE_SYSTEM_INVISIBLE
))
286 *child_id
= *child_pos
= cur_pos
;
287 IAccessible_AddRef(acc
);
293 IAccessible
*acc_child
= NULL
;
295 hr
= IDispatch_QueryInterface(disp
, &IID_IAccessible
, (void **)&acc_child
);
296 IDispatch_Release(disp
);
300 variant_init_i4(&cid
, CHILDID_SELF
);
301 if (!check_visible
|| !msaa_check_acc_state(acc_child
, cid
, STATE_SYSTEM_INVISIBLE
))
304 *child_id
= CHILDID_SELF
;
305 *child_pos
= cur_pos
;
309 IAccessible_Release(acc_child
);
312 if (direction
== DIR_FORWARD
)
317 if ((cur_pos
> child_count
) || (cur_pos
<= 0))
324 static HRESULT
msaa_acc_get_child_pos(IAccessible
*acc
, IAccessible
**out_parent
,
327 LONG child_count
, child_id
, child_pos
, match_pos
;
328 IAccessible
*child
, *parent
, *match
, **children
;
334 hr
= msaa_acc_get_parent(acc
, &parent
);
335 if (FAILED(hr
) || !parent
)
338 hr
= IAccessible_get_accChildCount(parent
, &child_count
);
339 if (FAILED(hr
) || !child_count
)
341 IAccessible_Release(parent
);
345 children
= calloc(child_count
, sizeof(*children
));
347 return E_OUTOFMEMORY
;
350 for (i
= 0; i
< child_count
; i
++)
352 hr
= msaa_acc_get_next_child(parent
, i
+ 1, DIR_FORWARD
, &child
, &child_id
, &child_pos
, FALSE
);
353 if (FAILED(hr
) || !child
)
359 IAccessible_Release(child
);
362 for (i
= 0; i
< child_count
; i
++)
367 if (msaa_acc_compare(acc
, children
[i
]))
374 /* Can't have more than one IAccessible match. */
387 *out_parent
= parent
;
388 *out_child_pos
= match_pos
;
391 IAccessible_Release(parent
);
393 for (i
= 0; i
< child_count
; i
++)
396 IAccessible_Release(children
[i
]);
404 static LONG
msaa_role_to_uia_control_type(LONG role
)
408 case ROLE_SYSTEM_TITLEBAR
: return UIA_TitleBarControlTypeId
;
409 case ROLE_SYSTEM_MENUBAR
: return UIA_MenuBarControlTypeId
;
410 case ROLE_SYSTEM_SCROLLBAR
: return UIA_ScrollBarControlTypeId
;
411 case ROLE_SYSTEM_INDICATOR
:
412 case ROLE_SYSTEM_GRIP
: return UIA_ThumbControlTypeId
;
413 case ROLE_SYSTEM_APPLICATION
:
414 case ROLE_SYSTEM_WINDOW
: return UIA_WindowControlTypeId
;
415 case ROLE_SYSTEM_MENUPOPUP
: return UIA_MenuControlTypeId
;
416 case ROLE_SYSTEM_TOOLTIP
: return UIA_ToolTipControlTypeId
;
417 case ROLE_SYSTEM_DOCUMENT
: return UIA_DocumentControlTypeId
;
418 case ROLE_SYSTEM_PANE
: return UIA_PaneControlTypeId
;
419 case ROLE_SYSTEM_GROUPING
: return UIA_GroupControlTypeId
;
420 case ROLE_SYSTEM_SEPARATOR
: return UIA_SeparatorControlTypeId
;
421 case ROLE_SYSTEM_TOOLBAR
: return UIA_ToolBarControlTypeId
;
422 case ROLE_SYSTEM_STATUSBAR
: return UIA_StatusBarControlTypeId
;
423 case ROLE_SYSTEM_TABLE
: return UIA_TableControlTypeId
;
424 case ROLE_SYSTEM_COLUMNHEADER
:
425 case ROLE_SYSTEM_ROWHEADER
: return UIA_HeaderControlTypeId
;
426 case ROLE_SYSTEM_CELL
: return UIA_DataItemControlTypeId
;
427 case ROLE_SYSTEM_LINK
: return UIA_HyperlinkControlTypeId
;
428 case ROLE_SYSTEM_LIST
: return UIA_ListControlTypeId
;
429 case ROLE_SYSTEM_LISTITEM
: return UIA_ListItemControlTypeId
;
430 case ROLE_SYSTEM_OUTLINE
: return UIA_TreeControlTypeId
;
431 case ROLE_SYSTEM_OUTLINEITEM
: return UIA_TreeItemControlTypeId
;
432 case ROLE_SYSTEM_PAGETAB
: return UIA_TabItemControlTypeId
;
433 case ROLE_SYSTEM_GRAPHIC
: return UIA_ImageControlTypeId
;
434 case ROLE_SYSTEM_STATICTEXT
: return UIA_TextControlTypeId
;
435 case ROLE_SYSTEM_TEXT
: return UIA_EditControlTypeId
;
436 case ROLE_SYSTEM_CLOCK
:
437 case ROLE_SYSTEM_BUTTONDROPDOWNGRID
:
438 case ROLE_SYSTEM_PUSHBUTTON
: return UIA_ButtonControlTypeId
;
439 case ROLE_SYSTEM_CHECKBUTTON
: return UIA_CheckBoxControlTypeId
;
440 case ROLE_SYSTEM_RADIOBUTTON
: return UIA_RadioButtonControlTypeId
;
441 case ROLE_SYSTEM_COMBOBOX
: return UIA_ComboBoxControlTypeId
;
442 case ROLE_SYSTEM_PROGRESSBAR
: return UIA_ProgressBarControlTypeId
;
443 case ROLE_SYSTEM_SLIDER
: return UIA_SliderControlTypeId
;
444 case ROLE_SYSTEM_SPINBUTTON
: return UIA_SpinnerControlTypeId
;
445 case ROLE_SYSTEM_BUTTONMENU
:
446 case ROLE_SYSTEM_MENUITEM
: return UIA_MenuItemControlTypeId
;
447 case ROLE_SYSTEM_PAGETABLIST
: return UIA_TabControlTypeId
;
448 case ROLE_SYSTEM_BUTTONDROPDOWN
:
449 case ROLE_SYSTEM_SPLITBUTTON
: return UIA_SplitButtonControlTypeId
;
450 case ROLE_SYSTEM_SOUND
:
451 case ROLE_SYSTEM_CURSOR
:
452 case ROLE_SYSTEM_CARET
:
453 case ROLE_SYSTEM_ALERT
:
454 case ROLE_SYSTEM_CLIENT
:
455 case ROLE_SYSTEM_CHART
:
456 case ROLE_SYSTEM_DIALOG
:
457 case ROLE_SYSTEM_BORDER
:
458 case ROLE_SYSTEM_COLUMN
:
459 case ROLE_SYSTEM_ROW
:
460 case ROLE_SYSTEM_HELPBALLOON
:
461 case ROLE_SYSTEM_CHARACTER
:
462 case ROLE_SYSTEM_PROPERTYPAGE
:
463 case ROLE_SYSTEM_DROPLIST
:
464 case ROLE_SYSTEM_DIAL
:
465 case ROLE_SYSTEM_HOTKEYFIELD
:
466 case ROLE_SYSTEM_DIAGRAM
:
467 case ROLE_SYSTEM_ANIMATION
:
468 case ROLE_SYSTEM_EQUATION
:
469 case ROLE_SYSTEM_WHITESPACE
:
470 case ROLE_SYSTEM_IPADDRESS
:
471 case ROLE_SYSTEM_OUTLINEBUTTON
:
472 WARN("No UIA control type mapping for MSAA role %ld\n", role
);
476 FIXME("UIA control type mapping unimplemented for MSAA role %ld\n", role
);
484 * UiaProviderFromIAccessible IRawElementProviderSimple interface.
486 struct msaa_provider
{
487 IRawElementProviderSimple IRawElementProviderSimple_iface
;
488 IRawElementProviderFragment IRawElementProviderFragment_iface
;
489 ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface
;
498 BOOL root_acc_check_ran
;
505 static BOOL
msaa_check_root_acc(struct msaa_provider
*msaa_prov
)
510 if (msaa_prov
->root_acc_check_ran
)
511 return msaa_prov
->is_root_acc
;
513 msaa_prov
->root_acc_check_ran
= TRUE
;
514 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
|| msaa_prov
->parent
)
517 hr
= AccessibleObjectFromWindow(msaa_prov
->hwnd
, OBJID_CLIENT
, &IID_IAccessible
, (void **)&acc
);
521 if (msaa_acc_compare(msaa_prov
->acc
, acc
))
522 msaa_prov
->is_root_acc
= TRUE
;
524 IAccessible_Release(acc
);
525 return msaa_prov
->is_root_acc
;
528 static inline struct msaa_provider
*impl_from_msaa_provider(IRawElementProviderSimple
*iface
)
530 return CONTAINING_RECORD(iface
, struct msaa_provider
, IRawElementProviderSimple_iface
);
533 HRESULT WINAPI
msaa_provider_QueryInterface(IRawElementProviderSimple
*iface
, REFIID riid
, void **ppv
)
535 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
538 if (IsEqualIID(riid
, &IID_IRawElementProviderSimple
) || IsEqualIID(riid
, &IID_IUnknown
))
540 else if (IsEqualIID(riid
, &IID_IRawElementProviderFragment
))
541 *ppv
= &msaa_prov
->IRawElementProviderFragment_iface
;
542 else if (IsEqualIID(riid
, &IID_ILegacyIAccessibleProvider
))
543 *ppv
= &msaa_prov
->ILegacyIAccessibleProvider_iface
;
545 return E_NOINTERFACE
;
547 IRawElementProviderSimple_AddRef(iface
);
551 ULONG WINAPI
msaa_provider_AddRef(IRawElementProviderSimple
*iface
)
553 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
554 ULONG refcount
= InterlockedIncrement(&msaa_prov
->refcount
);
556 TRACE("%p, refcount %ld\n", iface
, refcount
);
561 ULONG WINAPI
msaa_provider_Release(IRawElementProviderSimple
*iface
)
563 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
564 ULONG refcount
= InterlockedDecrement(&msaa_prov
->refcount
);
566 TRACE("%p, refcount %ld\n", iface
, refcount
);
570 IAccessible_Release(msaa_prov
->acc
);
571 if (msaa_prov
->parent
)
572 IAccessible_Release(msaa_prov
->parent
);
574 IAccessible2_Release(msaa_prov
->ia2
);
581 HRESULT WINAPI
msaa_provider_get_ProviderOptions(IRawElementProviderSimple
*iface
,
582 enum ProviderOptions
*ret_val
)
584 TRACE("%p, %p\n", iface
, ret_val
);
585 *ret_val
= ProviderOptions_ServerSideProvider
| ProviderOptions_UseComThreading
;
589 HRESULT WINAPI
msaa_provider_GetPatternProvider(IRawElementProviderSimple
*iface
,
590 PATTERNID pattern_id
, IUnknown
**ret_val
)
592 TRACE("%p, %d, %p\n", iface
, pattern_id
, ret_val
);
597 case UIA_LegacyIAccessiblePatternId
:
598 return IRawElementProviderSimple_QueryInterface(iface
, &IID_IUnknown
, (void **)ret_val
);
601 FIXME("Unimplemented patternId %d\n", pattern_id
);
608 HRESULT WINAPI
msaa_provider_GetPropertyValue(IRawElementProviderSimple
*iface
,
609 PROPERTYID prop_id
, VARIANT
*ret_val
)
611 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
615 TRACE("%p, %d, %p\n", iface
, prop_id
, ret_val
);
617 VariantInit(ret_val
);
621 case UIA_ProviderDescriptionPropertyId
:
622 V_VT(ret_val
) = VT_BSTR
;
623 V_BSTR(ret_val
) = SysAllocString(L
"Wine: MSAA Proxy");
626 case UIA_ControlTypePropertyId
:
627 if (!msaa_prov
->control_type
)
629 hr
= IAccessible_get_accRole(msaa_prov
->acc
, msaa_prov
->cid
, &v
);
630 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
631 msaa_prov
->control_type
= msaa_role_to_uia_control_type(V_I4(&v
));
634 if (msaa_prov
->control_type
)
635 variant_init_i4(ret_val
, msaa_prov
->control_type
);
639 case UIA_HasKeyboardFocusPropertyId
:
640 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
641 STATE_SYSTEM_FOCUSED
));
644 case UIA_IsKeyboardFocusablePropertyId
:
645 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
646 STATE_SYSTEM_FOCUSABLE
));
649 case UIA_IsEnabledPropertyId
:
650 variant_init_bool(ret_val
, !msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
651 STATE_SYSTEM_UNAVAILABLE
));
654 case UIA_IsPasswordPropertyId
:
655 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
656 STATE_SYSTEM_PROTECTED
));
659 case UIA_NamePropertyId
:
663 hr
= IAccessible_get_accName(msaa_prov
->acc
, msaa_prov
->cid
, &name
);
664 if (SUCCEEDED(hr
) && name
)
666 V_VT(ret_val
) = VT_BSTR
;
667 V_BSTR(ret_val
) = name
;
672 case UIA_IsOffscreenPropertyId
:
674 RECT rect
[2] = { 0 };
678 variant_init_bool(ret_val
, FALSE
);
679 if (msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
, STATE_SYSTEM_OFFSCREEN
))
681 variant_init_bool(ret_val
, TRUE
);
685 hr
= IAccessible_accLocation(msaa_prov
->acc
, &rect
[0].left
, &rect
[0].top
, &width
, &height
, msaa_prov
->cid
);
689 rect
[0].right
= rect
[0].left
+ width
;
690 rect
[0].bottom
= rect
[0].top
+ height
;
691 SetLastError(NOERROR
);
692 if (!GetClientRect(msaa_prov
->hwnd
, &rect
[1]))
694 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
)
695 variant_init_bool(ret_val
, TRUE
);
699 SetLastError(NOERROR
);
700 if (!MapWindowPoints(msaa_prov
->hwnd
, NULL
, (POINT
*)&rect
[1], 2) && GetLastError())
702 if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE
)
703 variant_init_bool(ret_val
, TRUE
);
707 variant_init_bool(ret_val
, !IntersectRect(&intersect_rect
, &rect
[0], &rect
[1]));
712 FIXME("Unimplemented propertyId %d\n", prop_id
);
719 HRESULT WINAPI
msaa_provider_get_HostRawElementProvider(IRawElementProviderSimple
*iface
,
720 IRawElementProviderSimple
**ret_val
)
722 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
724 TRACE("%p, %p\n", iface
, ret_val
);
727 if (msaa_check_root_acc(msaa_prov
))
728 return UiaHostProviderFromHwnd(msaa_prov
->hwnd
, ret_val
);
733 static const IRawElementProviderSimpleVtbl msaa_provider_vtbl
= {
734 msaa_provider_QueryInterface
,
735 msaa_provider_AddRef
,
736 msaa_provider_Release
,
737 msaa_provider_get_ProviderOptions
,
738 msaa_provider_GetPatternProvider
,
739 msaa_provider_GetPropertyValue
,
740 msaa_provider_get_HostRawElementProvider
,
744 * IRawElementProviderFragment interface for UiaProviderFromIAccessible
747 static inline struct msaa_provider
*impl_from_msaa_fragment(IRawElementProviderFragment
*iface
)
749 return CONTAINING_RECORD(iface
, struct msaa_provider
, IRawElementProviderFragment_iface
);
752 static HRESULT WINAPI
msaa_fragment_QueryInterface(IRawElementProviderFragment
*iface
, REFIID riid
,
755 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
756 return IRawElementProviderSimple_QueryInterface(&msaa_prov
->IRawElementProviderSimple_iface
, riid
, ppv
);
759 static ULONG WINAPI
msaa_fragment_AddRef(IRawElementProviderFragment
*iface
)
761 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
762 return IRawElementProviderSimple_AddRef(&msaa_prov
->IRawElementProviderSimple_iface
);
765 static ULONG WINAPI
msaa_fragment_Release(IRawElementProviderFragment
*iface
)
767 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
768 return IRawElementProviderSimple_Release(&msaa_prov
->IRawElementProviderSimple_iface
);
771 static HRESULT WINAPI
msaa_fragment_Navigate(IRawElementProviderFragment
*iface
,
772 enum NavigateDirection direction
, IRawElementProviderFragment
**ret_val
)
774 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
775 LONG child_count
, child_id
, child_pos
;
776 IRawElementProviderSimple
*elprov
;
780 TRACE("%p, %d, %p\n", iface
, direction
, ret_val
);
785 case NavigateDirection_Parent
:
786 if (msaa_check_root_acc(msaa_prov
))
789 if (V_I4(&msaa_prov
->cid
) == CHILDID_SELF
)
791 hr
= msaa_acc_get_parent(msaa_prov
->acc
, &acc
);
792 if (FAILED(hr
) || !acc
)
796 acc
= msaa_prov
->acc
;
798 hr
= create_msaa_provider(acc
, CHILDID_SELF
, NULL
, FALSE
, &elprov
);
801 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
802 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
805 if (acc
!= msaa_prov
->acc
)
806 IAccessible_Release(acc
);
810 case NavigateDirection_FirstChild
:
811 case NavigateDirection_LastChild
:
812 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
)
815 hr
= IAccessible_get_accChildCount(msaa_prov
->acc
, &child_count
);
816 if (FAILED(hr
) || !child_count
)
819 if (direction
== NavigateDirection_FirstChild
)
820 hr
= msaa_acc_get_next_child(msaa_prov
->acc
, 1, DIR_FORWARD
, &acc
, &child_id
,
823 hr
= msaa_acc_get_next_child(msaa_prov
->acc
, child_count
, DIR_REVERSE
, &acc
, &child_id
,
826 if (FAILED(hr
) || !acc
)
829 hr
= create_msaa_provider(acc
, child_id
, NULL
, FALSE
, &elprov
);
832 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
834 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
835 prov
->parent
= msaa_prov
->acc
;
836 IAccessible_AddRef(msaa_prov
->acc
);
837 if (acc
!= msaa_prov
->acc
)
838 prov
->child_pos
= child_pos
;
840 prov
->child_pos
= child_id
;
842 IAccessible_Release(acc
);
846 case NavigateDirection_NextSibling
:
847 case NavigateDirection_PreviousSibling
:
848 if (msaa_check_root_acc(msaa_prov
))
851 if (!msaa_prov
->parent
)
853 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
)
855 msaa_prov
->parent
= msaa_prov
->acc
;
856 IAccessible_AddRef(msaa_prov
->acc
);
857 msaa_prov
->child_pos
= V_I4(&msaa_prov
->cid
);
861 hr
= msaa_acc_get_child_pos(msaa_prov
->acc
, &acc
, &child_pos
);
862 if (FAILED(hr
) || !acc
)
864 msaa_prov
->parent
= acc
;
865 msaa_prov
->child_pos
= child_pos
;
869 if (direction
== NavigateDirection_NextSibling
)
870 hr
= msaa_acc_get_next_child(msaa_prov
->parent
, msaa_prov
->child_pos
+ 1, DIR_FORWARD
,
871 &acc
, &child_id
, &child_pos
, TRUE
);
873 hr
= msaa_acc_get_next_child(msaa_prov
->parent
, msaa_prov
->child_pos
- 1, DIR_REVERSE
,
874 &acc
, &child_id
, &child_pos
, TRUE
);
876 if (FAILED(hr
) || !acc
)
879 hr
= create_msaa_provider(acc
, child_id
, NULL
, FALSE
, &elprov
);
882 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
884 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
885 prov
->parent
= msaa_prov
->parent
;
886 IAccessible_AddRef(msaa_prov
->parent
);
887 if (acc
!= msaa_prov
->acc
)
888 prov
->child_pos
= child_pos
;
890 prov
->child_pos
= child_id
;
892 IAccessible_Release(acc
);
897 FIXME("Invalid NavigateDirection %d\n", direction
);
904 static HRESULT WINAPI
msaa_fragment_GetRuntimeId(IRawElementProviderFragment
*iface
,
907 FIXME("%p, %p: stub!\n", iface
, ret_val
);
912 static HRESULT WINAPI
msaa_fragment_get_BoundingRectangle(IRawElementProviderFragment
*iface
,
913 struct UiaRect
*ret_val
)
915 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
916 LONG left
, top
, width
, height
;
919 TRACE("%p, %p\n", iface
, ret_val
);
921 memset(ret_val
, 0, sizeof(*ret_val
));
924 * If this IAccessible is at the root of its HWND, the BaseHwnd provider
925 * will supply the bounding rectangle.
927 if (msaa_check_root_acc(msaa_prov
))
930 if (msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
, STATE_SYSTEM_OFFSCREEN
))
933 hr
= IAccessible_accLocation(msaa_prov
->acc
, &left
, &top
, &width
, &height
, msaa_prov
->cid
);
937 ret_val
->left
= left
;
939 ret_val
->width
= width
;
940 ret_val
->height
= height
;
945 static HRESULT WINAPI
msaa_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment
*iface
,
948 FIXME("%p, %p: stub!\n", iface
, ret_val
);
953 static HRESULT WINAPI
msaa_fragment_SetFocus(IRawElementProviderFragment
*iface
)
955 FIXME("%p: stub!\n", iface
);
959 static HRESULT WINAPI
msaa_fragment_get_FragmentRoot(IRawElementProviderFragment
*iface
,
960 IRawElementProviderFragmentRoot
**ret_val
)
962 FIXME("%p, %p: stub!\n", iface
, ret_val
);
967 static const IRawElementProviderFragmentVtbl msaa_fragment_vtbl
= {
968 msaa_fragment_QueryInterface
,
969 msaa_fragment_AddRef
,
970 msaa_fragment_Release
,
971 msaa_fragment_Navigate
,
972 msaa_fragment_GetRuntimeId
,
973 msaa_fragment_get_BoundingRectangle
,
974 msaa_fragment_GetEmbeddedFragmentRoots
,
975 msaa_fragment_SetFocus
,
976 msaa_fragment_get_FragmentRoot
,
980 * ILegacyIAccessibleProvider interface for UiaProviderFromIAccessible
983 static inline struct msaa_provider
*impl_from_msaa_acc_provider(ILegacyIAccessibleProvider
*iface
)
985 return CONTAINING_RECORD(iface
, struct msaa_provider
, ILegacyIAccessibleProvider_iface
);
988 static HRESULT WINAPI
msaa_acc_provider_QueryInterface(ILegacyIAccessibleProvider
*iface
, REFIID riid
, void **ppv
)
990 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
991 return IRawElementProviderSimple_QueryInterface(&msaa_prov
->IRawElementProviderSimple_iface
, riid
, ppv
);
994 static ULONG WINAPI
msaa_acc_provider_AddRef(ILegacyIAccessibleProvider
*iface
)
996 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
997 return IRawElementProviderSimple_AddRef(&msaa_prov
->IRawElementProviderSimple_iface
);
1000 static ULONG WINAPI
msaa_acc_provider_Release(ILegacyIAccessibleProvider
*iface
)
1002 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
1003 return IRawElementProviderSimple_Release(&msaa_prov
->IRawElementProviderSimple_iface
);
1006 static HRESULT WINAPI
msaa_acc_provider_Select(ILegacyIAccessibleProvider
*iface
, LONG select_flags
)
1008 FIXME("%p, %#lx: stub!\n", iface
, select_flags
);
1012 static HRESULT WINAPI
msaa_acc_provider_DoDefaultAction(ILegacyIAccessibleProvider
*iface
)
1014 FIXME("%p: stub!\n", iface
);
1018 static HRESULT WINAPI
msaa_acc_provider_SetValue(ILegacyIAccessibleProvider
*iface
, LPCWSTR val
)
1020 FIXME("%p, %p<%s>: stub!\n", iface
, val
, debugstr_w(val
));
1024 static HRESULT WINAPI
msaa_acc_provider_GetIAccessible(ILegacyIAccessibleProvider
*iface
,
1025 IAccessible
**out_acc
)
1027 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
1029 TRACE("%p, %p\n", iface
, out_acc
);
1031 IAccessible_AddRef(msaa_prov
->acc
);
1032 *out_acc
= msaa_prov
->acc
;
1037 static HRESULT WINAPI
msaa_acc_provider_get_ChildId(ILegacyIAccessibleProvider
*iface
, int *out_cid
)
1039 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
1041 TRACE("%p, %p\n", iface
, out_cid
);
1042 *out_cid
= V_I4(&msaa_prov
->cid
);
1047 static HRESULT WINAPI
msaa_acc_provider_get_Name(ILegacyIAccessibleProvider
*iface
, BSTR
*out_name
)
1049 FIXME("%p, %p: stub!\n", iface
, out_name
);
1053 static HRESULT WINAPI
msaa_acc_provider_get_Value(ILegacyIAccessibleProvider
*iface
, BSTR
*out_value
)
1055 FIXME("%p, %p: stub!\n", iface
, out_value
);
1059 static HRESULT WINAPI
msaa_acc_provider_get_Description(ILegacyIAccessibleProvider
*iface
,
1060 BSTR
*out_description
)
1062 FIXME("%p, %p: stub!\n", iface
, out_description
);
1066 static HRESULT WINAPI
msaa_acc_provider_get_Role(ILegacyIAccessibleProvider
*iface
, DWORD
*out_role
)
1068 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
1072 TRACE("%p, %p\n", iface
, out_role
);
1076 hr
= IAccessible_get_accRole(msaa_prov
->acc
, msaa_prov
->cid
, &v
);
1077 if (SUCCEEDED(hr
) && V_VT(&v
) == VT_I4
)
1078 *out_role
= V_I4(&v
);
1083 static HRESULT WINAPI
msaa_acc_provider_get_State(ILegacyIAccessibleProvider
*iface
, DWORD
*out_state
)
1085 FIXME("%p, %p: stub!\n", iface
, out_state
);
1089 static HRESULT WINAPI
msaa_acc_provider_get_Help(ILegacyIAccessibleProvider
*iface
, BSTR
*out_help
)
1091 FIXME("%p, %p: stub!\n", iface
, out_help
);
1095 static HRESULT WINAPI
msaa_acc_provider_get_KeyboardShortcut(ILegacyIAccessibleProvider
*iface
,
1096 BSTR
*out_kbd_shortcut
)
1098 FIXME("%p, %p: stub!\n", iface
, out_kbd_shortcut
);
1102 static HRESULT WINAPI
msaa_acc_provider_GetSelection(ILegacyIAccessibleProvider
*iface
,
1103 SAFEARRAY
**out_selected
)
1105 FIXME("%p, %p: stub!\n", iface
, out_selected
);
1109 static HRESULT WINAPI
msaa_acc_provider_get_DefaultAction(ILegacyIAccessibleProvider
*iface
,
1110 BSTR
*out_default_action
)
1112 FIXME("%p, %p: stub!\n", iface
, out_default_action
);
1116 static const ILegacyIAccessibleProviderVtbl msaa_acc_provider_vtbl
= {
1117 msaa_acc_provider_QueryInterface
,
1118 msaa_acc_provider_AddRef
,
1119 msaa_acc_provider_Release
,
1120 msaa_acc_provider_Select
,
1121 msaa_acc_provider_DoDefaultAction
,
1122 msaa_acc_provider_SetValue
,
1123 msaa_acc_provider_GetIAccessible
,
1124 msaa_acc_provider_get_ChildId
,
1125 msaa_acc_provider_get_Name
,
1126 msaa_acc_provider_get_Value
,
1127 msaa_acc_provider_get_Description
,
1128 msaa_acc_provider_get_Role
,
1129 msaa_acc_provider_get_State
,
1130 msaa_acc_provider_get_Help
,
1131 msaa_acc_provider_get_KeyboardShortcut
,
1132 msaa_acc_provider_GetSelection
,
1133 msaa_acc_provider_get_DefaultAction
,
1136 HRESULT
create_msaa_provider(IAccessible
*acc
, LONG child_id
, HWND hwnd
, BOOL known_root_acc
,
1137 IRawElementProviderSimple
**elprov
)
1139 struct msaa_provider
*msaa_prov
= calloc(1, sizeof(*msaa_prov
));
1142 return E_OUTOFMEMORY
;
1144 msaa_prov
->IRawElementProviderSimple_iface
.lpVtbl
= &msaa_provider_vtbl
;
1145 msaa_prov
->IRawElementProviderFragment_iface
.lpVtbl
= &msaa_fragment_vtbl
;
1146 msaa_prov
->ILegacyIAccessibleProvider_iface
.lpVtbl
= &msaa_acc_provider_vtbl
;
1147 msaa_prov
->refcount
= 1;
1148 variant_init_i4(&msaa_prov
->cid
, child_id
);
1149 msaa_prov
->acc
= acc
;
1150 IAccessible_AddRef(acc
);
1151 msaa_prov
->ia2
= msaa_acc_get_ia2(acc
);
1157 hr
= WindowFromAccessibleObject(acc
, &msaa_prov
->hwnd
);
1159 WARN("WindowFromAccessibleObject failed with hr %#lx\n", hr
);
1162 msaa_prov
->hwnd
= hwnd
;
1165 msaa_prov
->root_acc_check_ran
= msaa_prov
->is_root_acc
= TRUE
;
1167 *elprov
= &msaa_prov
->IRawElementProviderSimple_iface
;
1172 /***********************************************************************
1173 * UiaProviderFromIAccessible (uiautomationcore.@)
1175 HRESULT WINAPI
UiaProviderFromIAccessible(IAccessible
*acc
, LONG child_id
, DWORD flags
,
1176 IRawElementProviderSimple
**elprov
)
1178 IServiceProvider
*serv_prov
;
1182 TRACE("(%p, %ld, %#lx, %p)\n", acc
, child_id
, flags
, elprov
);
1190 return E_INVALIDARG
;
1192 if (flags
!= UIA_PFIA_DEFAULT
)
1194 FIXME("unsupported flags %#lx\n", flags
);
1198 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void **)&serv_prov
);
1203 hr
= IServiceProvider_QueryService(serv_prov
, &IIS_IsOleaccProxy
, &IID_IUnknown
, (void **)&unk
);
1206 WARN("Cannot wrap an oleacc proxy IAccessible!\n");
1207 IUnknown_Release(unk
);
1208 IServiceProvider_Release(serv_prov
);
1209 return E_INVALIDARG
;
1212 IServiceProvider_Release(serv_prov
);
1215 hr
= WindowFromAccessibleObject(acc
, &hwnd
);
1221 return create_msaa_provider(acc
, child_id
, hwnd
, FALSE
, elprov
);
1224 static HRESULT
uia_get_hr_for_last_error(void)
1226 DWORD last_err
= GetLastError();
1230 case ERROR_INVALID_WINDOW_HANDLE
:
1231 return UIA_E_ELEMENTNOTAVAILABLE
;
1234 return UIA_E_TIMEOUT
;
1241 #define UIA_DEFAULT_MSG_TIMEOUT 10000
1242 static HRESULT
uia_send_message_timeout(HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
, UINT timeout
, LRESULT
*lres
)
1245 if (!SendMessageTimeoutW(hwnd
, msg
, wparam
, lparam
, SMTO_NORMAL
, timeout
, (PDWORD_PTR
)lres
))
1246 return uia_get_hr_for_last_error();
1251 static BOOL
is_top_level_hwnd(HWND hwnd
)
1253 return GetAncestor(hwnd
, GA_PARENT
) == GetDesktopWindow();
1256 static HRESULT
get_uia_control_type_for_hwnd(HWND hwnd
, int *control_type
)
1258 LONG_PTR style
, ex_style
;
1261 if ((ex_style
= GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
)) & WS_EX_APPWINDOW
)
1263 *control_type
= UIA_WindowControlTypeId
;
1267 SetLastError(NO_ERROR
);
1268 if (!(style
= GetWindowLongPtrW(hwnd
, GWL_STYLE
)) && (GetLastError() != NO_ERROR
))
1269 return uia_get_hr_for_last_error();
1272 * Non-caption HWNDs that are popups or tool windows aren't considered full
1273 * windows, only panes.
1275 if (((style
& WS_CAPTION
) != WS_CAPTION
) && ((ex_style
& WS_EX_TOOLWINDOW
) || (style
& WS_POPUP
)))
1277 *control_type
= UIA_PaneControlTypeId
;
1281 /* Non top-level HWNDs are considered panes as well. */
1282 if (!is_top_level_hwnd(hwnd
))
1283 *control_type
= UIA_PaneControlTypeId
;
1285 *control_type
= UIA_WindowControlTypeId
;
1291 * Default ProviderType_BaseHwnd IRawElementProviderSimple interface.
1293 struct base_hwnd_provider
{
1294 IRawElementProviderSimple IRawElementProviderSimple_iface
;
1295 IRawElementProviderFragment IRawElementProviderFragment_iface
;
1301 static inline struct base_hwnd_provider
*impl_from_base_hwnd_provider(IRawElementProviderSimple
*iface
)
1303 return CONTAINING_RECORD(iface
, struct base_hwnd_provider
, IRawElementProviderSimple_iface
);
1306 static HRESULT WINAPI
base_hwnd_provider_QueryInterface(IRawElementProviderSimple
*iface
, REFIID riid
, void **ppv
)
1308 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_provider(iface
);
1311 if (IsEqualIID(riid
, &IID_IRawElementProviderSimple
) || IsEqualIID(riid
, &IID_IUnknown
))
1313 else if (IsEqualIID(riid
, &IID_IRawElementProviderFragment
))
1314 *ppv
= &base_hwnd_prov
->IRawElementProviderFragment_iface
;
1316 return E_NOINTERFACE
;
1318 IRawElementProviderSimple_AddRef(iface
);
1322 static ULONG WINAPI
base_hwnd_provider_AddRef(IRawElementProviderSimple
*iface
)
1324 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_provider(iface
);
1325 ULONG refcount
= InterlockedIncrement(&base_hwnd_prov
->refcount
);
1327 TRACE("%p, refcount %ld\n", iface
, refcount
);
1332 static ULONG WINAPI
base_hwnd_provider_Release(IRawElementProviderSimple
*iface
)
1334 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_provider(iface
);
1335 ULONG refcount
= InterlockedDecrement(&base_hwnd_prov
->refcount
);
1337 TRACE("%p, refcount %ld\n", iface
, refcount
);
1340 free(base_hwnd_prov
);
1345 static HRESULT WINAPI
base_hwnd_provider_get_ProviderOptions(IRawElementProviderSimple
*iface
,
1346 enum ProviderOptions
*ret_val
)
1348 TRACE("%p, %p\n", iface
, ret_val
);
1349 *ret_val
= ProviderOptions_ClientSideProvider
;
1353 static HRESULT WINAPI
base_hwnd_provider_GetPatternProvider(IRawElementProviderSimple
*iface
,
1354 PATTERNID pattern_id
, IUnknown
**ret_val
)
1356 FIXME("%p, %d, %p: stub\n", iface
, pattern_id
, ret_val
);
1361 static HRESULT WINAPI
base_hwnd_provider_GetPropertyValue(IRawElementProviderSimple
*iface
,
1362 PROPERTYID prop_id
, VARIANT
*ret_val
)
1364 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_provider(iface
);
1367 TRACE("%p, %d, %p\n", iface
, prop_id
, ret_val
);
1369 VariantInit(ret_val
);
1370 if (!IsWindow(base_hwnd_prov
->hwnd
))
1371 return UIA_E_ELEMENTNOTAVAILABLE
;
1375 case UIA_ProviderDescriptionPropertyId
:
1376 V_VT(ret_val
) = VT_BSTR
;
1377 V_BSTR(ret_val
) = SysAllocString(L
"Wine: HWND Proxy");
1380 case UIA_NativeWindowHandlePropertyId
:
1381 V_VT(ret_val
) = VT_I4
;
1382 V_I4(ret_val
) = HandleToUlong(base_hwnd_prov
->hwnd
);
1385 case UIA_ProcessIdPropertyId
:
1389 if (!GetWindowThreadProcessId(base_hwnd_prov
->hwnd
, &pid
))
1390 return UIA_E_ELEMENTNOTAVAILABLE
;
1392 V_VT(ret_val
) = VT_I4
;
1393 V_I4(ret_val
) = pid
;
1397 case UIA_ClassNamePropertyId
:
1399 WCHAR buf
[256] = { 0 };
1401 if (!GetClassNameW(base_hwnd_prov
->hwnd
, buf
, ARRAY_SIZE(buf
)))
1402 hr
= uia_get_hr_for_last_error();
1405 V_VT(ret_val
) = VT_BSTR
;
1406 V_BSTR(ret_val
) = SysAllocString(buf
);
1411 case UIA_NamePropertyId
:
1415 V_VT(ret_val
) = VT_BSTR
;
1416 V_BSTR(ret_val
) = SysAllocString(L
"");
1417 hr
= uia_send_message_timeout(base_hwnd_prov
->hwnd
, WM_GETTEXTLENGTH
, 0, 0, UIA_DEFAULT_MSG_TIMEOUT
, &lres
);
1418 if (FAILED(hr
) || !lres
)
1421 if (!SysReAllocStringLen(&V_BSTR(ret_val
), NULL
, lres
))
1427 hr
= uia_send_message_timeout(base_hwnd_prov
->hwnd
, WM_GETTEXT
, SysStringLen(V_BSTR(ret_val
)) + 1,
1428 (LPARAM
)V_BSTR(ret_val
), UIA_DEFAULT_MSG_TIMEOUT
, &lres
);
1432 case UIA_ControlTypePropertyId
:
1436 hr
= get_uia_control_type_for_hwnd(base_hwnd_prov
->hwnd
, &control_type
);
1439 V_VT(ret_val
) = VT_I4
;
1440 V_I4(ret_val
) = control_type
;
1450 VariantClear(ret_val
);
1455 static HRESULT WINAPI
base_hwnd_provider_get_HostRawElementProvider(IRawElementProviderSimple
*iface
,
1456 IRawElementProviderSimple
**ret_val
)
1458 TRACE("%p, %p\n", iface
, ret_val
);
1463 static const IRawElementProviderSimpleVtbl base_hwnd_provider_vtbl
= {
1464 base_hwnd_provider_QueryInterface
,
1465 base_hwnd_provider_AddRef
,
1466 base_hwnd_provider_Release
,
1467 base_hwnd_provider_get_ProviderOptions
,
1468 base_hwnd_provider_GetPatternProvider
,
1469 base_hwnd_provider_GetPropertyValue
,
1470 base_hwnd_provider_get_HostRawElementProvider
,
1474 * IRawElementProviderFragment interface for default ProviderType_BaseHwnd
1477 static inline struct base_hwnd_provider
*impl_from_base_hwnd_fragment(IRawElementProviderFragment
*iface
)
1479 return CONTAINING_RECORD(iface
, struct base_hwnd_provider
, IRawElementProviderFragment_iface
);
1482 static HRESULT WINAPI
base_hwnd_fragment_QueryInterface(IRawElementProviderFragment
*iface
, REFIID riid
,
1485 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_fragment(iface
);
1486 return IRawElementProviderSimple_QueryInterface(&base_hwnd_prov
->IRawElementProviderSimple_iface
, riid
, ppv
);
1489 static ULONG WINAPI
base_hwnd_fragment_AddRef(IRawElementProviderFragment
*iface
)
1491 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_fragment(iface
);
1492 return IRawElementProviderSimple_AddRef(&base_hwnd_prov
->IRawElementProviderSimple_iface
);
1495 static ULONG WINAPI
base_hwnd_fragment_Release(IRawElementProviderFragment
*iface
)
1497 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_fragment(iface
);
1498 return IRawElementProviderSimple_Release(&base_hwnd_prov
->IRawElementProviderSimple_iface
);
1501 static HRESULT WINAPI
base_hwnd_fragment_Navigate(IRawElementProviderFragment
*iface
,
1502 enum NavigateDirection direction
, IRawElementProviderFragment
**ret_val
)
1504 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_fragment(iface
);
1505 IRawElementProviderSimple
*elprov
= NULL
;
1508 TRACE("%p, %d, %p\n", iface
, direction
, ret_val
);
1514 case NavigateDirection_Parent
:
1519 * Top level owned windows have their owner window as a parent instead
1520 * of the desktop window.
1522 if (is_top_level_hwnd(base_hwnd_prov
->hwnd
) && (owner
= GetWindow(base_hwnd_prov
->hwnd
, GW_OWNER
)))
1525 parent
= GetAncestor(base_hwnd_prov
->hwnd
, GA_PARENT
);
1528 hr
= create_base_hwnd_provider(parent
, &elprov
);
1532 case NavigateDirection_FirstChild
:
1533 case NavigateDirection_LastChild
:
1534 case NavigateDirection_PreviousSibling
:
1535 case NavigateDirection_NextSibling
:
1536 FIXME("Unimplemented NavigateDirection %d\n", direction
);
1540 FIXME("Invalid NavigateDirection %d\n", direction
);
1541 return E_INVALIDARG
;
1546 hr
= IRawElementProviderSimple_QueryInterface(elprov
, &IID_IRawElementProviderFragment
, (void **)ret_val
);
1547 IRawElementProviderSimple_Release(elprov
);
1553 static HRESULT WINAPI
base_hwnd_fragment_GetRuntimeId(IRawElementProviderFragment
*iface
,
1554 SAFEARRAY
**ret_val
)
1556 FIXME("%p, %p: stub!\n", iface
, ret_val
);
1561 static HRESULT WINAPI
base_hwnd_fragment_get_BoundingRectangle(IRawElementProviderFragment
*iface
,
1562 struct UiaRect
*ret_val
)
1564 struct base_hwnd_provider
*base_hwnd_prov
= impl_from_base_hwnd_fragment(iface
);
1567 TRACE("%p, %p\n", iface
, ret_val
);
1569 memset(ret_val
, 0, sizeof(*ret_val
));
1571 /* Top level minimized window - Return empty rect. */
1572 if (is_top_level_hwnd(base_hwnd_prov
->hwnd
) && IsIconic(base_hwnd_prov
->hwnd
))
1575 if (!GetWindowRect(base_hwnd_prov
->hwnd
, &rect
))
1576 return uia_get_hr_for_last_error();
1578 ret_val
->left
= rect
.left
;
1579 ret_val
->top
= rect
.top
;
1580 ret_val
->width
= (rect
.right
- rect
.left
);
1581 ret_val
->height
= (rect
.bottom
- rect
.top
);
1586 static HRESULT WINAPI
base_hwnd_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment
*iface
,
1587 SAFEARRAY
**ret_val
)
1589 FIXME("%p, %p: stub!\n", iface
, ret_val
);
1594 static HRESULT WINAPI
base_hwnd_fragment_SetFocus(IRawElementProviderFragment
*iface
)
1596 FIXME("%p: stub!\n", iface
);
1600 static HRESULT WINAPI
base_hwnd_fragment_get_FragmentRoot(IRawElementProviderFragment
*iface
,
1601 IRawElementProviderFragmentRoot
**ret_val
)
1603 FIXME("%p, %p: stub!\n", iface
, ret_val
);
1608 static const IRawElementProviderFragmentVtbl base_hwnd_fragment_vtbl
= {
1609 base_hwnd_fragment_QueryInterface
,
1610 base_hwnd_fragment_AddRef
,
1611 base_hwnd_fragment_Release
,
1612 base_hwnd_fragment_Navigate
,
1613 base_hwnd_fragment_GetRuntimeId
,
1614 base_hwnd_fragment_get_BoundingRectangle
,
1615 base_hwnd_fragment_GetEmbeddedFragmentRoots
,
1616 base_hwnd_fragment_SetFocus
,
1617 base_hwnd_fragment_get_FragmentRoot
,
1620 HRESULT
create_base_hwnd_provider(HWND hwnd
, IRawElementProviderSimple
**elprov
)
1622 struct base_hwnd_provider
*base_hwnd_prov
;
1627 return E_INVALIDARG
;
1629 if (!IsWindow(hwnd
))
1630 return UIA_E_ELEMENTNOTAVAILABLE
;
1632 if (!(base_hwnd_prov
= calloc(1, sizeof(*base_hwnd_prov
))))
1633 return E_OUTOFMEMORY
;
1635 base_hwnd_prov
->IRawElementProviderSimple_iface
.lpVtbl
= &base_hwnd_provider_vtbl
;
1636 base_hwnd_prov
->IRawElementProviderFragment_iface
.lpVtbl
= &base_hwnd_fragment_vtbl
;
1637 base_hwnd_prov
->refcount
= 1;
1638 base_hwnd_prov
->hwnd
= hwnd
;
1639 *elprov
= &base_hwnd_prov
->IRawElementProviderSimple_iface
;
1645 * UI Automation provider thread functions.
1647 struct uia_provider_thread
1649 struct rb_tree node_map
;
1650 struct list nodes_list
;
1656 static struct uia_provider_thread provider_thread
;
1657 static CRITICAL_SECTION provider_thread_cs
;
1658 static CRITICAL_SECTION_DEBUG provider_thread_cs_debug
=
1660 0, 0, &provider_thread_cs
,
1661 { &provider_thread_cs_debug
.ProcessLocksList
, &provider_thread_cs_debug
.ProcessLocksList
},
1662 0, 0, { (DWORD_PTR
)(__FILE__
": provider_thread_cs") }
1664 static CRITICAL_SECTION provider_thread_cs
= { &provider_thread_cs_debug
, -1, 0, 0, 0, 0 };
1666 struct uia_provider_thread_map_entry
1668 struct rb_entry entry
;
1670 SAFEARRAY
*runtime_id
;
1671 struct list nodes_list
;
1674 static int uia_runtime_id_compare(const void *key
, const struct rb_entry
*entry
)
1676 struct uia_provider_thread_map_entry
*prov_entry
= RB_ENTRY_VALUE(entry
, struct uia_provider_thread_map_entry
, entry
);
1677 return uia_compare_safearrays(prov_entry
->runtime_id
, (SAFEARRAY
*)key
, UIAutomationType_IntArray
);
1680 void uia_provider_thread_remove_node(HUIANODE node
)
1682 struct uia_node
*node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
1684 TRACE("Removing node %p\n", node
);
1686 EnterCriticalSection(&provider_thread_cs
);
1688 list_remove(&node_data
->prov_thread_list_entry
);
1689 list_init(&node_data
->prov_thread_list_entry
);
1690 if (!list_empty(&node_data
->node_map_list_entry
))
1692 list_remove(&node_data
->node_map_list_entry
);
1693 list_init(&node_data
->node_map_list_entry
);
1694 if (list_empty(&node_data
->map
->nodes_list
))
1696 rb_remove(&provider_thread
.node_map
, &node_data
->map
->entry
);
1697 SafeArrayDestroy(node_data
->map
->runtime_id
);
1698 free(node_data
->map
);
1700 node_data
->map
= NULL
;
1703 LeaveCriticalSection(&provider_thread_cs
);
1706 static void uia_provider_thread_disconnect_node(SAFEARRAY
*sa
)
1708 struct rb_entry
*rb_entry
;
1710 EnterCriticalSection(&provider_thread_cs
);
1712 /* Provider thread hasn't been started, no nodes to disconnect. */
1713 if (!provider_thread
.ref
)
1716 rb_entry
= rb_get(&provider_thread
.node_map
, sa
);
1719 struct uia_provider_thread_map_entry
*prov_map
;
1720 struct list
*cursor
, *cursor2
;
1721 struct uia_node
*node_data
;
1723 prov_map
= RB_ENTRY_VALUE(rb_entry
, struct uia_provider_thread_map_entry
, entry
);
1724 LIST_FOR_EACH_SAFE(cursor
, cursor2
, &prov_map
->nodes_list
)
1726 node_data
= LIST_ENTRY(cursor
, struct uia_node
, node_map_list_entry
);
1728 list_remove(cursor
);
1729 list_remove(&node_data
->prov_thread_list_entry
);
1730 list_init(&node_data
->prov_thread_list_entry
);
1731 list_init(&node_data
->node_map_list_entry
);
1732 node_data
->map
= NULL
;
1734 IWineUiaNode_disconnect(&node_data
->IWineUiaNode_iface
);
1737 rb_remove(&provider_thread
.node_map
, &prov_map
->entry
);
1738 SafeArrayDestroy(prov_map
->runtime_id
);
1743 LeaveCriticalSection(&provider_thread_cs
);
1746 static HRESULT
uia_provider_thread_add_node(HUIANODE node
, SAFEARRAY
*rt_id
)
1748 struct uia_node
*node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
1749 int prov_type
= get_node_provider_type_at_idx(node_data
, 0);
1750 struct uia_provider
*prov_data
;
1753 prov_data
= impl_from_IWineUiaProvider(node_data
->prov
[prov_type
]);
1754 node_data
->nested_node
= prov_data
->return_nested_node
= prov_data
->refuse_hwnd_node_providers
= TRUE
;
1756 TRACE("Adding node %p\n", node
);
1758 EnterCriticalSection(&provider_thread_cs
);
1759 list_add_tail(&provider_thread
.nodes_list
, &node_data
->prov_thread_list_entry
);
1761 /* If we have a runtime ID, create an entry in the rb tree. */
1764 struct uia_provider_thread_map_entry
*prov_map
;
1765 struct rb_entry
*rb_entry
;
1767 if ((rb_entry
= rb_get(&provider_thread
.node_map
, rt_id
)))
1768 prov_map
= RB_ENTRY_VALUE(rb_entry
, struct uia_provider_thread_map_entry
, entry
);
1771 prov_map
= calloc(1, sizeof(*prov_map
));
1778 hr
= SafeArrayCopy(rt_id
, &prov_map
->runtime_id
);
1784 list_init(&prov_map
->nodes_list
);
1785 rb_put(&provider_thread
.node_map
, prov_map
->runtime_id
, &prov_map
->entry
);
1788 list_add_tail(&prov_map
->nodes_list
, &node_data
->node_map_list_entry
);
1789 node_data
->map
= prov_map
;
1793 LeaveCriticalSection(&provider_thread_cs
);
1798 #define WM_GET_OBJECT_UIA_NODE (WM_USER + 1)
1799 #define WM_UIA_PROVIDER_THREAD_STOP (WM_USER + 2)
1800 static LRESULT CALLBACK
uia_provider_thread_msg_proc(HWND hwnd
, UINT msg
, WPARAM wparam
,
1805 case WM_GET_OBJECT_UIA_NODE
:
1807 SAFEARRAY
*rt_id
= (SAFEARRAY
*)wparam
;
1808 HUIANODE node
= (HUIANODE
)lparam
;
1811 if (FAILED(uia_provider_thread_add_node(node
, rt_id
)))
1813 WARN("Failed to add node %p to provider thread list.\n", node
);
1818 * LresultFromObject returns an index into the global atom string table,
1819 * which has a valid range of 0xc000-0xffff.
1821 lr
= LresultFromObject(&IID_IWineUiaNode
, 0, (IUnknown
*)node
);
1822 if ((lr
> 0xffff) || (lr
< 0xc000))
1824 WARN("Got invalid lresult %Ix\n", lr
);
1835 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
1838 static DWORD WINAPI
uia_provider_thread_proc(void *arg
)
1840 HANDLE initialized_event
= arg
;
1844 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
1845 hwnd
= CreateWindowW(L
"Message", NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, NULL
, NULL
, NULL
);
1848 WARN("CreateWindow failed: %ld\n", GetLastError());
1850 FreeLibraryAndExitThread(huia_module
, 1);
1853 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (LONG_PTR
)uia_provider_thread_msg_proc
);
1854 provider_thread
.hwnd
= hwnd
;
1856 /* Initialization complete, thread can now process window messages. */
1857 SetEvent(initialized_event
);
1858 TRACE("Provider thread started.\n");
1859 while (GetMessageW(&msg
, NULL
, 0, 0))
1861 if (msg
.message
== WM_UIA_PROVIDER_THREAD_STOP
)
1863 TranslateMessage(&msg
);
1864 DispatchMessageW(&msg
);
1867 TRACE("Shutting down UI Automation provider thread.\n");
1869 DestroyWindow(hwnd
);
1871 FreeLibraryAndExitThread(huia_module
, 0);
1874 static BOOL
uia_start_provider_thread(void)
1876 BOOL started
= TRUE
;
1878 EnterCriticalSection(&provider_thread_cs
);
1879 if (++provider_thread
.ref
== 1)
1886 /* Increment DLL reference count. */
1887 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
1888 (const WCHAR
*)uia_start_provider_thread
, &hmodule
);
1890 list_init(&provider_thread
.nodes_list
);
1891 rb_init(&provider_thread
.node_map
, uia_runtime_id_compare
);
1892 events
[0] = ready_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1893 if (!(provider_thread
.hthread
= CreateThread(NULL
, 0, uia_provider_thread_proc
,
1894 ready_event
, 0, NULL
)))
1896 FreeLibrary(hmodule
);
1901 events
[1] = provider_thread
.hthread
;
1902 wait_obj
= WaitForMultipleObjects(2, events
, FALSE
, INFINITE
);
1903 if (wait_obj
!= WAIT_OBJECT_0
)
1905 CloseHandle(provider_thread
.hthread
);
1910 CloseHandle(ready_event
);
1913 WARN("Failed to start provider thread\n");
1914 memset(&provider_thread
, 0, sizeof(provider_thread
));
1918 LeaveCriticalSection(&provider_thread_cs
);
1922 void uia_stop_provider_thread(void)
1924 EnterCriticalSection(&provider_thread_cs
);
1925 if (!--provider_thread
.ref
)
1927 PostMessageW(provider_thread
.hwnd
, WM_UIA_PROVIDER_THREAD_STOP
, 0, 0);
1928 CloseHandle(provider_thread
.hthread
);
1929 if (!list_empty(&provider_thread
.nodes_list
))
1930 ERR("Provider thread shutdown with nodes still in the list\n");
1931 memset(&provider_thread
, 0, sizeof(provider_thread
));
1933 LeaveCriticalSection(&provider_thread_cs
);
1937 * Pass our IWineUiaNode interface to the provider thread for marshaling. UI
1938 * Automation has to work regardless of whether or not COM is initialized on
1939 * the thread calling UiaReturnRawElementProvider.
1941 LRESULT
uia_lresult_from_node(HUIANODE huianode
)
1947 hr
= UiaGetRuntimeId(huianode
, &rt_id
);
1948 if (SUCCEEDED(hr
) && uia_start_provider_thread())
1949 lr
= SendMessageW(provider_thread
.hwnd
, WM_GET_OBJECT_UIA_NODE
, (WPARAM
)rt_id
, (LPARAM
)huianode
);
1952 WARN("UiaGetRuntimeId failed with hr %#lx\n", hr
);
1955 * LresultFromObject increases refcnt by 1. If LresultFromObject
1956 * failed or wasn't called, this is expected to release the node.
1958 UiaNodeRelease(huianode
);
1959 SafeArrayDestroy(rt_id
);
1963 /***********************************************************************
1964 * UiaReturnRawElementProvider (uiautomationcore.@)
1966 LRESULT WINAPI
UiaReturnRawElementProvider(HWND hwnd
, WPARAM wparam
,
1967 LPARAM lparam
, IRawElementProviderSimple
*elprov
)
1972 TRACE("(%p, %Ix, %#Ix, %p)\n", hwnd
, wparam
, lparam
, elprov
);
1974 if (!wparam
&& !lparam
&& !elprov
)
1976 FIXME("UIA-to-MSAA bridge not implemented, no provider map to free.\n");
1980 if (lparam
!= UiaRootObjectId
)
1982 FIXME("Unsupported object id %Id, ignoring.\n", lparam
);
1986 hr
= create_uia_node_from_elprov(elprov
, &node
, FALSE
);
1989 WARN("Failed to create HUIANODE with hr %#lx\n", hr
);
1993 return uia_lresult_from_node(node
);
1996 /***********************************************************************
1997 * UiaDisconnectProvider (uiautomationcore.@)
1999 HRESULT WINAPI
UiaDisconnectProvider(IRawElementProviderSimple
*elprov
)
2005 TRACE("(%p)\n", elprov
);
2007 hr
= create_uia_node_from_elprov(elprov
, &node
, FALSE
);
2011 hr
= UiaGetRuntimeId(node
, &sa
);
2012 UiaNodeRelease(node
);
2017 return E_INVALIDARG
;
2019 uia_provider_thread_disconnect_node(sa
);
2021 SafeArrayDestroy(sa
);