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"
22 #include "wine/rbtree.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(uiautomation
);
26 static SAFEARRAY
*uia_desktop_node_rt_id
;
27 static BOOL WINAPI
uia_init_desktop_rt_id(INIT_ONCE
*once
, void *param
, void **ctx
)
31 if ((sa
= SafeArrayCreateVector(VT_I4
, 0, 2)))
33 if (SUCCEEDED(write_runtime_id_base(sa
, GetDesktopWindow())))
34 uia_desktop_node_rt_id
= sa
;
39 return !!uia_desktop_node_rt_id
;
42 static SAFEARRAY
*uia_get_desktop_rt_id(void)
44 static INIT_ONCE once
= INIT_ONCE_STATIC_INIT
;
46 if (!uia_desktop_node_rt_id
)
47 InitOnceExecuteOnce(&once
, uia_init_desktop_rt_id
, NULL
, NULL
);
49 return uia_desktop_node_rt_id
;
52 static int win_event_to_uia_event_id(int win_event
)
56 case EVENT_OBJECT_FOCUS
: return UIA_AutomationFocusChangedEventId
;
57 case EVENT_SYSTEM_ALERT
: return UIA_SystemAlertEventId
;
58 case EVENT_OBJECT_SHOW
: return UIA_StructureChangedEventId
;
59 case EVENT_OBJECT_DESTROY
: return UIA_StructureChangedEventId
;
68 static BOOL CALLBACK
uia_win_event_enum_top_level_hwnds(HWND hwnd
, LPARAM lparam
)
70 struct rb_tree
*hwnd_map
= (struct rb_tree
*)lparam
;
73 if (!uia_hwnd_is_visible(hwnd
))
76 hr
= uia_hwnd_map_add_hwnd(hwnd_map
, hwnd
);
78 WARN("Failed to add hwnd to map, hr %#lx\n", hr
);
83 HRESULT
uia_event_add_win_event_hwnd(struct uia_event
*event
, HWND hwnd
)
85 if (!uia_clientside_event_start_event_thread(event
))
88 if (hwnd
== GetDesktopWindow())
89 EnumWindows(uia_win_event_enum_top_level_hwnds
, (LPARAM
)&event
->u
.clientside
.win_event_hwnd_map
);
91 return uia_hwnd_map_add_hwnd(&event
->u
.clientside
.win_event_hwnd_map
, hwnd
);
95 * UI Automation event map.
97 static struct uia_event_map
99 struct rb_tree event_map
;
102 /* rb_tree for serverside events, sorted by PID/event cookie. */
103 struct rb_tree serverside_event_map
;
104 LONG serverside_event_count
;
107 struct uia_event_map_entry
109 struct rb_entry entry
;
115 * List of registered events for this event ID. Events are only removed
116 * from the list when the event map entry reference count hits 0 and the
117 * entry is destroyed. This avoids dealing with mid-list removal while
118 * iterating over the list when an event is raised. Rather than remove
119 * an event from the list, we mark an event as being defunct so it is
122 struct list events_list
;
123 struct list serverside_events_list
;
126 struct uia_event_identifier
{
131 static int uia_serverside_event_id_compare(const void *key
, const struct rb_entry
*entry
)
133 struct uia_event
*event
= RB_ENTRY_VALUE(entry
, struct uia_event
, u
.serverside
.serverside_event_entry
);
134 struct uia_event_identifier
*event_id
= (struct uia_event_identifier
*)key
;
136 if (event_id
->proc_id
!= event
->u
.serverside
.proc_id
)
137 return (event_id
->proc_id
> event
->u
.serverside
.proc_id
) - (event_id
->proc_id
< event
->u
.serverside
.proc_id
);
139 return (event_id
->event_cookie
> event
->event_cookie
) - (event_id
->event_cookie
< event
->event_cookie
);
142 static CRITICAL_SECTION event_map_cs
;
143 static CRITICAL_SECTION_DEBUG event_map_cs_debug
=
146 { &event_map_cs_debug
.ProcessLocksList
, &event_map_cs_debug
.ProcessLocksList
},
147 0, 0, { (DWORD_PTR
)(__FILE__
": event_map_cs") }
149 static CRITICAL_SECTION event_map_cs
= { &event_map_cs_debug
, -1, 0, 0, 0, 0 };
151 static int uia_event_map_id_compare(const void *key
, const struct rb_entry
*entry
)
153 struct uia_event_map_entry
*event_entry
= RB_ENTRY_VALUE(entry
, struct uia_event_map_entry
, entry
);
154 int event_id
= *((int *)key
);
156 return (event_entry
->event_id
> event_id
) - (event_entry
->event_id
< event_id
);
159 static struct uia_event_map_entry
*uia_get_event_map_entry_for_event(int event_id
)
161 struct uia_event_map_entry
*map_entry
= NULL
;
162 struct rb_entry
*rb_entry
;
164 if (uia_event_map
.event_count
&& (rb_entry
= rb_get(&uia_event_map
.event_map
, &event_id
)))
165 map_entry
= RB_ENTRY_VALUE(rb_entry
, struct uia_event_map_entry
, entry
);
170 static HRESULT
uia_event_map_add_event(struct uia_event
*event
)
172 const int subtree_scope
= TreeScope_Element
| TreeScope_Descendants
;
173 struct uia_event_map_entry
*event_entry
;
175 if (((event
->scope
& subtree_scope
) == subtree_scope
) && event
->runtime_id
&&
176 !uia_compare_safearrays(uia_get_desktop_rt_id(), event
->runtime_id
, UIAutomationType_IntArray
))
177 event
->desktop_subtree_event
= TRUE
;
179 EnterCriticalSection(&event_map_cs
);
181 if (!(event_entry
= uia_get_event_map_entry_for_event(event
->event_id
)))
183 if (!(event_entry
= calloc(1, sizeof(*event_entry
))))
185 LeaveCriticalSection(&event_map_cs
);
186 return E_OUTOFMEMORY
;
189 event_entry
->event_id
= event
->event_id
;
190 list_init(&event_entry
->events_list
);
191 list_init(&event_entry
->serverside_events_list
);
193 if (!uia_event_map
.event_count
)
194 rb_init(&uia_event_map
.event_map
, uia_event_map_id_compare
);
195 rb_put(&uia_event_map
.event_map
, &event
->event_id
, &event_entry
->entry
);
196 uia_event_map
.event_count
++;
199 IWineUiaEvent_AddRef(&event
->IWineUiaEvent_iface
);
200 if (event
->event_type
== EVENT_TYPE_SERVERSIDE
)
201 list_add_head(&event_entry
->serverside_events_list
, &event
->event_list_entry
);
203 list_add_head(&event_entry
->events_list
, &event
->event_list_entry
);
204 InterlockedIncrement(&event_entry
->refs
);
206 event
->event_map_entry
= event_entry
;
207 LeaveCriticalSection(&event_map_cs
);
212 static void uia_event_map_entry_release(struct uia_event_map_entry
*entry
)
214 ULONG ref
= InterlockedDecrement(&entry
->refs
);
218 struct list
*cursor
, *cursor2
;
220 EnterCriticalSection(&event_map_cs
);
223 * Someone grabbed this while we were waiting to enter the CS, abort
226 if (InterlockedCompareExchange(&entry
->refs
, 0, 0) != 0)
228 LeaveCriticalSection(&event_map_cs
);
232 rb_remove(&uia_event_map
.event_map
, &entry
->entry
);
233 uia_event_map
.event_count
--;
234 LeaveCriticalSection(&event_map_cs
);
236 /* Release all events in the list. */
237 LIST_FOR_EACH_SAFE(cursor
, cursor2
, &entry
->events_list
)
239 struct uia_event
*event
= LIST_ENTRY(cursor
, struct uia_event
, event_list_entry
);
241 IWineUiaEvent_Release(&event
->IWineUiaEvent_iface
);
244 LIST_FOR_EACH_SAFE(cursor
, cursor2
, &entry
->serverside_events_list
)
246 struct uia_event
*event
= LIST_ENTRY(cursor
, struct uia_event
, event_list_entry
);
248 IWineUiaEvent_Release(&event
->IWineUiaEvent_iface
);
255 HRESULT
uia_event_for_each(int event_id
, UiaWineEventForEachCallback
*callback
, void *user_data
,
256 BOOL clientside_only
)
258 struct uia_event_map_entry
*event_entry
;
262 EnterCriticalSection(&event_map_cs
);
263 if ((event_entry
= uia_get_event_map_entry_for_event(event_id
)))
264 InterlockedIncrement(&event_entry
->refs
);
265 LeaveCriticalSection(&event_map_cs
);
270 for (i
= 0; i
< 2; i
++)
272 struct list
*events
= !i
? &event_entry
->events_list
: &event_entry
->serverside_events_list
;
273 struct list
*cursor
, *cursor2
;
275 if (i
&& clientside_only
)
278 LIST_FOR_EACH_SAFE(cursor
, cursor2
, events
)
280 struct uia_event
*event
= LIST_ENTRY(cursor
, struct uia_event
, event_list_entry
);
282 /* Event is no longer valid. */
283 if (InterlockedCompareExchange(&event
->event_defunct
, 0, 0) != 0)
286 hr
= callback(event
, user_data
);
294 WARN("Event callback failed with hr %#lx\n", hr
);
295 uia_event_map_entry_release(event_entry
);
300 * Functions for struct uia_event_args, a reference counted structure
301 * used to store event arguments. This is necessary for serverside events
302 * as they're raised on a background thread after the event raising
303 * function has returned.
305 static struct uia_event_args
*create_uia_event_args(const struct uia_event_info
*event_info
)
307 struct uia_event_args
*args
= calloc(1, sizeof(*args
));
312 args
->simple_args
.Type
= event_info
->event_arg_type
;
313 args
->simple_args
.EventId
= event_info
->event_id
;
319 static void uia_event_args_release(struct uia_event_args
*args
)
321 if (!InterlockedDecrement(&args
->ref
))
325 struct event_sink_event
327 struct list event_sink_list_entry
;
329 IRawElementProviderSimple
*elprov
;
330 struct uia_event_args
*args
;
333 static HRESULT
uia_event_sink_list_add_event(struct list
*sink_events
, IRawElementProviderSimple
*elprov
,
334 struct uia_event_args
*args
)
336 struct event_sink_event
*sink_event
= calloc(1, sizeof(*sink_event
));
339 return E_OUTOFMEMORY
;
341 IRawElementProviderSimple_AddRef(elprov
);
342 InterlockedIncrement(&args
->ref
);
344 sink_event
->elprov
= elprov
;
345 sink_event
->args
= args
;
346 list_add_tail(sink_events
, &sink_event
->event_sink_list_entry
);
352 * IProxyProviderWinEventSink interface implementation.
354 struct uia_proxy_win_event_sink
{
355 IProxyProviderWinEventSink IProxyProviderWinEventSink_iface
;
361 struct list sink_events
;
364 static inline struct uia_proxy_win_event_sink
*impl_from_IProxyProviderWinEventSink(IProxyProviderWinEventSink
*iface
)
366 return CONTAINING_RECORD(iface
, struct uia_proxy_win_event_sink
, IProxyProviderWinEventSink_iface
);
369 static HRESULT WINAPI
uia_proxy_win_event_sink_QueryInterface(IProxyProviderWinEventSink
*iface
, REFIID riid
, void **obj
)
371 struct uia_proxy_win_event_sink
*sink
= impl_from_IProxyProviderWinEventSink(iface
);
374 if (IsEqualIID(riid
, &IID_IProxyProviderWinEventSink
) || IsEqualIID(riid
, &IID_IUnknown
))
376 else if (IsEqualIID(riid
, &IID_IMarshal
))
377 return IUnknown_QueryInterface(sink
->marshal
, riid
, obj
);
379 return E_NOINTERFACE
;
381 IProxyProviderWinEventSink_AddRef(iface
);
385 static ULONG WINAPI
uia_proxy_win_event_sink_AddRef(IProxyProviderWinEventSink
*iface
)
387 struct uia_proxy_win_event_sink
*sink
= impl_from_IProxyProviderWinEventSink(iface
);
388 ULONG ref
= InterlockedIncrement(&sink
->ref
);
390 TRACE("%p, refcount %ld\n", sink
, ref
);
395 static ULONG WINAPI
uia_proxy_win_event_sink_Release(IProxyProviderWinEventSink
*iface
)
397 struct uia_proxy_win_event_sink
*sink
= impl_from_IProxyProviderWinEventSink(iface
);
398 ULONG ref
= InterlockedDecrement(&sink
->ref
);
400 TRACE("%p, refcount %ld\n", sink
, ref
);
404 assert(list_empty(&sink
->sink_events
));
405 IUnknown_Release(sink
->marshal
);
412 static HRESULT WINAPI
uia_proxy_win_event_sink_AddAutomationPropertyChangedEvent(IProxyProviderWinEventSink
*iface
,
413 IRawElementProviderSimple
*elprov
, PROPERTYID prop_id
, VARIANT new_value
)
415 FIXME("%p, %p, %d, %s: stub\n", iface
, elprov
, prop_id
, debugstr_variant(&new_value
));
419 static HRESULT WINAPI
uia_proxy_win_event_sink_AddAutomationEvent(IProxyProviderWinEventSink
*iface
,
420 IRawElementProviderSimple
*elprov
, EVENTID event_id
)
422 struct uia_proxy_win_event_sink
*sink
= impl_from_IProxyProviderWinEventSink(iface
);
423 struct uia_event_args
*args
;
426 TRACE("%p, %p, %d\n", iface
, elprov
, event_id
);
428 if (event_id
!= sink
->event_id
)
431 args
= create_uia_event_args(uia_event_info_from_id(event_id
));
433 return E_OUTOFMEMORY
;
435 if (InterlockedCompareExchange(&sink
->sink_defunct
, 0, 0) == 0)
436 hr
= uia_event_sink_list_add_event(&sink
->sink_events
, elprov
, args
);
437 uia_event_args_release(args
);
441 static HRESULT WINAPI
uia_proxy_win_event_sink_AddStructureChangedEvent(IProxyProviderWinEventSink
*iface
,
442 IRawElementProviderSimple
*elprov
, enum StructureChangeType structure_change_type
, SAFEARRAY
*runtime_id
)
444 FIXME("%p, %p, %d, %p: stub\n", iface
, elprov
, structure_change_type
, runtime_id
);
448 static const IProxyProviderWinEventSinkVtbl uia_proxy_event_sink_vtbl
= {
449 uia_proxy_win_event_sink_QueryInterface
,
450 uia_proxy_win_event_sink_AddRef
,
451 uia_proxy_win_event_sink_Release
,
452 uia_proxy_win_event_sink_AddAutomationPropertyChangedEvent
,
453 uia_proxy_win_event_sink_AddAutomationEvent
,
454 uia_proxy_win_event_sink_AddStructureChangedEvent
,
457 static HRESULT
create_proxy_win_event_sink(struct uia_proxy_win_event_sink
**out_sink
, int event_id
)
459 struct uia_proxy_win_event_sink
*sink
= calloc(1, sizeof(*sink
));
464 return E_OUTOFMEMORY
;
466 sink
->IProxyProviderWinEventSink_iface
.lpVtbl
= &uia_proxy_event_sink_vtbl
;
468 sink
->event_id
= event_id
;
469 list_init(&sink
->sink_events
);
471 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&sink
->IProxyProviderWinEventSink_iface
, &sink
->marshal
);
483 * UI Automation event thread.
485 struct uia_event_thread
491 struct list
*event_queue
;
495 #define WM_UIA_EVENT_THREAD_STOP (WM_USER + 1)
496 #define WM_UIA_EVENT_THREAD_PROCESS_QUEUE (WM_USER + 2)
497 static struct uia_event_thread event_thread
;
498 static CRITICAL_SECTION event_thread_cs
;
499 static CRITICAL_SECTION_DEBUG event_thread_cs_debug
=
501 0, 0, &event_thread_cs
,
502 { &event_thread_cs_debug
.ProcessLocksList
, &event_thread_cs_debug
.ProcessLocksList
},
503 0, 0, { (DWORD_PTR
)(__FILE__
": event_thread_cs") }
505 static CRITICAL_SECTION event_thread_cs
= { &event_thread_cs_debug
, -1, 0, 0, 0, 0 };
507 enum uia_queue_event_type
{
508 QUEUE_EVENT_TYPE_SERVERSIDE
,
509 QUEUE_EVENT_TYPE_CLIENTSIDE
,
510 QUEUE_EVENT_TYPE_WIN_EVENT
,
513 struct uia_queue_event
515 struct list event_queue_entry
;
516 int queue_event_type
;
519 struct uia_queue_uia_event
521 struct uia_queue_event queue_entry
;
523 struct uia_event_args
*args
;
524 struct uia_event
*event
;
528 HUIANODE nav_start_node
;
532 LRESULT nav_start_node
;
537 struct uia_queue_win_event
539 struct uia_queue_event queue_entry
;
550 static void uia_event_queue_push(struct uia_queue_event
*event
, int queue_event_type
)
552 event
->queue_event_type
= queue_event_type
;
553 EnterCriticalSection(&event_thread_cs
);
555 if (queue_event_type
== QUEUE_EVENT_TYPE_WIN_EVENT
)
557 struct uia_queue_win_event
*win_event
= (struct uia_queue_win_event
*)event
;
559 if (win_event
->hook
!= event_thread
.hook
)
566 assert(event_thread
.event_queue
);
567 list_add_tail(event_thread
.event_queue
, &event
->event_queue_entry
);
568 PostMessageW(event_thread
.hwnd
, WM_UIA_EVENT_THREAD_PROCESS_QUEUE
, 0, 0);
571 LeaveCriticalSection(&event_thread_cs
);
574 static struct uia_queue_event
*uia_event_queue_pop(struct list
*event_queue
)
576 struct uia_queue_event
*queue_event
= NULL
;
578 EnterCriticalSection(&event_thread_cs
);
580 if (!list_empty(event_queue
))
582 queue_event
= LIST_ENTRY(list_head(event_queue
), struct uia_queue_event
, event_queue_entry
);
583 list_remove(list_head(event_queue
));
586 LeaveCriticalSection(&event_thread_cs
);
590 static HRESULT
uia_raise_clientside_event(struct uia_queue_uia_event
*event
)
592 HUIANODE node
, nav_start_node
;
595 node
= nav_start_node
= NULL
;
596 hr
= uia_node_from_lresult(event
->u
.clientside
.node
, &node
, 0);
599 WARN("Failed to create node from lresult, hr %#lx\n", hr
);
600 uia_node_lresult_release(event
->u
.clientside
.nav_start_node
);
604 if (event
->u
.clientside
.nav_start_node
)
606 hr
= uia_node_from_lresult(event
->u
.clientside
.nav_start_node
, &nav_start_node
, 0);
609 WARN("Failed to create nav_start_node from lresult, hr %#lx\n", hr
);
610 UiaNodeRelease(node
);
615 hr
= uia_event_invoke(node
, nav_start_node
, event
->args
, event
->event
);
616 UiaNodeRelease(node
);
617 UiaNodeRelease(nav_start_node
);
622 static HRESULT
uia_raise_serverside_event(struct uia_queue_uia_event
*event
)
629 * uia_lresult_from_node is expected to release the node here upon
633 if (!(lr
= uia_lresult_from_node(event
->u
.serverside
.node
)))
635 UiaNodeRelease(event
->u
.serverside
.nav_start_node
);
639 if (event
->u
.serverside
.nav_start_node
&& !(lr2
= uia_lresult_from_node(event
->u
.serverside
.nav_start_node
)))
641 uia_node_lresult_release(lr
);
646 variant_init_i4(&v
, lr
);
648 variant_init_i4(&v2
, lr2
);
650 hr
= IWineUiaEvent_raise_event(event
->event
->u
.serverside
.event_iface
, v
, v2
);
653 uia_node_lresult_release(lr
);
654 uia_node_lresult_release(lr2
);
660 /* Check the parent chain of HWNDs, excluding the desktop. */
661 static BOOL
uia_win_event_hwnd_map_contains_ancestors(struct rb_tree
*hwnd_map
, HWND hwnd
)
663 HWND parent
= GetAncestor(hwnd
, GA_PARENT
);
664 const HWND desktop
= GetDesktopWindow();
666 while (parent
&& (parent
!= desktop
))
668 if (uia_hwnd_map_check_hwnd(hwnd_map
, parent
))
671 parent
= GetAncestor(parent
, GA_PARENT
);
677 HRESULT
create_msaa_provider_from_hwnd(HWND hwnd
, int in_child_id
, IRawElementProviderSimple
**ret_elprov
)
679 IRawElementProviderSimple
*elprov
;
685 hr
= AccessibleObjectFromWindow(hwnd
, OBJID_CLIENT
, &IID_IAccessible
, (void **)&acc
);
689 child_id
= in_child_id
;
690 if (in_child_id
!= CHILDID_SELF
)
696 variant_init_i4(&cid
, in_child_id
);
697 hr
= IAccessible_get_accChild(acc
, cid
, &disp
);
699 TRACE("get_accChild failed with %#lx!\n", hr
);
701 if (SUCCEEDED(hr
) && disp
)
703 IAccessible_Release(acc
);
704 hr
= IDispatch_QueryInterface(disp
, &IID_IAccessible
, (void **)&acc
);
705 IDispatch_Release(disp
);
709 child_id
= CHILDID_SELF
;
713 hr
= create_msaa_provider(acc
, child_id
, hwnd
, TRUE
, in_child_id
== CHILDID_SELF
, &elprov
);
714 IAccessible_Release(acc
);
718 *ret_elprov
= elprov
;
722 struct uia_elprov_event_data
724 IRawElementProviderSimple
*elprov
;
725 struct uia_event_args
*args
;
726 BOOL clientside_only
;
732 static HRESULT
uia_raise_elprov_event_callback(struct uia_event
*event
, void *data
);
733 static HRESULT
uia_win_event_for_each_callback(struct uia_event
*event
, void *data
)
735 struct uia_queue_win_event
*win_event
= (struct uia_queue_win_event
*)data
;
736 struct event_sink_event
*sink_event
, *sink_event2
;
737 struct uia_proxy_win_event_sink
*sink
;
738 IRawElementProviderSimple
*elprov
;
739 struct uia_node
*node_data
;
745 * Check if this HWND, or any of it's ancestors (excluding the desktop)
748 if (!uia_hwnd_map_check_hwnd(&event
->u
.clientside
.win_event_hwnd_map
, win_event
->hwnd
) &&
749 !uia_win_event_hwnd_map_contains_ancestors(&event
->u
.clientside
.win_event_hwnd_map
, win_event
->hwnd
))
752 /* Has a native serverside provider, no need to do WinEvent translation. */
753 if (UiaHasServerSideProvider(win_event
->hwnd
))
757 * Regardless of the object ID of the WinEvent, OBJID_CLIENT is queried
758 * for the HWND with the same child ID as the WinEvent.
760 hr
= create_msaa_provider_from_hwnd(win_event
->hwnd
, win_event
->child_id
, &elprov
);
764 hr
= create_uia_node_from_elprov(elprov
, &node
, TRUE
, NODE_FLAG_IGNORE_COM_THREADING
);
765 IRawElementProviderSimple_Release(elprov
);
769 hr
= create_proxy_win_event_sink(&sink
, event
->event_id
);
772 node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
773 for (i
= 0; i
< node_data
->prov_count
; i
++)
775 hr
= respond_to_win_event_on_node_provider((IWineUiaNode
*)node
, i
, win_event
->event_id
, win_event
->hwnd
, win_event
->obj_id
,
776 win_event
->child_id
, &sink
->IProxyProviderWinEventSink_iface
);
777 if (FAILED(hr
) || !list_empty(&sink
->sink_events
))
781 InterlockedIncrement(&sink
->sink_defunct
);
782 LIST_FOR_EACH_ENTRY_SAFE(sink_event
, sink_event2
, &sink
->sink_events
, struct event_sink_event
, event_sink_list_entry
)
784 struct uia_elprov_event_data event_data
= { sink_event
->elprov
, sink_event
->args
, TRUE
};
785 list_remove(&sink_event
->event_sink_list_entry
);
787 hr
= uia_raise_elprov_event_callback(event
, (void *)&event_data
);
789 WARN("uia_raise_elprov_event_callback failed with hr %#lx\n", hr
);
791 UiaNodeRelease(event_data
.node
);
792 SafeArrayDestroy(event_data
.rt_id
);
794 IRawElementProviderSimple_Release(sink_event
->elprov
);
795 uia_event_args_release(sink_event
->args
);
799 IProxyProviderWinEventSink_Release(&sink
->IProxyProviderWinEventSink_iface
);
802 UiaNodeRelease(node
);
806 static void uia_event_thread_process_queue(struct list
*event_queue
)
810 struct uia_queue_event
*event
;
813 if (!(event
= uia_event_queue_pop(event_queue
)))
816 switch (event
->queue_event_type
)
818 case QUEUE_EVENT_TYPE_SERVERSIDE
:
819 case QUEUE_EVENT_TYPE_CLIENTSIDE
:
821 struct uia_queue_uia_event
*uia_event
= (struct uia_queue_uia_event
*)event
;
823 if (event
->queue_event_type
== QUEUE_EVENT_TYPE_SERVERSIDE
)
824 hr
= uia_raise_serverside_event(uia_event
);
826 hr
= uia_raise_clientside_event(uia_event
);
828 uia_event_args_release(uia_event
->args
);
829 IWineUiaEvent_Release(&uia_event
->event
->IWineUiaEvent_iface
);
833 case QUEUE_EVENT_TYPE_WIN_EVENT
:
835 struct uia_queue_win_event
*win_event
= (struct uia_queue_win_event
*)event
;
837 hr
= uia_com_win_event_callback(win_event
->event_id
, win_event
->hwnd
, win_event
->obj_id
, win_event
->child_id
,
838 win_event
->thread_id
, win_event
->event_time
);
840 WARN("uia_com_win_event_callback failed with hr %#lx\n", hr
);
842 hr
= uia_event_for_each(win_event_to_uia_event_id(win_event
->event_id
), uia_win_event_for_each_callback
,
843 (void *)win_event
, TRUE
);
852 WARN("Failed to raise event type %d with hr %#lx\n", event
->queue_event_type
, hr
);
858 static void CALLBACK
uia_event_thread_win_event_proc(HWINEVENTHOOK hook
, DWORD event_id
, HWND hwnd
, LONG obj_id
,
859 LONG child_id
, DWORD thread_id
, DWORD event_time
)
861 struct uia_queue_win_event
*win_event
;
863 TRACE("%p, %ld, %p, %ld, %ld, %ld, %ld\n", hook
, event_id
, hwnd
, obj_id
, child_id
, thread_id
, event_time
);
865 if (!win_event_to_uia_event_id(event_id
))
868 if (!(win_event
= calloc(1, sizeof(*win_event
))))
870 ERR("Failed to allocate uia_queue_win_event structure\n");
874 win_event
->hook
= hook
;
875 win_event
->event_id
= event_id
;
876 win_event
->hwnd
= hwnd
;
877 win_event
->obj_id
= obj_id
;
878 win_event
->child_id
= child_id
;
879 win_event
->thread_id
= thread_id
;
880 win_event
->event_time
= event_time
;
881 uia_event_queue_push(&win_event
->queue_entry
, QUEUE_EVENT_TYPE_WIN_EVENT
);
884 static DWORD WINAPI
uia_event_thread_proc(void *arg
)
886 HANDLE initialized_event
= arg
;
887 struct list event_queue
;
892 list_init(&event_queue
);
893 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
894 hwnd
= CreateWindowW(L
"Message", NULL
, 0, 0, 0, 0, 0, HWND_MESSAGE
, NULL
, NULL
, NULL
);
897 WARN("CreateWindow failed: %ld\n", GetLastError());
899 FreeLibraryAndExitThread(huia_module
, 1);
902 event_thread
.hwnd
= hwnd
;
903 event_thread
.event_queue
= &event_queue
;
904 event_thread
.hook
= hook
= SetWinEventHook(EVENT_MIN
, EVENT_MAX
, 0, uia_event_thread_win_event_proc
, 0, 0,
905 WINEVENT_OUTOFCONTEXT
);
907 /* Initialization complete, thread can now process window messages. */
908 SetEvent(initialized_event
);
909 TRACE("Event thread started.\n");
910 while (GetMessageW(&msg
, NULL
, 0, 0))
912 if ((msg
.hwnd
== hwnd
) && ((msg
.message
== WM_UIA_EVENT_THREAD_STOP
) ||
913 (msg
.message
== WM_UIA_EVENT_THREAD_PROCESS_QUEUE
)))
915 uia_event_thread_process_queue(&event_queue
);
916 if (msg
.message
== WM_UIA_EVENT_THREAD_STOP
)
920 TranslateMessage(&msg
);
921 DispatchMessageW(&msg
);
924 TRACE("Shutting down UI Automation event thread.\n");
926 UnhookWinEvent(hook
);
929 FreeLibraryAndExitThread(huia_module
, 0);
932 static BOOL
uia_start_event_thread(void)
936 EnterCriticalSection(&event_thread_cs
);
937 if (++event_thread
.ref
== 1)
939 HANDLE ready_event
= NULL
;
944 /* Increment DLL reference count. */
945 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
946 (const WCHAR
*)uia_start_event_thread
, &hmodule
);
948 events
[0] = ready_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
949 if (!(event_thread
.hthread
= CreateThread(NULL
, 0, uia_event_thread_proc
,
950 ready_event
, 0, NULL
)))
952 FreeLibrary(hmodule
);
957 events
[1] = event_thread
.hthread
;
958 wait_obj
= WaitForMultipleObjects(2, events
, FALSE
, INFINITE
);
959 if (wait_obj
!= WAIT_OBJECT_0
)
961 CloseHandle(event_thread
.hthread
);
967 CloseHandle(ready_event
);
969 memset(&event_thread
, 0, sizeof(event_thread
));
972 LeaveCriticalSection(&event_thread_cs
);
976 static void uia_stop_event_thread(void)
978 EnterCriticalSection(&event_thread_cs
);
979 if (!--event_thread
.ref
)
981 PostMessageW(event_thread
.hwnd
, WM_UIA_EVENT_THREAD_STOP
, 0, 0);
982 CloseHandle(event_thread
.hthread
);
983 memset(&event_thread
, 0, sizeof(event_thread
));
985 LeaveCriticalSection(&event_thread_cs
);
988 BOOL
uia_clientside_event_start_event_thread(struct uia_event
*event
)
990 if (!event
->u
.clientside
.event_thread_started
)
991 event
->u
.clientside
.event_thread_started
= uia_start_event_thread();
993 return event
->u
.clientside
.event_thread_started
;
997 * IWineUiaEvent interface.
999 static inline struct uia_event
*impl_from_IWineUiaEvent(IWineUiaEvent
*iface
)
1001 return CONTAINING_RECORD(iface
, struct uia_event
, IWineUiaEvent_iface
);
1004 static HRESULT WINAPI
uia_event_QueryInterface(IWineUiaEvent
*iface
, REFIID riid
, void **ppv
)
1007 if (IsEqualIID(riid
, &IID_IWineUiaEvent
) || IsEqualIID(riid
, &IID_IUnknown
))
1010 return E_NOINTERFACE
;
1012 IWineUiaEvent_AddRef(iface
);
1016 static ULONG WINAPI
uia_event_AddRef(IWineUiaEvent
*iface
)
1018 struct uia_event
*event
= impl_from_IWineUiaEvent(iface
);
1019 ULONG ref
= InterlockedIncrement(&event
->ref
);
1021 TRACE("%p, refcount %ld\n", event
, ref
);
1025 static ULONG WINAPI
uia_event_Release(IWineUiaEvent
*iface
)
1027 struct uia_event
*event
= impl_from_IWineUiaEvent(iface
);
1028 ULONG ref
= InterlockedDecrement(&event
->ref
);
1030 TRACE("%p, refcount %ld\n", event
, ref
);
1036 * If this event has an event_map_entry, it should've been released
1037 * before hitting a reference count of 0.
1039 assert(!event
->event_map_entry
);
1041 SafeArrayDestroy(event
->runtime_id
);
1042 if (event
->event_type
== EVENT_TYPE_CLIENTSIDE
)
1044 uia_cache_request_destroy(&event
->u
.clientside
.cache_req
);
1045 if (event
->u
.clientside
.event_thread_started
)
1046 uia_stop_event_thread();
1047 uia_hwnd_map_destroy(&event
->u
.clientside
.win_event_hwnd_map
);
1051 EnterCriticalSection(&event_map_cs
);
1052 rb_remove(&uia_event_map
.serverside_event_map
, &event
->u
.serverside
.serverside_event_entry
);
1053 uia_event_map
.serverside_event_count
--;
1054 LeaveCriticalSection(&event_map_cs
);
1055 if (event
->u
.serverside
.event_iface
)
1056 IWineUiaEvent_Release(event
->u
.serverside
.event_iface
);
1057 uia_stop_event_thread();
1060 for (i
= 0; i
< event
->event_advisers_count
; i
++)
1061 IWineUiaEventAdviser_Release(event
->event_advisers
[i
]);
1062 free(event
->event_advisers
);
1069 static HRESULT WINAPI
uia_event_advise_events(IWineUiaEvent
*iface
, BOOL advise_added
, LONG adviser_start_idx
)
1071 struct uia_event
*event
= impl_from_IWineUiaEvent(iface
);
1075 TRACE("%p, %d, %ld\n", event
, advise_added
, adviser_start_idx
);
1077 for (i
= adviser_start_idx
; i
< event
->event_advisers_count
; i
++)
1079 hr
= IWineUiaEventAdviser_advise(event
->event_advisers
[i
], advise_added
, (UINT_PTR
)event
);
1085 * First call to advise events on a serverside provider, add it to the
1086 * events list so it can be raised.
1088 if (!adviser_start_idx
&& advise_added
&& event
->event_type
== EVENT_TYPE_SERVERSIDE
)
1090 hr
= uia_event_map_add_event(event
);
1092 WARN("Failed to add event to event map, hr %#lx\n", hr
);
1096 * Once we've advised of removal, no need to keep the advisers around.
1097 * We can also release our reference to the event map.
1101 InterlockedIncrement(&event
->event_defunct
);
1102 uia_event_map_entry_release(event
->event_map_entry
);
1103 event
->event_map_entry
= NULL
;
1105 for (i
= 0; i
< event
->event_advisers_count
; i
++)
1106 IWineUiaEventAdviser_Release(event
->event_advisers
[i
]);
1107 free(event
->event_advisers
);
1108 event
->event_advisers_count
= event
->event_advisers_arr_size
= 0;
1114 static HRESULT WINAPI
uia_event_set_event_data(IWineUiaEvent
*iface
, const GUID
*event_guid
, LONG scope
,
1115 VARIANT runtime_id
, IWineUiaEvent
*event_iface
)
1117 struct uia_event
*event
= impl_from_IWineUiaEvent(iface
);
1119 TRACE("%p, %s, %ld, %s, %p\n", event
, debugstr_guid(event_guid
), scope
, debugstr_variant(&runtime_id
), event_iface
);
1121 assert(event
->event_type
== EVENT_TYPE_SERVERSIDE
);
1123 event
->event_id
= UiaLookupId(AutomationIdentifierType_Event
, event_guid
);
1124 event
->scope
= scope
;
1125 if (V_VT(&runtime_id
) == (VT_I4
| VT_ARRAY
))
1129 hr
= SafeArrayCopy(V_ARRAY(&runtime_id
), &event
->runtime_id
);
1132 WARN("Failed to copy runtime id, hr %#lx\n", hr
);
1136 event
->u
.serverside
.event_iface
= event_iface
;
1137 IWineUiaEvent_AddRef(event_iface
);
1142 static HRESULT WINAPI
uia_event_raise_event(IWineUiaEvent
*iface
, VARIANT in_node
, VARIANT in_nav_start_node
)
1144 struct uia_event
*event
= impl_from_IWineUiaEvent(iface
);
1145 struct uia_queue_uia_event
*queue_event
;
1146 struct uia_event_args
*args
;
1148 TRACE("%p, %s, %s\n", iface
, debugstr_variant(&in_node
), debugstr_variant(&in_nav_start_node
));
1150 assert(event
->event_type
!= EVENT_TYPE_SERVERSIDE
);
1152 if (!(queue_event
= calloc(1, sizeof(*queue_event
))))
1153 return E_OUTOFMEMORY
;
1155 if (!(args
= create_uia_event_args(uia_event_info_from_id(event
->event_id
))))
1158 return E_OUTOFMEMORY
;
1161 queue_event
->args
= args
;
1162 queue_event
->event
= event
;
1163 queue_event
->u
.clientside
.node
= V_I4(&in_node
);
1164 if (V_VT(&in_nav_start_node
) == VT_I4
)
1165 queue_event
->u
.clientside
.nav_start_node
= V_I4(&in_nav_start_node
);
1167 IWineUiaEvent_AddRef(&event
->IWineUiaEvent_iface
);
1168 uia_event_queue_push(&queue_event
->queue_entry
, QUEUE_EVENT_TYPE_CLIENTSIDE
);
1173 static const IWineUiaEventVtbl uia_event_vtbl
= {
1174 uia_event_QueryInterface
,
1177 uia_event_advise_events
,
1178 uia_event_set_event_data
,
1179 uia_event_raise_event
,
1182 static struct uia_event
*unsafe_impl_from_IWineUiaEvent(IWineUiaEvent
*iface
)
1184 if (!iface
|| (iface
->lpVtbl
!= &uia_event_vtbl
))
1187 return CONTAINING_RECORD(iface
, struct uia_event
, IWineUiaEvent_iface
);
1190 static HRESULT
create_uia_event(struct uia_event
**out_event
, LONG event_cookie
, int event_type
)
1192 struct uia_event
*event
= calloc(1, sizeof(*event
));
1196 return E_OUTOFMEMORY
;
1198 event
->IWineUiaEvent_iface
.lpVtbl
= &uia_event_vtbl
;
1200 event
->event_cookie
= event_cookie
;
1201 event
->event_type
= event_type
;
1207 static HRESULT
create_clientside_uia_event(struct uia_event
**out_event
, int event_id
, int scope
,
1208 UiaWineEventCallback
*cback
, void *cback_data
, SAFEARRAY
*runtime_id
)
1210 struct uia_event
*event
= NULL
;
1211 static LONG next_event_cookie
;
1215 hr
= create_uia_event(&event
, InterlockedIncrement(&next_event_cookie
), EVENT_TYPE_CLIENTSIDE
);
1219 event
->runtime_id
= runtime_id
;
1220 event
->event_id
= event_id
;
1221 event
->scope
= scope
;
1222 event
->u
.clientside
.event_callback
= cback
;
1223 event
->u
.clientside
.callback_data
= cback_data
;
1224 uia_hwnd_map_init(&event
->u
.clientside
.win_event_hwnd_map
);
1230 HRESULT
create_serverside_uia_event(struct uia_event
**out_event
, LONG process_id
, LONG event_cookie
)
1232 struct uia_event_identifier event_identifier
= { event_cookie
, process_id
};
1233 struct rb_entry
*rb_entry
;
1234 struct uia_event
*event
;
1238 * Attempt to lookup an existing event for this PID/event_cookie. If there
1239 * is one, return S_FALSE.
1242 EnterCriticalSection(&event_map_cs
);
1243 if (uia_event_map
.serverside_event_count
&& (rb_entry
= rb_get(&uia_event_map
.serverside_event_map
, &event_identifier
)))
1245 *out_event
= RB_ENTRY_VALUE(rb_entry
, struct uia_event
, u
.serverside
.serverside_event_entry
);
1250 hr
= create_uia_event(&event
, event_cookie
, EVENT_TYPE_SERVERSIDE
);
1254 if (!uia_start_event_thread())
1261 event
->u
.serverside
.proc_id
= process_id
;
1262 uia_event_map
.serverside_event_count
++;
1263 if (uia_event_map
.serverside_event_count
== 1)
1264 rb_init(&uia_event_map
.serverside_event_map
, uia_serverside_event_id_compare
);
1265 rb_put(&uia_event_map
.serverside_event_map
, &event_identifier
, &event
->u
.serverside
.serverside_event_entry
);
1269 LeaveCriticalSection(&event_map_cs
);
1273 static HRESULT
uia_event_add_event_adviser(IWineUiaEventAdviser
*adviser
, struct uia_event
*event
)
1275 if (!uia_array_reserve((void **)&event
->event_advisers
, &event
->event_advisers_arr_size
,
1276 event
->event_advisers_count
+ 1, sizeof(*event
->event_advisers
)))
1277 return E_OUTOFMEMORY
;
1279 event
->event_advisers
[event
->event_advisers_count
] = adviser
;
1280 IWineUiaEventAdviser_AddRef(adviser
);
1281 event
->event_advisers_count
++;
1287 * IWineUiaEventAdviser interface.
1289 struct uia_event_adviser
{
1290 IWineUiaEventAdviser IWineUiaEventAdviser_iface
;
1293 IRawElementProviderAdviseEvents
*advise_events
;
1297 static inline struct uia_event_adviser
*impl_from_IWineUiaEventAdviser(IWineUiaEventAdviser
*iface
)
1299 return CONTAINING_RECORD(iface
, struct uia_event_adviser
, IWineUiaEventAdviser_iface
);
1302 static HRESULT WINAPI
uia_event_adviser_QueryInterface(IWineUiaEventAdviser
*iface
, REFIID riid
, void **ppv
)
1305 if (IsEqualIID(riid
, &IID_IWineUiaEventAdviser
) || IsEqualIID(riid
, &IID_IUnknown
))
1308 return E_NOINTERFACE
;
1310 IWineUiaEventAdviser_AddRef(iface
);
1314 static ULONG WINAPI
uia_event_adviser_AddRef(IWineUiaEventAdviser
*iface
)
1316 struct uia_event_adviser
*adv_events
= impl_from_IWineUiaEventAdviser(iface
);
1317 ULONG ref
= InterlockedIncrement(&adv_events
->ref
);
1319 TRACE("%p, refcount %ld\n", adv_events
, ref
);
1323 static ULONG WINAPI
uia_event_adviser_Release(IWineUiaEventAdviser
*iface
)
1325 struct uia_event_adviser
*adv_events
= impl_from_IWineUiaEventAdviser(iface
);
1326 ULONG ref
= InterlockedDecrement(&adv_events
->ref
);
1328 TRACE("%p, refcount %ld\n", adv_events
, ref
);
1331 if (adv_events
->git_cookie
)
1333 if (FAILED(unregister_interface_in_git(adv_events
->git_cookie
)))
1334 WARN("Failed to revoke advise events interface from GIT\n");
1336 IRawElementProviderAdviseEvents_Release(adv_events
->advise_events
);
1343 static HRESULT WINAPI
uia_event_adviser_advise(IWineUiaEventAdviser
*iface
, BOOL advise_added
, LONG_PTR huiaevent
)
1345 struct uia_event_adviser
*adv_events
= impl_from_IWineUiaEventAdviser(iface
);
1346 struct uia_event
*event_data
= (struct uia_event
*)huiaevent
;
1347 IRawElementProviderAdviseEvents
*advise_events
;
1350 TRACE("%p, %d, %#Ix\n", adv_events
, advise_added
, huiaevent
);
1352 if (adv_events
->git_cookie
)
1354 hr
= get_interface_in_git(&IID_IRawElementProviderAdviseEvents
, adv_events
->git_cookie
,
1355 (IUnknown
**)&advise_events
);
1361 advise_events
= adv_events
->advise_events
;
1362 IRawElementProviderAdviseEvents_AddRef(advise_events
);
1366 hr
= IRawElementProviderAdviseEvents_AdviseEventAdded(advise_events
, event_data
->event_id
, NULL
);
1368 hr
= IRawElementProviderAdviseEvents_AdviseEventRemoved(advise_events
, event_data
->event_id
, NULL
);
1370 IRawElementProviderAdviseEvents_Release(advise_events
);
1374 static const IWineUiaEventAdviserVtbl uia_event_adviser_vtbl
= {
1375 uia_event_adviser_QueryInterface
,
1376 uia_event_adviser_AddRef
,
1377 uia_event_adviser_Release
,
1378 uia_event_adviser_advise
,
1381 HRESULT
uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents
*advise_events
, struct uia_event
*event
)
1383 struct uia_event_adviser
*adv_events
;
1384 IRawElementProviderSimple
*elprov
;
1385 enum ProviderOptions prov_opts
;
1388 hr
= IRawElementProviderAdviseEvents_QueryInterface(advise_events
, &IID_IRawElementProviderSimple
,
1392 ERR("Failed to get IRawElementProviderSimple from advise events\n");
1396 hr
= IRawElementProviderSimple_get_ProviderOptions(elprov
, &prov_opts
);
1397 IRawElementProviderSimple_Release(elprov
);
1401 if (!(adv_events
= calloc(1, sizeof(*adv_events
))))
1402 return E_OUTOFMEMORY
;
1404 if (prov_opts
& ProviderOptions_UseComThreading
)
1406 hr
= register_interface_in_git((IUnknown
*)advise_events
, &IID_IRawElementProviderAdviseEvents
,
1407 &adv_events
->git_cookie
);
1415 adv_events
->IWineUiaEventAdviser_iface
.lpVtbl
= &uia_event_adviser_vtbl
;
1416 adv_events
->ref
= 1;
1417 adv_events
->advise_events
= advise_events
;
1418 IRawElementProviderAdviseEvents_AddRef(advise_events
);
1420 hr
= uia_event_add_event_adviser(&adv_events
->IWineUiaEventAdviser_iface
, event
);
1421 IWineUiaEventAdviser_Release(&adv_events
->IWineUiaEventAdviser_iface
);
1427 * IWineUiaEventAdviser interface for serverside events.
1429 struct uia_serverside_event_adviser
{
1430 IWineUiaEventAdviser IWineUiaEventAdviser_iface
;
1433 IWineUiaEvent
*event_iface
;
1436 static inline struct uia_serverside_event_adviser
*impl_from_serverside_IWineUiaEventAdviser(IWineUiaEventAdviser
*iface
)
1438 return CONTAINING_RECORD(iface
, struct uia_serverside_event_adviser
, IWineUiaEventAdviser_iface
);
1441 static HRESULT WINAPI
uia_serverside_event_adviser_QueryInterface(IWineUiaEventAdviser
*iface
, REFIID riid
, void **ppv
)
1444 if (IsEqualIID(riid
, &IID_IWineUiaEventAdviser
) || IsEqualIID(riid
, &IID_IUnknown
))
1447 return E_NOINTERFACE
;
1449 IWineUiaEventAdviser_AddRef(iface
);
1453 static ULONG WINAPI
uia_serverside_event_adviser_AddRef(IWineUiaEventAdviser
*iface
)
1455 struct uia_serverside_event_adviser
*adv_events
= impl_from_serverside_IWineUiaEventAdviser(iface
);
1456 ULONG ref
= InterlockedIncrement(&adv_events
->ref
);
1458 TRACE("%p, refcount %ld\n", adv_events
, ref
);
1462 static ULONG WINAPI
uia_serverside_event_adviser_Release(IWineUiaEventAdviser
*iface
)
1464 struct uia_serverside_event_adviser
*adv_events
= impl_from_serverside_IWineUiaEventAdviser(iface
);
1465 ULONG ref
= InterlockedDecrement(&adv_events
->ref
);
1467 TRACE("%p, refcount %ld\n", adv_events
, ref
);
1470 IWineUiaEvent_Release(adv_events
->event_iface
);
1476 static HRESULT WINAPI
uia_serverside_event_adviser_advise(IWineUiaEventAdviser
*iface
, BOOL advise_added
, LONG_PTR huiaevent
)
1478 struct uia_serverside_event_adviser
*adv_events
= impl_from_serverside_IWineUiaEventAdviser(iface
);
1479 struct uia_event
*event_data
= (struct uia_event
*)huiaevent
;
1482 TRACE("%p, %d, %#Ix\n", adv_events
, advise_added
, huiaevent
);
1486 const struct uia_event_info
*event_info
= uia_event_info_from_id(event_data
->event_id
);
1490 if (event_data
->runtime_id
)
1492 V_VT(&v
) = VT_I4
| VT_ARRAY
;
1493 V_ARRAY(&v
) = event_data
->runtime_id
;
1496 hr
= IWineUiaEvent_set_event_data(adv_events
->event_iface
, event_info
->guid
, event_data
->scope
, v
,
1497 &event_data
->IWineUiaEvent_iface
);
1500 WARN("Failed to set event data on serverside event, hr %#lx\n", hr
);
1505 return IWineUiaEvent_advise_events(adv_events
->event_iface
, advise_added
, 0);
1508 static const IWineUiaEventAdviserVtbl uia_serverside_event_adviser_vtbl
= {
1509 uia_serverside_event_adviser_QueryInterface
,
1510 uia_serverside_event_adviser_AddRef
,
1511 uia_serverside_event_adviser_Release
,
1512 uia_serverside_event_adviser_advise
,
1515 HRESULT
uia_event_add_serverside_event_adviser(IWineUiaEvent
*serverside_event
, struct uia_event
*event
)
1517 struct uia_serverside_event_adviser
*adv_events
;
1521 * Need to create a proxy IWineUiaEvent for our clientside event to use
1522 * this serverside IWineUiaEvent proxy from the appropriate apartment.
1524 if (!event
->u
.clientside
.git_cookie
)
1526 if (!uia_clientside_event_start_event_thread(event
))
1529 hr
= register_interface_in_git((IUnknown
*)&event
->IWineUiaEvent_iface
, &IID_IWineUiaEvent
,
1530 &event
->u
.clientside
.git_cookie
);
1535 if (!(adv_events
= calloc(1, sizeof(*adv_events
))))
1536 return E_OUTOFMEMORY
;
1538 adv_events
->IWineUiaEventAdviser_iface
.lpVtbl
= &uia_serverside_event_adviser_vtbl
;
1539 adv_events
->ref
= 1;
1540 adv_events
->event_iface
= serverside_event
;
1541 IWineUiaEvent_AddRef(serverside_event
);
1543 hr
= uia_event_add_event_adviser(&adv_events
->IWineUiaEventAdviser_iface
, event
);
1544 IWineUiaEventAdviser_Release(&adv_events
->IWineUiaEventAdviser_iface
);
1549 static HRESULT
uia_event_advise(struct uia_event
*event
, BOOL advise_added
, LONG start_idx
)
1551 IWineUiaEvent
*event_iface
;
1554 if (event
->u
.clientside
.git_cookie
)
1556 hr
= get_interface_in_git(&IID_IWineUiaEvent
, event
->u
.clientside
.git_cookie
,
1557 (IUnknown
**)&event_iface
);
1563 event_iface
= &event
->IWineUiaEvent_iface
;
1564 IWineUiaEvent_AddRef(event_iface
);
1567 hr
= IWineUiaEvent_advise_events(event_iface
, advise_added
, start_idx
);
1568 IWineUiaEvent_Release(event_iface
);
1573 HRESULT
uia_event_advise_node(struct uia_event
*event
, HUIANODE node
)
1575 int old_event_advisers_count
= event
->event_advisers_count
;
1578 hr
= attach_event_to_uia_node(node
, event
);
1582 if (event
->event_advisers_count
!= old_event_advisers_count
)
1583 hr
= uia_event_advise(event
, TRUE
, old_event_advisers_count
);
1588 /***********************************************************************
1589 * UiaEventAddWindow (uiautomationcore.@)
1591 HRESULT WINAPI
UiaEventAddWindow(HUIAEVENT huiaevent
, HWND hwnd
)
1593 struct uia_event
*event
= unsafe_impl_from_IWineUiaEvent((IWineUiaEvent
*)huiaevent
);
1597 TRACE("(%p, %p)\n", huiaevent
, hwnd
);
1600 return E_INVALIDARG
;
1602 assert(event
->event_type
== EVENT_TYPE_CLIENTSIDE
);
1604 hr
= UiaNodeFromHandle(hwnd
, &node
);
1608 hr
= uia_event_advise_node(event
, node
);
1609 UiaNodeRelease(node
);
1614 static HRESULT
uia_clientside_event_callback(struct uia_event
*event
, struct uia_event_args
*args
,
1615 SAFEARRAY
*cache_req
, BSTR tree_struct
)
1617 UiaEventCallback
*event_callback
= (UiaEventCallback
*)event
->u
.clientside
.callback_data
;
1619 event_callback(&args
->simple_args
, cache_req
, tree_struct
);
1624 HRESULT
uia_add_clientside_event(HUIANODE huianode
, EVENTID event_id
, enum TreeScope scope
, PROPERTYID
*prop_ids
,
1625 int prop_ids_count
, struct UiaCacheRequest
*cache_req
, SAFEARRAY
*rt_id
, UiaWineEventCallback
*cback
,
1626 void *cback_data
, HUIAEVENT
*huiaevent
)
1628 struct uia_event
*event
;
1632 hr
= SafeArrayCopy(rt_id
, &sa
);
1636 hr
= create_clientside_uia_event(&event
, event_id
, scope
, cback
, cback_data
, sa
);
1639 SafeArrayDestroy(sa
);
1643 hr
= uia_cache_request_clone(&event
->u
.clientside
.cache_req
, cache_req
);
1647 hr
= attach_event_to_uia_node(huianode
, event
);
1651 hr
= uia_event_advise(event
, TRUE
, 0);
1655 hr
= uia_event_map_add_event(event
);
1659 *huiaevent
= (HUIAEVENT
)event
;
1663 IWineUiaEvent_Release(&event
->IWineUiaEvent_iface
);
1668 /***********************************************************************
1669 * UiaAddEvent (uiautomationcore.@)
1671 HRESULT WINAPI
UiaAddEvent(HUIANODE huianode
, EVENTID event_id
, UiaEventCallback
*callback
, enum TreeScope scope
,
1672 PROPERTYID
*prop_ids
, int prop_ids_count
, struct UiaCacheRequest
*cache_req
, HUIAEVENT
*huiaevent
)
1674 const struct uia_event_info
*event_info
= uia_event_info_from_id(event_id
);
1678 TRACE("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode
, event_id
, callback
, scope
, prop_ids
, prop_ids_count
,
1679 cache_req
, huiaevent
);
1681 if (!huianode
|| !callback
|| !cache_req
|| !huiaevent
)
1682 return E_INVALIDARG
;
1685 WARN("No event information for event ID %d\n", event_id
);
1688 if (event_info
&& (event_info
->event_arg_type
== EventArgsType_PropertyChanged
))
1690 FIXME("Property changed event registration currently unimplemented\n");
1694 hr
= UiaGetRuntimeId(huianode
, &sa
);
1698 hr
= uia_add_clientside_event(huianode
, event_id
, scope
, prop_ids
, prop_ids_count
, cache_req
, sa
,
1699 uia_clientside_event_callback
, (void *)callback
, huiaevent
);
1700 SafeArrayDestroy(sa
);
1705 /***********************************************************************
1706 * UiaRemoveEvent (uiautomationcore.@)
1708 HRESULT WINAPI
UiaRemoveEvent(HUIAEVENT huiaevent
)
1710 struct uia_event
*event
= unsafe_impl_from_IWineUiaEvent((IWineUiaEvent
*)huiaevent
);
1713 TRACE("(%p)\n", event
);
1716 return E_INVALIDARG
;
1718 assert(event
->event_type
== EVENT_TYPE_CLIENTSIDE
);
1719 hr
= uia_event_advise(event
, FALSE
, 0);
1723 if (event
->u
.clientside
.git_cookie
)
1725 hr
= unregister_interface_in_git(event
->u
.clientside
.git_cookie
);
1730 IWineUiaEvent_Release(&event
->IWineUiaEvent_iface
);
1734 HRESULT
uia_event_invoke(HUIANODE node
, HUIANODE nav_start_node
, struct uia_event_args
*args
, struct uia_event
*event
)
1738 if (event
->event_type
== EVENT_TYPE_CLIENTSIDE
)
1743 if (nav_start_node
&& (hr
= uia_event_check_node_within_event_scope(event
, nav_start_node
, NULL
, NULL
)) != S_OK
)
1746 hr
= UiaGetUpdatedCache(node
, &event
->u
.clientside
.cache_req
, NormalizeState_View
, NULL
, &out_req
,
1750 hr
= event
->u
.clientside
.event_callback(event
, args
, out_req
, tree_struct
);
1752 WARN("Event callback failed with hr %#lx\n", hr
);
1753 SafeArrayDestroy(out_req
);
1754 SysFreeString(tree_struct
);
1759 struct uia_queue_uia_event
*queue_event
;
1760 HUIANODE node2
, nav_start_node2
;
1762 if (!(queue_event
= calloc(1, sizeof(*queue_event
))))
1763 return E_OUTOFMEMORY
;
1765 node2
= nav_start_node2
= NULL
;
1766 hr
= clone_uia_node(node
, &node2
);
1775 hr
= clone_uia_node(nav_start_node
, &nav_start_node2
);
1779 UiaNodeRelease(node2
);
1784 queue_event
->args
= args
;
1785 queue_event
->event
= event
;
1786 queue_event
->u
.serverside
.node
= node2
;
1787 queue_event
->u
.serverside
.nav_start_node
= nav_start_node2
;
1789 InterlockedIncrement(&args
->ref
);
1790 IWineUiaEvent_AddRef(&event
->IWineUiaEvent_iface
);
1791 uia_event_queue_push(&queue_event
->queue_entry
, QUEUE_EVENT_TYPE_SERVERSIDE
);
1797 static void set_refuse_hwnd_providers(struct uia_node
*node
, BOOL refuse_hwnd_providers
)
1799 struct uia_provider
*prov_data
= impl_from_IWineUiaProvider(node
->prov
[get_node_provider_type_at_idx(node
, 0)]);
1801 prov_data
->refuse_hwnd_node_providers
= refuse_hwnd_providers
;
1805 * Check if a node is within the scope of a registered event.
1806 * If it is, return S_OK.
1807 * If it isn't, return S_FALSE.
1808 * Upon failure, return a failure HR.
1810 HRESULT
uia_event_check_node_within_event_scope(struct uia_event
*event
, HUIANODE node
, SAFEARRAY
*rt_id
,
1811 HUIANODE
*clientside_nav_node_out
)
1813 struct UiaPropertyCondition prop_cond
= { ConditionType_Property
, UIA_RuntimeIdPropertyId
};
1814 struct uia_node
*node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node
);
1815 BOOL in_scope
= FALSE
;
1816 HRESULT hr
= S_FALSE
;
1818 if (clientside_nav_node_out
)
1819 *clientside_nav_node_out
= NULL
;
1821 if (event
->event_type
== EVENT_TYPE_SERVERSIDE
)
1822 assert(clientside_nav_node_out
);
1824 /* Event is no longer valid. */
1825 if (InterlockedCompareExchange(&event
->event_defunct
, 0, 0) != 0)
1828 /* Can't match an event that doesn't have a runtime ID, early out. */
1829 if (!event
->runtime_id
)
1832 if (event
->desktop_subtree_event
)
1835 if (rt_id
&& !uia_compare_safearrays(rt_id
, event
->runtime_id
, UIAutomationType_IntArray
))
1836 return (event
->scope
& TreeScope_Element
) ? S_OK
: S_FALSE
;
1838 if (!(event
->scope
& (TreeScope_Descendants
| TreeScope_Children
)))
1841 V_VT(&prop_cond
.Value
) = VT_I4
| VT_ARRAY
;
1842 V_ARRAY(&prop_cond
.Value
) = event
->runtime_id
;
1844 IWineUiaNode_AddRef(&node_data
->IWineUiaNode_iface
);
1847 HUIANODE node2
= NULL
;
1850 * When trying to match serverside events through navigation, we
1851 * don't want any clientside providers added in the server process.
1852 * Once we encounter a provider with an HWND, we pass it off to the
1853 * client for any further navigation.
1855 if (event
->event_type
== EVENT_TYPE_SERVERSIDE
)
1857 if (node_data
->hwnd
)
1859 *clientside_nav_node_out
= (HUIANODE
)&node_data
->IWineUiaNode_iface
;
1860 IWineUiaNode_AddRef(&node_data
->IWineUiaNode_iface
);
1864 set_refuse_hwnd_providers(node_data
, TRUE
);
1867 hr
= navigate_uia_node(node_data
, NavigateDirection_Parent
, &node2
);
1868 if (FAILED(hr
) || !node2
)
1871 IWineUiaNode_Release(&node_data
->IWineUiaNode_iface
);
1873 node_data
= impl_from_IWineUiaNode((IWineUiaNode
*)node2
);
1874 hr
= uia_condition_check(node2
, (struct UiaCondition
*)&prop_cond
);
1878 if (uia_condition_matched(hr
))
1884 if (!(event
->scope
& TreeScope_Descendants
))
1887 IWineUiaNode_Release(&node_data
->IWineUiaNode_iface
);
1892 return in_scope
? S_OK
: S_FALSE
;
1895 static HRESULT
uia_raise_elprov_event_callback(struct uia_event
*event
, void *data
)
1897 struct uia_elprov_event_data
*event_data
= (struct uia_elprov_event_data
*)data
;
1898 HUIANODE nav_node
= NULL
;
1901 if (!event_data
->node
)
1904 * For events raised on server-side providers, we don't want to add any
1905 * clientside HWND providers.
1907 hr
= create_uia_node_from_elprov(event_data
->elprov
, &event_data
->node
, event_data
->clientside_only
, 0);
1911 hr
= UiaGetRuntimeId(event_data
->node
, &event_data
->rt_id
);
1916 hr
= uia_event_check_node_within_event_scope(event
, event_data
->node
, event_data
->rt_id
, &nav_node
);
1918 hr
= uia_event_invoke(event_data
->node
, nav_node
, event_data
->args
, event
);
1920 UiaNodeRelease(nav_node
);
1924 static HRESULT
uia_raise_elprov_event(IRawElementProviderSimple
*elprov
, struct uia_event_args
*args
)
1926 struct uia_elprov_event_data event_data
= { elprov
, args
};
1927 enum ProviderOptions prov_opts
= 0;
1930 hr
= IRawElementProviderSimple_get_ProviderOptions(elprov
, &prov_opts
);
1934 event_data
.clientside_only
= !(prov_opts
& ProviderOptions_ServerSideProvider
);
1935 hr
= uia_event_for_each(args
->simple_args
.EventId
, uia_raise_elprov_event_callback
, (void *)&event_data
,
1936 event_data
.clientside_only
);
1938 WARN("uia_event_for_each failed with hr %#lx\n", hr
);
1940 UiaNodeRelease(event_data
.node
);
1941 SafeArrayDestroy(event_data
.rt_id
);
1946 /***********************************************************************
1947 * UiaRaiseAutomationEvent (uiautomationcore.@)
1949 HRESULT WINAPI
UiaRaiseAutomationEvent(IRawElementProviderSimple
*elprov
, EVENTID id
)
1951 const struct uia_event_info
*event_info
= uia_event_info_from_id(id
);
1952 struct uia_event_args
*args
;
1955 TRACE("(%p, %d)\n", elprov
, id
);
1958 return E_INVALIDARG
;
1960 if (!event_info
|| event_info
->event_arg_type
!= EventArgsType_Simple
)
1963 FIXME("No event info structure for event id %d\n", id
);
1965 WARN("Wrong event raising function for event args type %d\n", event_info
->event_arg_type
);
1970 args
= create_uia_event_args(event_info
);
1972 return E_OUTOFMEMORY
;
1974 hr
= uia_raise_elprov_event(elprov
, args
);
1975 uia_event_args_release(args
);