From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 26 ++-- dlls/uiautomationcore/uia_com_client.c | 148 ++++++++++++++++++++- 2 files changed, 159 insertions(+), 15 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 0aff7a8ad24..b71d89bba45 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13089,11 +13089,11 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati */ hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, NULL, TreeScope_SubTree, NULL, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, TreeScope_SubTree, NULL, NULL); - todo_wine ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr);
/* * Passing in a NULL element to this method results in an access violation @@ -13118,21 +13118,21 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati */ hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_AutomationFocusChangedEventId, elem, TreeScope_SubTree, NULL, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr);
/* Windows 11 queries the HWND for the element when adding a new handler. */ set_uia_hwnd_expects(3, 2, 2, 3, 0); /* All other event IDs are fine, only focus events are blocked. */ hr = IUIAutomation_AddAutomationEventHandler(uia_iface, 1, elem, TreeScope_SubTree, NULL, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); check_uia_hwnd_expects_at_most(3, 2, 2, 3, 0);
hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, 1, elem, &AutomationEventHandler.IUIAutomationEventHandler_iface); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
/* * Test event raising behavior. @@ -13140,8 +13140,8 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati set_uia_hwnd_expects(3, 2, 2, 3, 0); hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, TreeScope_SubTree, NULL, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); check_uia_hwnd_expects_at_most(3, 2, 2, 3, 0);
/* Same behavior as HUIAEVENTs, events are matched by runtime ID. */ @@ -13184,7 +13184,7 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem, &AutomationEventHandler.IUIAutomationEventHandler_iface); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
VariantInit(&v); initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); @@ -13207,18 +13207,18 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati */ hr = IUIAutomation_AddAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem2, TreeScope_SubTree, NULL, &AutomationEventHandler.IUIAutomationEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
/* No removal will occur due to a lack of a runtime ID to match. */ hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_LiveRegionChangedEventId, elem2, &AutomationEventHandler.IUIAutomationEventHandler_iface); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + ok(AutomationEventHandler.ref > 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
hr = IUIAutomation_RemoveAllEventHandlers(uia_iface); todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref); + todo_wine ok(AutomationEventHandler.ref == 1, "Unexpected refcnt %ld\n", AutomationEventHandler.ref);
IUIAutomationElement_Release(elem2); } diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 919936c7acb..6616568d535 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -898,6 +898,123 @@ static HRESULT get_uia_cache_request_struct_from_iface(IUIAutomationCacheRequest return S_OK; }
+/* + * COM API UI Automation event related functions. + */ +static struct uia_com_event_handlers +{ + struct rb_tree handler_map; + + LONG handler_count; +} com_event_handlers; + +static CRITICAL_SECTION com_event_handlers_cs; +static CRITICAL_SECTION_DEBUG com_event_handlers_cs_debug = +{ + 0, 0, &com_event_handlers_cs, + { &com_event_handlers_cs_debug.ProcessLocksList, &com_event_handlers_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": com_event_handlers_cs") } +}; +static CRITICAL_SECTION com_event_handlers_cs = { &com_event_handlers_cs_debug, -1, 0, 0, 0, 0 }; + +struct uia_event_handler_identifier { + IUnknown *handler_iface; + SAFEARRAY *runtime_id; + int event_id; +}; + +struct uia_event_handler_map_entry +{ + struct rb_entry entry; + + IUnknown *handler_iface; + SAFEARRAY *runtime_id; + int event_id; + + struct list handlers_list; +}; + +static int uia_com_event_handler_id_compare(const void *key, const struct rb_entry *entry) +{ + struct uia_event_handler_map_entry *map_entry = RB_ENTRY_VALUE(entry, struct uia_event_handler_map_entry, entry); + struct uia_event_handler_identifier *event_id = (struct uia_event_handler_identifier *)key; + + if (event_id->event_id != map_entry->event_id) + return (event_id->event_id > map_entry->event_id) - (event_id->event_id < map_entry->event_id); + else if (event_id->handler_iface != map_entry->handler_iface) + return (event_id->handler_iface > map_entry->handler_iface) - (event_id->handler_iface < map_entry->handler_iface); + else if (event_id->runtime_id && map_entry->runtime_id) + return uia_compare_safearrays(event_id->runtime_id, map_entry->runtime_id, UIAutomationType_IntArray); + else + return (event_id->runtime_id > map_entry->runtime_id) - (event_id->runtime_id < map_entry->runtime_id); +} + +struct uia_com_event { + IUnknown *handler_iface; + + struct list event_handler_map_list_entry; + struct uia_event_handler_map_entry *handler_map; +}; + +static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, SAFEARRAY *runtime_id, int event_id) +{ + struct uia_event_handler_identifier event_ident = { handler_iface, runtime_id, event_id }; + struct uia_event_handler_map_entry *event_map; + struct uia_com_event *event; + struct rb_entry *rb_entry; + HRESULT hr = S_OK; + + if (!(event = heap_alloc_zero(sizeof(*event)))) + return E_OUTOFMEMORY; + + event->handler_iface = handler_iface; + IUnknown_AddRef(handler_iface); + + EnterCriticalSection(&com_event_handlers_cs); + + if (!com_event_handlers.handler_count) + rb_init(&com_event_handlers.handler_map, uia_com_event_handler_id_compare); + + if ((rb_entry = rb_get(&com_event_handlers.handler_map, &event_ident))) + event_map = RB_ENTRY_VALUE(rb_entry, struct uia_event_handler_map_entry, entry); + else + { + if (!(event_map = heap_alloc_zero(sizeof(*event_map)))) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + hr = SafeArrayCopy(runtime_id, &event_map->runtime_id); + if (FAILED(hr)) + { + heap_free(event_map); + goto exit; + } + + event_map->event_id = event_id; + event_map->handler_iface = event->handler_iface; + IUnknown_AddRef(event_map->handler_iface); + + list_init(&event_map->handlers_list); + rb_put(&com_event_handlers.handler_map, &event_ident, &event_map->entry); + } + + list_add_tail(&event_map->handlers_list, &event->event_handler_map_list_entry); + event->handler_map = event_map; + com_event_handlers.handler_count++; + +exit: + LeaveCriticalSection(&com_event_handlers_cs); + if (FAILED(hr)) + { + IUnknown_Release(event->handler_iface); + heap_free(event); + } + + return hr; +} + /* * IUIAutomationElementArray interface. */ @@ -3052,8 +3169,35 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, IUIAutomationElement *elem, enum TreeScope scope, IUIAutomationCacheRequest *cache_req, IUIAutomationEventHandler *handler) { - FIXME("%p, %d, %p, %#x, %p, %p: stub\n", iface, event_id, elem, scope, cache_req, handler); - return E_NOTIMPL; + struct uia_element *element; + IUnknown *handler_iface; + SAFEARRAY *runtime_id; + HRESULT hr; + + TRACE("%p, %d, %p, %#x, %p, %p\n", iface, event_id, elem, scope, cache_req, handler); + + if (!elem || !handler) + return E_POINTER; + + if (event_id == UIA_AutomationFocusChangedEventId) + return E_INVALIDARG; + + hr = IUIAutomationEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_iface); + if (FAILED(hr)) + return hr; + + element = impl_from_IUIAutomationElement9((IUIAutomationElement9 *)elem); + hr = UiaGetRuntimeId(element->node, &runtime_id); + if (FAILED(hr)) + goto exit; + + hr = uia_event_handlers_add_handler(handler_iface, runtime_id, event_id); + +exit: + IUnknown_Release(handler_iface); + SafeArrayDestroy(runtime_id); + + return hr; }
static HRESULT WINAPI uia_iface_RemoveAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id,