dmband: Set DMUS_PATCH_PMSG bank LSB/MSB from instrument patch.
[wine.git] / dlls / uiautomationcore / uia_event.c
blob0106be0999906c7b380cb0162e8847d6011141a2
1 /*
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)
29 SAFEARRAY *sa;
31 if ((sa = SafeArrayCreateVector(VT_I4, 0, 2)))
33 if (SUCCEEDED(write_runtime_id_base(sa, GetDesktopWindow())))
34 uia_desktop_node_rt_id = sa;
35 else
36 SafeArrayDestroy(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)
54 switch (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;
61 default:
62 break;
65 return 0;
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;
71 HRESULT hr;
73 if (!uia_hwnd_is_visible(hwnd))
74 return TRUE;
76 hr = uia_hwnd_map_add_hwnd(hwnd_map, hwnd);
77 if (FAILED(hr))
78 WARN("Failed to add hwnd to map, hr %#lx\n", hr);
80 return TRUE;
83 HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd)
85 if (!uia_clientside_event_start_event_thread(event))
86 return E_FAIL;
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;
100 LONG event_count;
102 /* rb_tree for serverside events, sorted by PID/event cookie. */
103 struct rb_tree serverside_event_map;
104 LONG serverside_event_count;
105 } uia_event_map;
107 struct uia_event_map_entry
109 struct rb_entry entry;
110 LONG refs;
112 int event_id;
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
120 * ignored.
122 struct list events_list;
123 struct list serverside_events_list;
126 struct uia_event_identifier {
127 LONG event_cookie;
128 LONG proc_id;
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);
138 else
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 =
145 0, 0, &event_map_cs,
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);
167 return map_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);
202 else
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);
209 return S_OK;
212 static void uia_event_map_entry_release(struct uia_event_map_entry *entry)
214 ULONG ref = InterlockedDecrement(&entry->refs);
216 if (!ref)
218 struct list *cursor, *cursor2;
220 EnterCriticalSection(&event_map_cs);
223 * Someone grabbed this while we were waiting to enter the CS, abort
224 * destruction.
226 if (InterlockedCompareExchange(&entry->refs, 0, 0) != 0)
228 LeaveCriticalSection(&event_map_cs);
229 return;
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);
251 free(entry);
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;
259 HRESULT hr = S_OK;
260 int i;
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);
267 if (!event_entry)
268 return S_OK;
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)
276 break;
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)
284 continue;
286 hr = callback(event, user_data);
287 if (FAILED(hr))
288 goto exit;
292 exit:
293 if (FAILED(hr))
294 WARN("Event callback failed with hr %#lx\n", hr);
295 uia_event_map_entry_release(event_entry);
296 return hr;
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));
309 if (!args)
310 return NULL;
312 args->simple_args.Type = event_info->event_arg_type;
313 args->simple_args.EventId = event_info->event_id;
314 args->ref = 1;
316 return args;
319 static void uia_event_args_release(struct uia_event_args *args)
321 if (!InterlockedDecrement(&args->ref))
322 free(args);
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));
338 if (!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);
348 return S_OK;
352 * IProxyProviderWinEventSink interface implementation.
354 struct uia_proxy_win_event_sink {
355 IProxyProviderWinEventSink IProxyProviderWinEventSink_iface;
356 LONG ref;
358 int event_id;
359 IUnknown *marshal;
360 LONG sink_defunct;
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);
373 *obj = NULL;
374 if (IsEqualIID(riid, &IID_IProxyProviderWinEventSink) || IsEqualIID(riid, &IID_IUnknown))
375 *obj = iface;
376 else if (IsEqualIID(riid, &IID_IMarshal))
377 return IUnknown_QueryInterface(sink->marshal, riid, obj);
378 else
379 return E_NOINTERFACE;
381 IProxyProviderWinEventSink_AddRef(iface);
382 return S_OK;
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);
392 return 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);
402 if (!ref)
404 assert(list_empty(&sink->sink_events));
405 IUnknown_Release(sink->marshal);
406 free(sink);
409 return ref;
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));
416 return E_NOTIMPL;
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;
424 HRESULT hr = S_OK;
426 TRACE("%p, %p, %d\n", iface, elprov, event_id);
428 if (event_id != sink->event_id)
429 return S_OK;
431 args = create_uia_event_args(uia_event_info_from_id(event_id));
432 if (!args)
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);
438 return hr;
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);
445 return E_NOTIMPL;
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));
460 HRESULT hr;
462 *out_sink = NULL;
463 if (!sink)
464 return E_OUTOFMEMORY;
466 sink->IProxyProviderWinEventSink_iface.lpVtbl = &uia_proxy_event_sink_vtbl;
467 sink->ref = 1;
468 sink->event_id = event_id;
469 list_init(&sink->sink_events);
471 hr = CoCreateFreeThreadedMarshaler((IUnknown *)&sink->IProxyProviderWinEventSink_iface, &sink->marshal);
472 if (FAILED(hr))
474 free(sink);
475 return hr;
478 *out_sink = sink;
479 return S_OK;
483 * UI Automation event thread.
485 struct uia_event_thread
487 HANDLE hthread;
488 HWND hwnd;
489 LONG ref;
491 struct list *event_queue;
492 HWINEVENTHOOK hook;
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;
525 union {
526 struct {
527 HUIANODE node;
528 HUIANODE nav_start_node;
529 } serverside;
530 struct {
531 LRESULT node;
532 LRESULT nav_start_node;
533 } clientside;
534 } u;
537 struct uia_queue_win_event
539 struct uia_queue_event queue_entry;
541 HWINEVENTHOOK hook;
542 DWORD event_id;
543 HWND hwnd;
544 LONG obj_id;
545 LONG child_id;
546 DWORD thread_id;
547 DWORD event_time;
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)
561 free(event);
562 goto exit;
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);
570 exit:
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);
587 return queue_event;
590 static HRESULT uia_raise_clientside_event(struct uia_queue_uia_event *event)
592 HUIANODE node, nav_start_node;
593 HRESULT hr;
595 node = nav_start_node = NULL;
596 hr = uia_node_from_lresult(event->u.clientside.node, &node, 0);
597 if (FAILED(hr))
599 WARN("Failed to create node from lresult, hr %#lx\n", hr);
600 uia_node_lresult_release(event->u.clientside.nav_start_node);
601 return hr;
604 if (event->u.clientside.nav_start_node)
606 hr = uia_node_from_lresult(event->u.clientside.nav_start_node, &nav_start_node, 0);
607 if (FAILED(hr))
609 WARN("Failed to create nav_start_node from lresult, hr %#lx\n", hr);
610 UiaNodeRelease(node);
611 return hr;
615 hr = uia_event_invoke(node, nav_start_node, event->args, event->event);
616 UiaNodeRelease(node);
617 UiaNodeRelease(nav_start_node);
619 return hr;
622 static HRESULT uia_raise_serverside_event(struct uia_queue_uia_event *event)
624 HRESULT hr = S_OK;
625 LRESULT lr, lr2;
626 VARIANT v, v2;
629 * uia_lresult_from_node is expected to release the node here upon
630 * failure.
632 lr = lr2 = 0;
633 if (!(lr = uia_lresult_from_node(event->u.serverside.node)))
635 UiaNodeRelease(event->u.serverside.nav_start_node);
636 return E_FAIL;
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);
642 return E_FAIL;
645 VariantInit(&v2);
646 variant_init_i4(&v, lr);
647 if (lr2)
648 variant_init_i4(&v2, lr2);
650 hr = IWineUiaEvent_raise_event(event->event->u.serverside.event_iface, v, v2);
651 if (FAILED(hr))
653 uia_node_lresult_release(lr);
654 uia_node_lresult_release(lr2);
657 return hr;
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))
669 return TRUE;
671 parent = GetAncestor(parent, GA_PARENT);
674 return FALSE;
677 HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawElementProviderSimple **ret_elprov)
679 IRawElementProviderSimple *elprov;
680 IAccessible *acc;
681 int child_id;
682 HRESULT hr;
684 *ret_elprov = NULL;
685 hr = AccessibleObjectFromWindow(hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc);
686 if (FAILED(hr))
687 return hr;
689 child_id = in_child_id;
690 if (in_child_id != CHILDID_SELF)
692 IDispatch *disp;
693 VARIANT cid;
695 disp = NULL;
696 variant_init_i4(&cid, in_child_id);
697 hr = IAccessible_get_accChild(acc, cid, &disp);
698 if (FAILED(hr))
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);
706 if (FAILED(hr))
707 return hr;
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);
715 if (FAILED(hr))
716 return hr;
718 *ret_elprov = elprov;
719 return S_OK;
722 struct uia_elprov_event_data
724 IRawElementProviderSimple *elprov;
725 struct uia_event_args *args;
726 BOOL clientside_only;
728 SAFEARRAY *rt_id;
729 HUIANODE node;
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;
740 HUIANODE node;
741 HRESULT hr;
742 int i;
745 * Check if this HWND, or any of it's ancestors (excluding the desktop)
746 * are in our scope.
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))
750 return S_OK;
752 /* Has a native serverside provider, no need to do WinEvent translation. */
753 if (UiaHasServerSideProvider(win_event->hwnd))
754 return S_OK;
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);
761 if (FAILED(hr))
762 return hr;
764 hr = create_uia_node_from_elprov(elprov, &node, TRUE, NODE_FLAG_IGNORE_COM_THREADING);
765 IRawElementProviderSimple_Release(elprov);
766 if (FAILED(hr))
767 return hr;
769 hr = create_proxy_win_event_sink(&sink, event->event_id);
770 if (SUCCEEDED(hr))
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))
778 break;
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);
788 if (FAILED(hr))
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);
796 free(sink_event);
799 IProxyProviderWinEventSink_Release(&sink->IProxyProviderWinEventSink_iface);
802 UiaNodeRelease(node);
803 return hr;
806 static void uia_event_thread_process_queue(struct list *event_queue)
808 while (1)
810 struct uia_queue_event *event;
811 HRESULT hr = S_OK;
813 if (!(event = uia_event_queue_pop(event_queue)))
814 break;
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);
825 else
826 hr = uia_raise_clientside_event(uia_event);
828 uia_event_args_release(uia_event->args);
829 IWineUiaEvent_Release(&uia_event->event->IWineUiaEvent_iface);
830 break;
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);
839 if (FAILED(hr))
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);
844 break;
847 default:
848 break;
851 if (FAILED(hr))
852 WARN("Failed to raise event type %d with hr %#lx\n", event->queue_event_type, hr);
854 free(event);
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))
866 return;
868 if (!(win_event = calloc(1, sizeof(*win_event))))
870 ERR("Failed to allocate uia_queue_win_event structure\n");
871 return;
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;
888 HWINEVENTHOOK hook;
889 HWND hwnd;
890 MSG msg;
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);
895 if (!hwnd)
897 WARN("CreateWindow failed: %ld\n", GetLastError());
898 CoUninitialize();
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)
917 break;
920 TranslateMessage(&msg);
921 DispatchMessageW(&msg);
924 TRACE("Shutting down UI Automation event thread.\n");
926 UnhookWinEvent(hook);
927 DestroyWindow(hwnd);
928 CoUninitialize();
929 FreeLibraryAndExitThread(huia_module, 0);
932 static BOOL uia_start_event_thread(void)
934 BOOL started = TRUE;
936 EnterCriticalSection(&event_thread_cs);
937 if (++event_thread.ref == 1)
939 HANDLE ready_event = NULL;
940 HANDLE events[2];
941 HMODULE hmodule;
942 DWORD wait_obj;
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);
953 started = FALSE;
954 goto exit;
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);
962 started = FALSE;
965 exit:
966 if (ready_event)
967 CloseHandle(ready_event);
968 if (!started)
969 memset(&event_thread, 0, sizeof(event_thread));
972 LeaveCriticalSection(&event_thread_cs);
973 return started;
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)
1006 *ppv = NULL;
1007 if (IsEqualIID(riid, &IID_IWineUiaEvent) || IsEqualIID(riid, &IID_IUnknown))
1008 *ppv = iface;
1009 else
1010 return E_NOINTERFACE;
1012 IWineUiaEvent_AddRef(iface);
1013 return S_OK;
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);
1022 return 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);
1031 if (!ref)
1033 int i;
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);
1049 else
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);
1063 free(event);
1066 return ref;
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);
1072 HRESULT hr;
1073 int i;
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);
1080 if (FAILED(hr))
1081 return hr;
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);
1091 if (FAILED(hr))
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.
1099 if (!advise_added)
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;
1111 return S_OK;
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))
1127 HRESULT hr;
1129 hr = SafeArrayCopy(V_ARRAY(&runtime_id), &event->runtime_id);
1130 if (FAILED(hr))
1132 WARN("Failed to copy runtime id, hr %#lx\n", hr);
1133 return hr;
1136 event->u.serverside.event_iface = event_iface;
1137 IWineUiaEvent_AddRef(event_iface);
1139 return S_OK;
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))))
1157 free(queue_event);
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);
1170 return S_OK;
1173 static const IWineUiaEventVtbl uia_event_vtbl = {
1174 uia_event_QueryInterface,
1175 uia_event_AddRef,
1176 uia_event_Release,
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))
1185 return NULL;
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));
1194 *out_event = NULL;
1195 if (!event)
1196 return E_OUTOFMEMORY;
1198 event->IWineUiaEvent_iface.lpVtbl = &uia_event_vtbl;
1199 event->ref = 1;
1200 event->event_cookie = event_cookie;
1201 event->event_type = event_type;
1202 *out_event = event;
1204 return S_OK;
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;
1212 HRESULT hr;
1214 *out_event = NULL;
1215 hr = create_uia_event(&event, InterlockedIncrement(&next_event_cookie), EVENT_TYPE_CLIENTSIDE);
1216 if (FAILED(hr))
1217 return hr;
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);
1226 *out_event = event;
1227 return S_OK;
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;
1235 HRESULT hr = S_OK;
1238 * Attempt to lookup an existing event for this PID/event_cookie. If there
1239 * is one, return S_FALSE.
1241 *out_event = NULL;
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);
1246 hr = S_FALSE;
1247 goto exit;
1250 hr = create_uia_event(&event, event_cookie, EVENT_TYPE_SERVERSIDE);
1251 if (FAILED(hr))
1252 goto exit;
1254 if (!uia_start_event_thread())
1256 free(event);
1257 hr = E_FAIL;
1258 goto exit;
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);
1266 *out_event = event;
1268 exit:
1269 LeaveCriticalSection(&event_map_cs);
1270 return hr;
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++;
1283 return S_OK;
1287 * IWineUiaEventAdviser interface.
1289 struct uia_event_adviser {
1290 IWineUiaEventAdviser IWineUiaEventAdviser_iface;
1291 LONG ref;
1293 IRawElementProviderAdviseEvents *advise_events;
1294 DWORD git_cookie;
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)
1304 *ppv = NULL;
1305 if (IsEqualIID(riid, &IID_IWineUiaEventAdviser) || IsEqualIID(riid, &IID_IUnknown))
1306 *ppv = iface;
1307 else
1308 return E_NOINTERFACE;
1310 IWineUiaEventAdviser_AddRef(iface);
1311 return S_OK;
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);
1320 return 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);
1329 if (!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);
1337 free(adv_events);
1340 return ref;
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;
1348 HRESULT hr;
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);
1356 if (FAILED(hr))
1357 return hr;
1359 else
1361 advise_events = adv_events->advise_events;
1362 IRawElementProviderAdviseEvents_AddRef(advise_events);
1365 if (advise_added)
1366 hr = IRawElementProviderAdviseEvents_AdviseEventAdded(advise_events, event_data->event_id, NULL);
1367 else
1368 hr = IRawElementProviderAdviseEvents_AdviseEventRemoved(advise_events, event_data->event_id, NULL);
1370 IRawElementProviderAdviseEvents_Release(advise_events);
1371 return hr;
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;
1386 HRESULT hr;
1388 hr = IRawElementProviderAdviseEvents_QueryInterface(advise_events, &IID_IRawElementProviderSimple,
1389 (void **)&elprov);
1390 if (FAILED(hr))
1392 ERR("Failed to get IRawElementProviderSimple from advise events\n");
1393 return E_FAIL;
1396 hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
1397 IRawElementProviderSimple_Release(elprov);
1398 if (FAILED(hr))
1399 return hr;
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);
1408 if (FAILED(hr))
1410 free(adv_events);
1411 return hr;
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);
1423 return hr;
1427 * IWineUiaEventAdviser interface for serverside events.
1429 struct uia_serverside_event_adviser {
1430 IWineUiaEventAdviser IWineUiaEventAdviser_iface;
1431 LONG ref;
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)
1443 *ppv = NULL;
1444 if (IsEqualIID(riid, &IID_IWineUiaEventAdviser) || IsEqualIID(riid, &IID_IUnknown))
1445 *ppv = iface;
1446 else
1447 return E_NOINTERFACE;
1449 IWineUiaEventAdviser_AddRef(iface);
1450 return S_OK;
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);
1459 return 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);
1468 if (!ref)
1470 IWineUiaEvent_Release(adv_events->event_iface);
1471 free(adv_events);
1473 return ref;
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;
1480 HRESULT hr;
1482 TRACE("%p, %d, %#Ix\n", adv_events, advise_added, huiaevent);
1484 if (advise_added)
1486 const struct uia_event_info *event_info = uia_event_info_from_id(event_data->event_id);
1487 VARIANT v;
1489 VariantInit(&v);
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);
1498 if (FAILED(hr))
1500 WARN("Failed to set event data on serverside event, hr %#lx\n", hr);
1501 return 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;
1518 HRESULT hr;
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))
1527 return E_FAIL;
1529 hr = register_interface_in_git((IUnknown *)&event->IWineUiaEvent_iface, &IID_IWineUiaEvent,
1530 &event->u.clientside.git_cookie);
1531 if (FAILED(hr))
1532 return hr;
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);
1546 return hr;
1549 static HRESULT uia_event_advise(struct uia_event *event, BOOL advise_added, LONG start_idx)
1551 IWineUiaEvent *event_iface;
1552 HRESULT hr;
1554 if (event->u.clientside.git_cookie)
1556 hr = get_interface_in_git(&IID_IWineUiaEvent, event->u.clientside.git_cookie,
1557 (IUnknown **)&event_iface);
1558 if (FAILED(hr))
1559 return hr;
1561 else
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);
1570 return hr;
1573 HRESULT uia_event_advise_node(struct uia_event *event, HUIANODE node)
1575 int old_event_advisers_count = event->event_advisers_count;
1576 HRESULT hr;
1578 hr = attach_event_to_uia_node(node, event);
1579 if (FAILED(hr))
1580 return hr;
1582 if (event->event_advisers_count != old_event_advisers_count)
1583 hr = uia_event_advise(event, TRUE, old_event_advisers_count);
1585 return hr;
1588 /***********************************************************************
1589 * UiaEventAddWindow (uiautomationcore.@)
1591 HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd)
1593 struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent);
1594 HUIANODE node;
1595 HRESULT hr;
1597 TRACE("(%p, %p)\n", huiaevent, hwnd);
1599 if (!event)
1600 return E_INVALIDARG;
1602 assert(event->event_type == EVENT_TYPE_CLIENTSIDE);
1604 hr = UiaNodeFromHandle(hwnd, &node);
1605 if (FAILED(hr))
1606 return hr;
1608 hr = uia_event_advise_node(event, node);
1609 UiaNodeRelease(node);
1611 return hr;
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);
1621 return S_OK;
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;
1629 SAFEARRAY *sa;
1630 HRESULT hr;
1632 hr = SafeArrayCopy(rt_id, &sa);
1633 if (FAILED(hr))
1634 return hr;
1636 hr = create_clientside_uia_event(&event, event_id, scope, cback, cback_data, sa);
1637 if (FAILED(hr))
1639 SafeArrayDestroy(sa);
1640 return hr;
1643 hr = uia_cache_request_clone(&event->u.clientside.cache_req, cache_req);
1644 if (FAILED(hr))
1645 goto exit;
1647 hr = attach_event_to_uia_node(huianode, event);
1648 if (FAILED(hr))
1649 goto exit;
1651 hr = uia_event_advise(event, TRUE, 0);
1652 if (FAILED(hr))
1653 goto exit;
1655 hr = uia_event_map_add_event(event);
1656 if (FAILED(hr))
1657 goto exit;
1659 *huiaevent = (HUIAEVENT)event;
1661 exit:
1662 if (FAILED(hr))
1663 IWineUiaEvent_Release(&event->IWineUiaEvent_iface);
1665 return hr;
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);
1675 SAFEARRAY *sa;
1676 HRESULT hr;
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;
1684 if (!event_info)
1685 WARN("No event information for event ID %d\n", event_id);
1687 *huiaevent = NULL;
1688 if (event_info && (event_info->event_arg_type == EventArgsType_PropertyChanged))
1690 FIXME("Property changed event registration currently unimplemented\n");
1691 return E_NOTIMPL;
1694 hr = UiaGetRuntimeId(huianode, &sa);
1695 if (FAILED(hr))
1696 return hr;
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);
1702 return hr;
1705 /***********************************************************************
1706 * UiaRemoveEvent (uiautomationcore.@)
1708 HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
1710 struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent);
1711 HRESULT hr;
1713 TRACE("(%p)\n", event);
1715 if (!event)
1716 return E_INVALIDARG;
1718 assert(event->event_type == EVENT_TYPE_CLIENTSIDE);
1719 hr = uia_event_advise(event, FALSE, 0);
1720 if (FAILED(hr))
1721 return hr;
1723 if (event->u.clientside.git_cookie)
1725 hr = unregister_interface_in_git(event->u.clientside.git_cookie);
1726 if (FAILED(hr))
1727 return hr;
1730 IWineUiaEvent_Release(&event->IWineUiaEvent_iface);
1731 return S_OK;
1734 HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args, struct uia_event *event)
1736 HRESULT hr = S_OK;
1738 if (event->event_type == EVENT_TYPE_CLIENTSIDE)
1740 SAFEARRAY *out_req;
1741 BSTR tree_struct;
1743 if (nav_start_node && (hr = uia_event_check_node_within_event_scope(event, nav_start_node, NULL, NULL)) != S_OK)
1744 return hr;
1746 hr = UiaGetUpdatedCache(node, &event->u.clientside.cache_req, NormalizeState_View, NULL, &out_req,
1747 &tree_struct);
1748 if (SUCCEEDED(hr))
1750 hr = event->u.clientside.event_callback(event, args, out_req, tree_struct);
1751 if (FAILED(hr))
1752 WARN("Event callback failed with hr %#lx\n", hr);
1753 SafeArrayDestroy(out_req);
1754 SysFreeString(tree_struct);
1757 else
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);
1767 if (FAILED(hr))
1769 free(queue_event);
1770 return hr;
1773 if (nav_start_node)
1775 hr = clone_uia_node(nav_start_node, &nav_start_node2);
1776 if (FAILED(hr))
1778 free(queue_event);
1779 UiaNodeRelease(node2);
1780 return hr;
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);
1794 return hr;
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)
1826 return S_FALSE;
1828 /* Can't match an event that doesn't have a runtime ID, early out. */
1829 if (!event->runtime_id)
1830 return S_FALSE;
1832 if (event->desktop_subtree_event)
1833 return S_OK;
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)))
1839 return S_FALSE;
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);
1845 while (1)
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);
1861 in_scope = TRUE;
1862 break;
1864 set_refuse_hwnd_providers(node_data, TRUE);
1867 hr = navigate_uia_node(node_data, NavigateDirection_Parent, &node2);
1868 if (FAILED(hr) || !node2)
1869 break;
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);
1875 if (FAILED(hr))
1876 break;
1878 if (uia_condition_matched(hr))
1880 in_scope = TRUE;
1881 break;
1884 if (!(event->scope & TreeScope_Descendants))
1885 break;
1887 IWineUiaNode_Release(&node_data->IWineUiaNode_iface);
1889 if (FAILED(hr))
1890 return hr;
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;
1899 HRESULT hr = S_OK;
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);
1908 if (FAILED(hr))
1909 return hr;
1911 hr = UiaGetRuntimeId(event_data->node, &event_data->rt_id);
1912 if (FAILED(hr))
1913 return hr;
1916 hr = uia_event_check_node_within_event_scope(event, event_data->node, event_data->rt_id, &nav_node);
1917 if (hr == S_OK)
1918 hr = uia_event_invoke(event_data->node, nav_node, event_data->args, event);
1920 UiaNodeRelease(nav_node);
1921 return hr;
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;
1928 HRESULT hr;
1930 hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts);
1931 if (FAILED(hr))
1932 return hr;
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);
1937 if (FAILED(hr))
1938 WARN("uia_event_for_each failed with hr %#lx\n", hr);
1940 UiaNodeRelease(event_data.node);
1941 SafeArrayDestroy(event_data.rt_id);
1943 return hr;
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;
1953 HRESULT hr;
1955 TRACE("(%p, %d)\n", elprov, id);
1957 if (!elprov)
1958 return E_INVALIDARG;
1960 if (!event_info || event_info->event_arg_type != EventArgsType_Simple)
1962 if (!event_info)
1963 FIXME("No event info structure for event id %d\n", id);
1964 else
1965 WARN("Wrong event raising function for event args type %d\n", event_info->event_arg_type);
1967 return S_OK;
1970 args = create_uia_event_args(event_info);
1971 if (!args)
1972 return E_OUTOFMEMORY;
1974 hr = uia_raise_elprov_event(elprov, args);
1975 uia_event_args_release(args);
1976 if (FAILED(hr))
1977 return hr;
1979 return S_OK;