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"
23 #include "wine/heap.h"
24 #include "wine/rbtree.h"
26 #include "wine/iaccessible2.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation
);
30 DEFINE_GUID(SID_AccFromDAWrapper
, 0x33f139ee, 0xe509, 0x47f7, 0xbf,0x39, 0x83,0x76,0x44,0xf7,0x45,0x76);
32 static void variant_init_i4(VARIANT
*v
, int val
)
38 static void variant_init_bool(VARIANT
*v
, BOOL val
)
41 V_BOOL(v
) = val
? VARIANT_TRUE
: VARIANT_FALSE
;
44 static BOOL
msaa_check_acc_state(IAccessible
*acc
, VARIANT cid
, ULONG flag
)
50 hr
= IAccessible_get_accState(acc
, cid
, &v
);
51 if (SUCCEEDED(hr
) && V_VT(&v
) == VT_I4
&& (V_I4(&v
) & flag
))
57 static IAccessible2
*msaa_acc_get_ia2(IAccessible
*acc
)
59 IServiceProvider
*serv_prov
;
60 IAccessible2
*ia2
= NULL
;
63 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void **)&serv_prov
);
66 hr
= IServiceProvider_QueryService(serv_prov
, &IID_IAccessible2
, &IID_IAccessible2
, (void **)&ia2
);
67 IServiceProvider_Release(serv_prov
);
68 if (SUCCEEDED(hr
) && ia2
)
72 hr
= IAccessible_QueryInterface(acc
, &IID_IAccessible2
, (void **)&ia2
);
73 if (SUCCEEDED(hr
) && ia2
)
79 static IAccessible
*msaa_acc_da_unwrap(IAccessible
*acc
)
85 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void**)&sp
);
88 hr
= IServiceProvider_QueryService(sp
, &SID_AccFromDAWrapper
, &IID_IAccessible
, (void**)&acc2
);
89 IServiceProvider_Release(sp
);
92 if (SUCCEEDED(hr
) && acc2
)
95 IAccessible_AddRef(acc
);
100 * Compare role, state, child count, and location properties of the two
101 * IAccessibles. If all four are successfully retrieved and are equal, this is
102 * considered a match.
104 static HRESULT
msaa_acc_prop_match(IAccessible
*acc
, IAccessible
*acc2
)
106 BOOL role_match
, state_match
, child_count_match
, location_match
;
107 LONG child_count
[2], left
[2], top
[2], width
[2], height
[2];
111 role_match
= state_match
= child_count_match
= location_match
= FALSE
;
112 variant_init_i4(&cid
, CHILDID_SELF
);
113 hr
= IAccessible_get_accRole(acc
, cid
, &v
);
114 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
117 hr
= IAccessible_get_accRole(acc2
, cid
, &v2
);
118 if (SUCCEEDED(hr
) && (V_VT(&v2
) == VT_I4
))
120 if (V_I4(&v
) != V_I4(&v2
))
128 hr
= IAccessible_get_accState(acc
, cid
, &v
);
129 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
132 hr
= IAccessible_get_accState(acc2
, cid
, &v2
);
133 if (SUCCEEDED(hr
) && (V_VT(&v2
) == VT_I4
))
135 if (V_I4(&v
) != V_I4(&v2
))
142 hr
= IAccessible_get_accChildCount(acc
, &child_count
[0]);
143 hr2
= IAccessible_get_accChildCount(acc2
, &child_count
[1]);
144 if (SUCCEEDED(hr
) && SUCCEEDED(hr2
))
146 if (child_count
[0] != child_count
[1])
149 child_count_match
= TRUE
;
152 hr
= IAccessible_accLocation(acc
, &left
[0], &top
[0], &width
[0], &height
[0], cid
);
155 hr
= IAccessible_accLocation(acc2
, &left
[1], &top
[1], &width
[1], &height
[1], cid
);
158 if ((left
[0] != left
[1]) || (top
[0] != top
[1]) || (width
[0] != width
[1]) ||
159 (height
[0] != height
[1]))
162 location_match
= TRUE
;
166 if (role_match
&& state_match
&& child_count_match
&& location_match
)
172 static BOOL
msaa_acc_compare(IAccessible
*acc
, IAccessible
*acc2
)
174 IAccessible2
*ia2
[2] = { NULL
, NULL
};
175 IUnknown
*unk
, *unk2
;
176 BOOL matched
= FALSE
;
182 acc
= msaa_acc_da_unwrap(acc
);
183 acc2
= msaa_acc_da_unwrap(acc2
);
184 IAccessible_QueryInterface(acc
, &IID_IUnknown
, (void**)&unk
);
185 IAccessible_QueryInterface(acc2
, &IID_IUnknown
, (void**)&unk2
);
192 ia2
[0] = msaa_acc_get_ia2(acc
);
193 ia2
[1] = msaa_acc_get_ia2(acc2
);
194 if (!ia2
[0] != !ia2
[1])
198 hr
= IAccessible2_get_uniqueID(ia2
[0], &unique_id
[0]);
201 hr
= IAccessible2_get_uniqueID(ia2
[1], &unique_id
[1]);
204 if (unique_id
[0] == unique_id
[1])
212 hr
= msaa_acc_prop_match(acc
, acc2
);
218 variant_init_i4(&cid
, CHILDID_SELF
);
219 hr
= IAccessible_get_accName(acc
, cid
, &name
[0]);
222 hr
= IAccessible_get_accName(acc2
, cid
, &name
[1]);
225 if (!name
[0] && !name
[1])
227 else if (!name
[0] || !name
[1])
231 if (!wcscmp(name
[0], name
[1]))
237 SysFreeString(name
[1]);
240 SysFreeString(name
[0]);
244 IUnknown_Release(unk
);
245 IUnknown_Release(unk2
);
246 IAccessible_Release(acc
);
247 IAccessible_Release(acc2
);
249 IAccessible2_Release(ia2
[0]);
251 IAccessible2_Release(ia2
[1]);
256 static HRESULT
msaa_acc_get_parent(IAccessible
*acc
, IAccessible
**parent
)
258 IDispatch
*disp
= NULL
;
262 hr
= IAccessible_get_accParent(acc
, &disp
);
263 if (FAILED(hr
) || !disp
)
266 hr
= IDispatch_QueryInterface(disp
, &IID_IAccessible
, (void**)parent
);
267 IDispatch_Release(disp
);
271 #define DIR_FORWARD 0
272 #define DIR_REVERSE 1
273 static HRESULT
msaa_acc_get_next_child(IAccessible
*acc
, LONG start_pos
, LONG direction
,
274 IAccessible
**child
, LONG
*child_id
, LONG
*child_pos
, BOOL check_visible
)
276 LONG child_count
, cur_pos
;
286 hr
= IAccessible_get_accChildCount(acc
, &child_count
);
287 if (FAILED(hr
) || (cur_pos
> child_count
))
290 variant_init_i4(&cid
, cur_pos
);
291 hr
= IAccessible_get_accChild(acc
, cid
, &disp
);
297 if (!check_visible
|| !msaa_check_acc_state(acc
, cid
, STATE_SYSTEM_INVISIBLE
))
300 *child_id
= *child_pos
= cur_pos
;
301 IAccessible_AddRef(acc
);
307 IAccessible
*acc_child
= NULL
;
309 hr
= IDispatch_QueryInterface(disp
, &IID_IAccessible
, (void **)&acc_child
);
310 IDispatch_Release(disp
);
314 variant_init_i4(&cid
, CHILDID_SELF
);
315 if (!check_visible
|| !msaa_check_acc_state(acc_child
, cid
, STATE_SYSTEM_INVISIBLE
))
318 *child_id
= CHILDID_SELF
;
319 *child_pos
= cur_pos
;
323 IAccessible_Release(acc_child
);
326 if (direction
== DIR_FORWARD
)
331 if ((cur_pos
> child_count
) || (cur_pos
<= 0))
338 static HRESULT
msaa_acc_get_child_pos(IAccessible
*acc
, IAccessible
**out_parent
,
341 LONG child_count
, child_id
, child_pos
, match_pos
;
342 IAccessible
*child
, *parent
, *match
, **children
;
348 hr
= msaa_acc_get_parent(acc
, &parent
);
349 if (FAILED(hr
) || !parent
)
352 hr
= IAccessible_get_accChildCount(parent
, &child_count
);
353 if (FAILED(hr
) || !child_count
)
355 IAccessible_Release(parent
);
359 children
= heap_alloc_zero(sizeof(*children
) * child_count
);
361 return E_OUTOFMEMORY
;
364 for (i
= 0; i
< child_count
; i
++)
366 hr
= msaa_acc_get_next_child(parent
, i
+ 1, DIR_FORWARD
, &child
, &child_id
, &child_pos
, FALSE
);
367 if (FAILED(hr
) || !child
)
373 IAccessible_Release(child
);
376 for (i
= 0; i
< child_count
; i
++)
381 if (msaa_acc_compare(acc
, children
[i
]))
388 /* Can't have more than one IAccessible match. */
401 *out_parent
= parent
;
402 *out_child_pos
= match_pos
;
405 IAccessible_Release(parent
);
407 for (i
= 0; i
< child_count
; i
++)
410 IAccessible_Release(children
[i
]);
418 static LONG
msaa_role_to_uia_control_type(LONG role
)
422 case ROLE_SYSTEM_TITLEBAR
: return UIA_TitleBarControlTypeId
;
423 case ROLE_SYSTEM_MENUBAR
: return UIA_MenuBarControlTypeId
;
424 case ROLE_SYSTEM_SCROLLBAR
: return UIA_ScrollBarControlTypeId
;
425 case ROLE_SYSTEM_INDICATOR
:
426 case ROLE_SYSTEM_GRIP
: return UIA_ThumbControlTypeId
;
427 case ROLE_SYSTEM_APPLICATION
:
428 case ROLE_SYSTEM_WINDOW
: return UIA_WindowControlTypeId
;
429 case ROLE_SYSTEM_MENUPOPUP
: return UIA_MenuControlTypeId
;
430 case ROLE_SYSTEM_TOOLTIP
: return UIA_ToolTipControlTypeId
;
431 case ROLE_SYSTEM_DOCUMENT
: return UIA_DocumentControlTypeId
;
432 case ROLE_SYSTEM_PANE
: return UIA_PaneControlTypeId
;
433 case ROLE_SYSTEM_GROUPING
: return UIA_GroupControlTypeId
;
434 case ROLE_SYSTEM_SEPARATOR
: return UIA_SeparatorControlTypeId
;
435 case ROLE_SYSTEM_TOOLBAR
: return UIA_ToolBarControlTypeId
;
436 case ROLE_SYSTEM_STATUSBAR
: return UIA_StatusBarControlTypeId
;
437 case ROLE_SYSTEM_TABLE
: return UIA_TableControlTypeId
;
438 case ROLE_SYSTEM_COLUMNHEADER
:
439 case ROLE_SYSTEM_ROWHEADER
: return UIA_HeaderControlTypeId
;
440 case ROLE_SYSTEM_CELL
: return UIA_DataItemControlTypeId
;
441 case ROLE_SYSTEM_LINK
: return UIA_HyperlinkControlTypeId
;
442 case ROLE_SYSTEM_LIST
: return UIA_ListControlTypeId
;
443 case ROLE_SYSTEM_LISTITEM
: return UIA_ListItemControlTypeId
;
444 case ROLE_SYSTEM_OUTLINE
: return UIA_TreeControlTypeId
;
445 case ROLE_SYSTEM_OUTLINEITEM
: return UIA_TreeItemControlTypeId
;
446 case ROLE_SYSTEM_PAGETAB
: return UIA_TabItemControlTypeId
;
447 case ROLE_SYSTEM_GRAPHIC
: return UIA_ImageControlTypeId
;
448 case ROLE_SYSTEM_STATICTEXT
: return UIA_TextControlTypeId
;
449 case ROLE_SYSTEM_TEXT
: return UIA_EditControlTypeId
;
450 case ROLE_SYSTEM_CLOCK
:
451 case ROLE_SYSTEM_BUTTONDROPDOWNGRID
:
452 case ROLE_SYSTEM_PUSHBUTTON
: return UIA_ButtonControlTypeId
;
453 case ROLE_SYSTEM_CHECKBUTTON
: return UIA_CheckBoxControlTypeId
;
454 case ROLE_SYSTEM_RADIOBUTTON
: return UIA_RadioButtonControlTypeId
;
455 case ROLE_SYSTEM_COMBOBOX
: return UIA_ComboBoxControlTypeId
;
456 case ROLE_SYSTEM_PROGRESSBAR
: return UIA_ProgressBarControlTypeId
;
457 case ROLE_SYSTEM_SLIDER
: return UIA_SliderControlTypeId
;
458 case ROLE_SYSTEM_SPINBUTTON
: return UIA_SpinnerControlTypeId
;
459 case ROLE_SYSTEM_BUTTONMENU
:
460 case ROLE_SYSTEM_MENUITEM
: return UIA_MenuItemControlTypeId
;
461 case ROLE_SYSTEM_PAGETABLIST
: return UIA_TabControlTypeId
;
462 case ROLE_SYSTEM_BUTTONDROPDOWN
:
463 case ROLE_SYSTEM_SPLITBUTTON
: return UIA_SplitButtonControlTypeId
;
464 case ROLE_SYSTEM_SOUND
:
465 case ROLE_SYSTEM_CURSOR
:
466 case ROLE_SYSTEM_CARET
:
467 case ROLE_SYSTEM_ALERT
:
468 case ROLE_SYSTEM_CLIENT
:
469 case ROLE_SYSTEM_CHART
:
470 case ROLE_SYSTEM_DIALOG
:
471 case ROLE_SYSTEM_BORDER
:
472 case ROLE_SYSTEM_COLUMN
:
473 case ROLE_SYSTEM_ROW
:
474 case ROLE_SYSTEM_HELPBALLOON
:
475 case ROLE_SYSTEM_CHARACTER
:
476 case ROLE_SYSTEM_PROPERTYPAGE
:
477 case ROLE_SYSTEM_DROPLIST
:
478 case ROLE_SYSTEM_DIAL
:
479 case ROLE_SYSTEM_HOTKEYFIELD
:
480 case ROLE_SYSTEM_DIAGRAM
:
481 case ROLE_SYSTEM_ANIMATION
:
482 case ROLE_SYSTEM_EQUATION
:
483 case ROLE_SYSTEM_WHITESPACE
:
484 case ROLE_SYSTEM_IPADDRESS
:
485 case ROLE_SYSTEM_OUTLINEBUTTON
:
486 WARN("No UIA control type mapping for MSAA role %ld\n", role
);
490 FIXME("UIA control type mapping unimplemented for MSAA role %ld\n", role
);
498 * UiaProviderFromIAccessible IRawElementProviderSimple interface.
500 struct msaa_provider
{
501 IRawElementProviderSimple IRawElementProviderSimple_iface
;
502 IRawElementProviderFragment IRawElementProviderFragment_iface
;
503 ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface
;
512 BOOL root_acc_check_ran
;
519 static BOOL
msaa_check_root_acc(struct msaa_provider
*msaa_prov
)
524 if (msaa_prov
->root_acc_check_ran
)
525 return msaa_prov
->is_root_acc
;
527 msaa_prov
->root_acc_check_ran
= TRUE
;
528 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
|| msaa_prov
->parent
)
531 hr
= AccessibleObjectFromWindow(msaa_prov
->hwnd
, OBJID_CLIENT
, &IID_IAccessible
, (void **)&acc
);
535 if (msaa_acc_compare(msaa_prov
->acc
, acc
))
536 msaa_prov
->is_root_acc
= TRUE
;
538 IAccessible_Release(acc
);
539 return msaa_prov
->is_root_acc
;
542 static inline struct msaa_provider
*impl_from_msaa_provider(IRawElementProviderSimple
*iface
)
544 return CONTAINING_RECORD(iface
, struct msaa_provider
, IRawElementProviderSimple_iface
);
547 HRESULT WINAPI
msaa_provider_QueryInterface(IRawElementProviderSimple
*iface
, REFIID riid
, void **ppv
)
549 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
552 if (IsEqualIID(riid
, &IID_IRawElementProviderSimple
) || IsEqualIID(riid
, &IID_IUnknown
))
554 else if (IsEqualIID(riid
, &IID_IRawElementProviderFragment
))
555 *ppv
= &msaa_prov
->IRawElementProviderFragment_iface
;
556 else if (IsEqualIID(riid
, &IID_ILegacyIAccessibleProvider
))
557 *ppv
= &msaa_prov
->ILegacyIAccessibleProvider_iface
;
559 return E_NOINTERFACE
;
561 IRawElementProviderSimple_AddRef(iface
);
565 ULONG WINAPI
msaa_provider_AddRef(IRawElementProviderSimple
*iface
)
567 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
568 ULONG refcount
= InterlockedIncrement(&msaa_prov
->refcount
);
570 TRACE("%p, refcount %ld\n", iface
, refcount
);
575 ULONG WINAPI
msaa_provider_Release(IRawElementProviderSimple
*iface
)
577 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
578 ULONG refcount
= InterlockedDecrement(&msaa_prov
->refcount
);
580 TRACE("%p, refcount %ld\n", iface
, refcount
);
584 IAccessible_Release(msaa_prov
->acc
);
585 if (msaa_prov
->parent
)
586 IAccessible_Release(msaa_prov
->parent
);
588 IAccessible2_Release(msaa_prov
->ia2
);
589 heap_free(msaa_prov
);
595 HRESULT WINAPI
msaa_provider_get_ProviderOptions(IRawElementProviderSimple
*iface
,
596 enum ProviderOptions
*ret_val
)
598 TRACE("%p, %p\n", iface
, ret_val
);
599 *ret_val
= ProviderOptions_ServerSideProvider
| ProviderOptions_UseComThreading
;
603 HRESULT WINAPI
msaa_provider_GetPatternProvider(IRawElementProviderSimple
*iface
,
604 PATTERNID pattern_id
, IUnknown
**ret_val
)
606 TRACE("%p, %d, %p\n", iface
, pattern_id
, ret_val
);
611 case UIA_LegacyIAccessiblePatternId
:
612 return IRawElementProviderSimple_QueryInterface(iface
, &IID_IUnknown
, (void **)ret_val
);
615 FIXME("Unimplemented patternId %d\n", pattern_id
);
622 HRESULT WINAPI
msaa_provider_GetPropertyValue(IRawElementProviderSimple
*iface
,
623 PROPERTYID prop_id
, VARIANT
*ret_val
)
625 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
629 TRACE("%p, %d, %p\n", iface
, prop_id
, ret_val
);
631 VariantInit(ret_val
);
635 case UIA_ProviderDescriptionPropertyId
:
636 V_VT(ret_val
) = VT_BSTR
;
637 V_BSTR(ret_val
) = SysAllocString(L
"Wine: MSAA Proxy");
640 case UIA_ControlTypePropertyId
:
641 if (!msaa_prov
->control_type
)
643 hr
= IAccessible_get_accRole(msaa_prov
->acc
, msaa_prov
->cid
, &v
);
644 if (SUCCEEDED(hr
) && (V_VT(&v
) == VT_I4
))
645 msaa_prov
->control_type
= msaa_role_to_uia_control_type(V_I4(&v
));
648 if (msaa_prov
->control_type
)
649 variant_init_i4(ret_val
, msaa_prov
->control_type
);
653 case UIA_HasKeyboardFocusPropertyId
:
654 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
655 STATE_SYSTEM_FOCUSED
));
658 case UIA_IsKeyboardFocusablePropertyId
:
659 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
660 STATE_SYSTEM_FOCUSABLE
));
663 case UIA_IsEnabledPropertyId
:
664 variant_init_bool(ret_val
, !msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
665 STATE_SYSTEM_UNAVAILABLE
));
668 case UIA_IsPasswordPropertyId
:
669 variant_init_bool(ret_val
, msaa_check_acc_state(msaa_prov
->acc
, msaa_prov
->cid
,
670 STATE_SYSTEM_PROTECTED
));
674 FIXME("Unimplemented propertyId %d\n", prop_id
);
681 HRESULT WINAPI
msaa_provider_get_HostRawElementProvider(IRawElementProviderSimple
*iface
,
682 IRawElementProviderSimple
**ret_val
)
684 struct msaa_provider
*msaa_prov
= impl_from_msaa_provider(iface
);
686 TRACE("%p, %p\n", iface
, ret_val
);
689 if (msaa_check_root_acc(msaa_prov
))
690 return UiaHostProviderFromHwnd(msaa_prov
->hwnd
, ret_val
);
695 static const IRawElementProviderSimpleVtbl msaa_provider_vtbl
= {
696 msaa_provider_QueryInterface
,
697 msaa_provider_AddRef
,
698 msaa_provider_Release
,
699 msaa_provider_get_ProviderOptions
,
700 msaa_provider_GetPatternProvider
,
701 msaa_provider_GetPropertyValue
,
702 msaa_provider_get_HostRawElementProvider
,
706 * IRawElementProviderFragment interface for UiaProviderFromIAccessible
709 static inline struct msaa_provider
*impl_from_msaa_fragment(IRawElementProviderFragment
*iface
)
711 return CONTAINING_RECORD(iface
, struct msaa_provider
, IRawElementProviderFragment_iface
);
714 static HRESULT WINAPI
msaa_fragment_QueryInterface(IRawElementProviderFragment
*iface
, REFIID riid
,
717 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
718 return IRawElementProviderSimple_QueryInterface(&msaa_prov
->IRawElementProviderSimple_iface
, riid
, ppv
);
721 static ULONG WINAPI
msaa_fragment_AddRef(IRawElementProviderFragment
*iface
)
723 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
724 return IRawElementProviderSimple_AddRef(&msaa_prov
->IRawElementProviderSimple_iface
);
727 static ULONG WINAPI
msaa_fragment_Release(IRawElementProviderFragment
*iface
)
729 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
730 return IRawElementProviderSimple_Release(&msaa_prov
->IRawElementProviderSimple_iface
);
733 static HRESULT WINAPI
msaa_fragment_Navigate(IRawElementProviderFragment
*iface
,
734 enum NavigateDirection direction
, IRawElementProviderFragment
**ret_val
)
736 struct msaa_provider
*msaa_prov
= impl_from_msaa_fragment(iface
);
737 LONG child_count
, child_id
, child_pos
;
738 IRawElementProviderSimple
*elprov
;
742 TRACE("%p, %d, %p\n", iface
, direction
, ret_val
);
747 case NavigateDirection_Parent
:
748 if (msaa_check_root_acc(msaa_prov
))
751 if (V_I4(&msaa_prov
->cid
) == CHILDID_SELF
)
753 hr
= msaa_acc_get_parent(msaa_prov
->acc
, &acc
);
754 if (FAILED(hr
) || !acc
)
758 acc
= msaa_prov
->acc
;
760 hr
= UiaProviderFromIAccessible(acc
, CHILDID_SELF
, 0, &elprov
);
763 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
764 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
767 if (acc
!= msaa_prov
->acc
)
768 IAccessible_Release(acc
);
772 case NavigateDirection_FirstChild
:
773 case NavigateDirection_LastChild
:
774 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
)
777 hr
= IAccessible_get_accChildCount(msaa_prov
->acc
, &child_count
);
778 if (FAILED(hr
) || !child_count
)
781 if (direction
== NavigateDirection_FirstChild
)
782 hr
= msaa_acc_get_next_child(msaa_prov
->acc
, 1, DIR_FORWARD
, &acc
, &child_id
,
785 hr
= msaa_acc_get_next_child(msaa_prov
->acc
, child_count
, DIR_REVERSE
, &acc
, &child_id
,
788 if (FAILED(hr
) || !acc
)
791 hr
= UiaProviderFromIAccessible(acc
, child_id
, 0, &elprov
);
794 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
796 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
797 prov
->parent
= msaa_prov
->acc
;
798 IAccessible_AddRef(msaa_prov
->acc
);
799 if (acc
!= msaa_prov
->acc
)
800 prov
->child_pos
= child_pos
;
802 prov
->child_pos
= child_id
;
804 IAccessible_Release(acc
);
808 case NavigateDirection_NextSibling
:
809 case NavigateDirection_PreviousSibling
:
810 if (msaa_check_root_acc(msaa_prov
))
813 if (!msaa_prov
->parent
)
815 if (V_I4(&msaa_prov
->cid
) != CHILDID_SELF
)
817 msaa_prov
->parent
= msaa_prov
->acc
;
818 IAccessible_AddRef(msaa_prov
->acc
);
819 msaa_prov
->child_pos
= V_I4(&msaa_prov
->cid
);
823 hr
= msaa_acc_get_child_pos(msaa_prov
->acc
, &acc
, &child_pos
);
824 if (FAILED(hr
) || !acc
)
826 msaa_prov
->parent
= acc
;
827 msaa_prov
->child_pos
= child_pos
;
831 if (direction
== NavigateDirection_NextSibling
)
832 hr
= msaa_acc_get_next_child(msaa_prov
->parent
, msaa_prov
->child_pos
+ 1, DIR_FORWARD
,
833 &acc
, &child_id
, &child_pos
, TRUE
);
835 hr
= msaa_acc_get_next_child(msaa_prov
->parent
, msaa_prov
->child_pos
- 1, DIR_REVERSE
,
836 &acc
, &child_id
, &child_pos
, TRUE
);
838 if (FAILED(hr
) || !acc
)
841 hr
= UiaProviderFromIAccessible(acc
, child_id
, 0, &elprov
);
844 struct msaa_provider
*prov
= impl_from_msaa_provider(elprov
);
846 *ret_val
= &prov
->IRawElementProviderFragment_iface
;
847 prov
->parent
= msaa_prov
->parent
;
848 IAccessible_AddRef(msaa_prov
->parent
);
849 if (acc
!= msaa_prov
->acc
)
850 prov
->child_pos
= child_pos
;
852 prov
->child_pos
= child_id
;
854 IAccessible_Release(acc
);
859 FIXME("Invalid NavigateDirection %d\n", direction
);
866 static HRESULT WINAPI
msaa_fragment_GetRuntimeId(IRawElementProviderFragment
*iface
,
869 FIXME("%p, %p: stub!\n", iface
, ret_val
);
874 static HRESULT WINAPI
msaa_fragment_get_BoundingRectangle(IRawElementProviderFragment
*iface
,
875 struct UiaRect
*ret_val
)
877 FIXME("%p, %p: stub!\n", iface
, ret_val
);
881 static HRESULT WINAPI
msaa_fragment_GetEmbeddedFragmentRoots(IRawElementProviderFragment
*iface
,
884 FIXME("%p, %p: stub!\n", iface
, ret_val
);
889 static HRESULT WINAPI
msaa_fragment_SetFocus(IRawElementProviderFragment
*iface
)
891 FIXME("%p: stub!\n", iface
);
895 static HRESULT WINAPI
msaa_fragment_get_FragmentRoot(IRawElementProviderFragment
*iface
,
896 IRawElementProviderFragmentRoot
**ret_val
)
898 FIXME("%p, %p: stub!\n", iface
, ret_val
);
903 static const IRawElementProviderFragmentVtbl msaa_fragment_vtbl
= {
904 msaa_fragment_QueryInterface
,
905 msaa_fragment_AddRef
,
906 msaa_fragment_Release
,
907 msaa_fragment_Navigate
,
908 msaa_fragment_GetRuntimeId
,
909 msaa_fragment_get_BoundingRectangle
,
910 msaa_fragment_GetEmbeddedFragmentRoots
,
911 msaa_fragment_SetFocus
,
912 msaa_fragment_get_FragmentRoot
,
916 * ILegacyIAccessibleProvider interface for UiaProviderFromIAccessible
919 static inline struct msaa_provider
*impl_from_msaa_acc_provider(ILegacyIAccessibleProvider
*iface
)
921 return CONTAINING_RECORD(iface
, struct msaa_provider
, ILegacyIAccessibleProvider_iface
);
924 static HRESULT WINAPI
msaa_acc_provider_QueryInterface(ILegacyIAccessibleProvider
*iface
, REFIID riid
, void **ppv
)
926 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
927 return IRawElementProviderSimple_QueryInterface(&msaa_prov
->IRawElementProviderSimple_iface
, riid
, ppv
);
930 static ULONG WINAPI
msaa_acc_provider_AddRef(ILegacyIAccessibleProvider
*iface
)
932 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
933 return IRawElementProviderSimple_AddRef(&msaa_prov
->IRawElementProviderSimple_iface
);
936 static ULONG WINAPI
msaa_acc_provider_Release(ILegacyIAccessibleProvider
*iface
)
938 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
939 return IRawElementProviderSimple_Release(&msaa_prov
->IRawElementProviderSimple_iface
);
942 static HRESULT WINAPI
msaa_acc_provider_Select(ILegacyIAccessibleProvider
*iface
, LONG select_flags
)
944 FIXME("%p, %#lx: stub!\n", iface
, select_flags
);
948 static HRESULT WINAPI
msaa_acc_provider_DoDefaultAction(ILegacyIAccessibleProvider
*iface
)
950 FIXME("%p: stub!\n", iface
);
954 static HRESULT WINAPI
msaa_acc_provider_SetValue(ILegacyIAccessibleProvider
*iface
, LPCWSTR val
)
956 FIXME("%p, %p<%s>: stub!\n", iface
, val
, debugstr_w(val
));
960 static HRESULT WINAPI
msaa_acc_provider_GetIAccessible(ILegacyIAccessibleProvider
*iface
,
961 IAccessible
**out_acc
)
963 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
965 TRACE("%p, %p\n", iface
, out_acc
);
967 IAccessible_AddRef(msaa_prov
->acc
);
968 *out_acc
= msaa_prov
->acc
;
973 static HRESULT WINAPI
msaa_acc_provider_get_ChildId(ILegacyIAccessibleProvider
*iface
, int *out_cid
)
975 struct msaa_provider
*msaa_prov
= impl_from_msaa_acc_provider(iface
);
977 TRACE("%p, %p\n", iface
, out_cid
);
978 *out_cid
= V_I4(&msaa_prov
->cid
);
983 static HRESULT WINAPI
msaa_acc_provider_get_Name(ILegacyIAccessibleProvider
*iface
, BSTR
*out_name
)
985 FIXME("%p, %p: stub!\n", iface
, out_name
);
989 static HRESULT WINAPI
msaa_acc_provider_get_Value(ILegacyIAccessibleProvider
*iface
, BSTR
*out_value
)
991 FIXME("%p, %p: stub!\n", iface
, out_value
);
995 static HRESULT WINAPI
msaa_acc_provider_get_Description(ILegacyIAccessibleProvider
*iface
,
996 BSTR
*out_description
)
998 FIXME("%p, %p: stub!\n", iface
, out_description
);
1002 static HRESULT WINAPI
msaa_acc_provider_get_Role(ILegacyIAccessibleProvider
*iface
, DWORD
*out_role
)
1004 FIXME("%p, %p: stub!\n", iface
, out_role
);
1008 static HRESULT WINAPI
msaa_acc_provider_get_State(ILegacyIAccessibleProvider
*iface
, DWORD
*out_state
)
1010 FIXME("%p, %p: stub!\n", iface
, out_state
);
1014 static HRESULT WINAPI
msaa_acc_provider_get_Help(ILegacyIAccessibleProvider
*iface
, BSTR
*out_help
)
1016 FIXME("%p, %p: stub!\n", iface
, out_help
);
1020 static HRESULT WINAPI
msaa_acc_provider_get_KeyboardShortcut(ILegacyIAccessibleProvider
*iface
,
1021 BSTR
*out_kbd_shortcut
)
1023 FIXME("%p, %p: stub!\n", iface
, out_kbd_shortcut
);
1027 static HRESULT WINAPI
msaa_acc_provider_GetSelection(ILegacyIAccessibleProvider
*iface
,
1028 SAFEARRAY
**out_selected
)
1030 FIXME("%p, %p: stub!\n", iface
, out_selected
);
1034 static HRESULT WINAPI
msaa_acc_provider_get_DefaultAction(ILegacyIAccessibleProvider
*iface
,
1035 BSTR
*out_default_action
)
1037 FIXME("%p, %p: stub!\n", iface
, out_default_action
);
1041 static const ILegacyIAccessibleProviderVtbl msaa_acc_provider_vtbl
= {
1042 msaa_acc_provider_QueryInterface
,
1043 msaa_acc_provider_AddRef
,
1044 msaa_acc_provider_Release
,
1045 msaa_acc_provider_Select
,
1046 msaa_acc_provider_DoDefaultAction
,
1047 msaa_acc_provider_SetValue
,
1048 msaa_acc_provider_GetIAccessible
,
1049 msaa_acc_provider_get_ChildId
,
1050 msaa_acc_provider_get_Name
,
1051 msaa_acc_provider_get_Value
,
1052 msaa_acc_provider_get_Description
,
1053 msaa_acc_provider_get_Role
,
1054 msaa_acc_provider_get_State
,
1055 msaa_acc_provider_get_Help
,
1056 msaa_acc_provider_get_KeyboardShortcut
,
1057 msaa_acc_provider_GetSelection
,
1058 msaa_acc_provider_get_DefaultAction
,
1061 /***********************************************************************
1062 * UiaProviderFromIAccessible (uiautomationcore.@)
1064 HRESULT WINAPI
UiaProviderFromIAccessible(IAccessible
*acc
, long child_id
, DWORD flags
,
1065 IRawElementProviderSimple
**elprov
)
1067 struct msaa_provider
*msaa_prov
;
1068 IServiceProvider
*serv_prov
;
1072 TRACE("(%p, %ld, %#lx, %p)\n", acc
, child_id
, flags
, elprov
);
1080 return E_INVALIDARG
;
1082 if (flags
!= UIA_PFIA_DEFAULT
)
1084 FIXME("unsupported flags %#lx\n", flags
);
1088 hr
= IAccessible_QueryInterface(acc
, &IID_IServiceProvider
, (void **)&serv_prov
);
1093 hr
= IServiceProvider_QueryService(serv_prov
, &IIS_IsOleaccProxy
, &IID_IUnknown
, (void **)&unk
);
1096 WARN("Cannot wrap an oleacc proxy IAccessible!\n");
1097 IUnknown_Release(unk
);
1098 IServiceProvider_Release(serv_prov
);
1099 return E_INVALIDARG
;
1102 IServiceProvider_Release(serv_prov
);
1105 hr
= WindowFromAccessibleObject(acc
, &hwnd
);
1111 msaa_prov
= heap_alloc_zero(sizeof(*msaa_prov
));
1113 return E_OUTOFMEMORY
;
1115 msaa_prov
->IRawElementProviderSimple_iface
.lpVtbl
= &msaa_provider_vtbl
;
1116 msaa_prov
->IRawElementProviderFragment_iface
.lpVtbl
= &msaa_fragment_vtbl
;
1117 msaa_prov
->ILegacyIAccessibleProvider_iface
.lpVtbl
= &msaa_acc_provider_vtbl
;
1118 msaa_prov
->refcount
= 1;
1119 msaa_prov
->hwnd
= hwnd
;
1120 variant_init_i4(&msaa_prov
->cid
, child_id
);
1121 msaa_prov
->acc
= acc
;
1122 IAccessible_AddRef(acc
);
1123 msaa_prov
->ia2
= msaa_acc_get_ia2(acc
);
1124 *elprov
= &msaa_prov
->IRawElementProviderSimple_iface
;
1130 * UI Automation provider thread functions.
1132 struct uia_provider_thread
1134 struct rb_tree node_map
;
1135 struct list nodes_list
;
1141 static struct uia_provider_thread provider_thread
;
1142 static CRITICAL_SECTION provider_thread_cs
;
1143 static CRITICAL_SECTION_DEBUG provider_thread_cs_debug
=
1145 0, 0, &provider_thread_cs
,
1146 { &provider_thread_cs_debug
.ProcessLocksList
, &provider_thread_cs_debug
.ProcessLocksList
},
1147 0, 0, { (DWORD_PTR
)(__FILE__
": provider_thread_cs") }
1149 static CRITICAL_SECTION provider_thread_cs
= { &provider_thread_cs_debug
, -1, 0, 0, 0, 0 };
1151 struct uia_provider_thread_map_entry
1153 struct rb_entry entry
;
1155 SAFEARRAY
*runtime_id
;
1156 struct list nodes_list
;
1159 static int uia_runtime_id_compare(const void *key
, const struct rb_entry
*entry
)
1161 struct uia_provider_thread_map_entry
*prov_entry
= RB_ENTRY_VALUE(entry
, struct uia_provider_thread_map_entry
, entry
);
1162 return uia_compare_safearrays(prov_entry
->runtime_id
, (SAFEARRAY
*)key
, UIAutomationType_IntArray
);
1165 void uia_provider_thread_remove_node(HUIANODE node
)
1167 struct uia_node
*node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
1169 TRACE("Removing node %p\n", node
);
1171 EnterCriticalSection(&provider_thread_cs
);
1173 list_remove(&node_data
->prov_thread_list_entry
);
1174 list_init(&node_data
->prov_thread_list_entry
);
1175 if (!list_empty(&node_data
->node_map_list_entry
))
1177 list_remove(&node_data
->node_map_list_entry
);
1178 list_init(&node_data
->node_map_list_entry
);
1179 if (list_empty(&node_data
->map
->nodes_list
))
1181 rb_remove(&provider_thread
.node_map
, &node_data
->map
->entry
);
1182 SafeArrayDestroy(node_data
->map
->runtime_id
);
1183 heap_free(node_data
->map
);
1185 node_data
->map
= NULL
;
1188 LeaveCriticalSection(&provider_thread_cs
);
1191 static void uia_provider_thread_disconnect_node(SAFEARRAY
*sa
)
1193 struct rb_entry
*rb_entry
;
1195 EnterCriticalSection(&provider_thread_cs
);
1197 /* Provider thread hasn't been started, no nodes to disconnect. */
1198 if (!provider_thread
.ref
)
1201 rb_entry
= rb_get(&provider_thread
.node_map
, sa
);
1204 struct uia_provider_thread_map_entry
*prov_map
;
1205 struct list
*cursor
, *cursor2
;
1206 struct uia_node
*node_data
;
1208 prov_map
= RB_ENTRY_VALUE(rb_entry
, struct uia_provider_thread_map_entry
, entry
);
1209 LIST_FOR_EACH_SAFE(cursor
, cursor2
, &prov_map
->nodes_list
)
1211 node_data
= LIST_ENTRY(cursor
, struct uia_node
, node_map_list_entry
);
1213 list_remove(cursor
);
1214 list_remove(&node_data
->prov_thread_list_entry
);
1215 list_init(&node_data
->prov_thread_list_entry
);
1216 list_init(&node_data
->node_map_list_entry
);
1217 node_data
->map
= NULL
;
1219 IWineUiaNode_disconnect(&node_data
->IWineUiaNode_iface
);
1222 rb_remove(&provider_thread
.node_map
, &prov_map
->entry
);
1223 SafeArrayDestroy(prov_map
->runtime_id
);
1224 heap_free(prov_map
);
1228 LeaveCriticalSection(&provider_thread_cs
);
1231 static HRESULT
uia_provider_thread_add_node(HUIANODE node
)
1233 struct uia_node
*node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
1234 int prov_type
= get_node_provider_type_at_idx(node_data
, 0);
1235 struct uia_provider
*prov_data
;
1239 prov_data
= impl_from_IWineUiaProvider(node_data
->prov
[prov_type
]);
1240 node_data
->nested_node
= prov_data
->return_nested_node
= TRUE
;
1241 hr
= UiaGetRuntimeId(node
, &sa
);
1245 TRACE("Adding node %p\n", node
);
1247 EnterCriticalSection(&provider_thread_cs
);
1248 list_add_tail(&provider_thread
.nodes_list
, &node_data
->prov_thread_list_entry
);
1250 /* If we have a runtime ID, create an entry in the rb tree. */
1253 struct uia_provider_thread_map_entry
*prov_map
;
1254 struct rb_entry
*rb_entry
;
1256 if ((rb_entry
= rb_get(&provider_thread
.node_map
, sa
)))
1258 prov_map
= RB_ENTRY_VALUE(rb_entry
, struct uia_provider_thread_map_entry
, entry
);
1259 SafeArrayDestroy(sa
);
1263 prov_map
= heap_alloc_zero(sizeof(*prov_map
));
1266 SafeArrayDestroy(sa
);
1267 LeaveCriticalSection(&provider_thread_cs
);
1268 return E_OUTOFMEMORY
;
1271 prov_map
->runtime_id
= sa
;
1272 list_init(&prov_map
->nodes_list
);
1273 rb_put(&provider_thread
.node_map
, sa
, &prov_map
->entry
);
1276 list_add_tail(&prov_map
->nodes_list
, &node_data
->node_map_list_entry
);
1277 node_data
->map
= prov_map
;
1280 LeaveCriticalSection(&provider_thread_cs
);
1285 #define WM_GET_OBJECT_UIA_NODE (WM_USER + 1)
1286 #define WM_UIA_PROVIDER_THREAD_STOP (WM_USER + 2)
1287 static LRESULT CALLBACK
uia_provider_thread_msg_proc(HWND hwnd
, UINT msg
, WPARAM wparam
,
1292 case WM_GET_OBJECT_UIA_NODE
:
1294 HUIANODE node
= (HUIANODE
)lparam
;
1297 if (FAILED(uia_provider_thread_add_node(node
)))
1299 WARN("Failed to add node %p to provider thread list.\n", node
);
1300 UiaNodeRelease(node
);
1305 * LresultFromObject returns an index into the global atom string table,
1306 * which has a valid range of 0xc000-0xffff.
1308 lr
= LresultFromObject(&IID_IWineUiaNode
, 0, (IUnknown
*)node
);
1309 if ((lr
> 0xffff) || (lr
< 0xc000))
1311 WARN("Got invalid lresult %Ix\n", lr
);
1316 * LresultFromObject increases refcnt by 1. If LresultFromObject
1317 * failed, this is expected to release the node.
1319 UiaNodeRelease(node
);
1327 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
1330 static DWORD WINAPI
uia_provider_thread_proc(void *arg
)
1332 HANDLE initialized_event
= arg
;
1336 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
1337 hwnd
= CreateWindowW(L
"Message", NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, NULL
, NULL
, NULL
);
1340 WARN("CreateWindow failed: %ld\n", GetLastError());
1342 FreeLibraryAndExitThread(huia_module
, 1);
1345 SetWindowLongPtrW(hwnd
, GWLP_WNDPROC
, (LONG_PTR
)uia_provider_thread_msg_proc
);
1346 provider_thread
.hwnd
= hwnd
;
1348 /* Initialization complete, thread can now process window messages. */
1349 SetEvent(initialized_event
);
1350 TRACE("Provider thread started.\n");
1351 while (GetMessageW(&msg
, NULL
, 0, 0))
1353 if (msg
.message
== WM_UIA_PROVIDER_THREAD_STOP
)
1355 TranslateMessage(&msg
);
1356 DispatchMessageW(&msg
);
1359 TRACE("Shutting down UI Automation provider thread.\n");
1361 DestroyWindow(hwnd
);
1363 FreeLibraryAndExitThread(huia_module
, 0);
1366 static BOOL
uia_start_provider_thread(void)
1368 BOOL started
= TRUE
;
1370 EnterCriticalSection(&provider_thread_cs
);
1371 if (++provider_thread
.ref
== 1)
1378 /* Increment DLL reference count. */
1379 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
1380 (const WCHAR
*)uia_start_provider_thread
, &hmodule
);
1382 list_init(&provider_thread
.nodes_list
);
1383 rb_init(&provider_thread
.node_map
, uia_runtime_id_compare
);
1384 events
[0] = ready_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1385 if (!(provider_thread
.hthread
= CreateThread(NULL
, 0, uia_provider_thread_proc
,
1386 ready_event
, 0, NULL
)))
1388 FreeLibrary(hmodule
);
1393 events
[1] = provider_thread
.hthread
;
1394 wait_obj
= WaitForMultipleObjects(2, events
, FALSE
, INFINITE
);
1395 if (wait_obj
!= WAIT_OBJECT_0
)
1397 CloseHandle(provider_thread
.hthread
);
1402 CloseHandle(ready_event
);
1405 WARN("Failed to start provider thread\n");
1406 memset(&provider_thread
, 0, sizeof(provider_thread
));
1410 LeaveCriticalSection(&provider_thread_cs
);
1414 void uia_stop_provider_thread(void)
1416 EnterCriticalSection(&provider_thread_cs
);
1417 if (!--provider_thread
.ref
)
1419 PostMessageW(provider_thread
.hwnd
, WM_UIA_PROVIDER_THREAD_STOP
, 0, 0);
1420 CloseHandle(provider_thread
.hthread
);
1421 if (!list_empty(&provider_thread
.nodes_list
))
1422 ERR("Provider thread shutdown with nodes still in the list\n");
1423 memset(&provider_thread
, 0, sizeof(provider_thread
));
1425 LeaveCriticalSection(&provider_thread_cs
);
1429 * Pass our IWineUiaNode interface to the provider thread for marshaling. UI
1430 * Automation has to work regardless of whether or not COM is initialized on
1431 * the thread calling UiaReturnRawElementProvider.
1433 LRESULT
uia_lresult_from_node(HUIANODE huianode
)
1435 if (!uia_start_provider_thread())
1437 UiaNodeRelease(huianode
);
1441 return SendMessageW(provider_thread
.hwnd
, WM_GET_OBJECT_UIA_NODE
, 0, (LPARAM
)huianode
);
1444 /***********************************************************************
1445 * UiaReturnRawElementProvider (uiautomationcore.@)
1447 LRESULT WINAPI
UiaReturnRawElementProvider(HWND hwnd
, WPARAM wparam
,
1448 LPARAM lparam
, IRawElementProviderSimple
*elprov
)
1453 TRACE("(%p, %Ix, %#Ix, %p)\n", hwnd
, wparam
, lparam
, elprov
);
1455 if (!wparam
&& !lparam
&& !elprov
)
1457 FIXME("UIA-to-MSAA bridge not implemented, no provider map to free.\n");
1461 if (lparam
!= UiaRootObjectId
)
1463 FIXME("Unsupported object id %Id, ignoring.\n", lparam
);
1467 hr
= create_uia_node_from_elprov(elprov
, &node
, FALSE
);
1470 WARN("Failed to create HUIANODE with hr %#lx\n", hr
);
1474 return uia_lresult_from_node(node
);
1477 /***********************************************************************
1478 * UiaDisconnectProvider (uiautomationcore.@)
1480 HRESULT WINAPI
UiaDisconnectProvider(IRawElementProviderSimple
*elprov
)
1486 TRACE("(%p)\n", elprov
);
1488 hr
= create_uia_node_from_elprov(elprov
, &node
, FALSE
);
1492 hr
= UiaGetRuntimeId(node
, &sa
);
1493 UiaNodeRelease(node
);
1498 return E_INVALIDARG
;
1500 uia_provider_thread_disconnect_node(sa
);
1502 SafeArrayDestroy(sa
);