From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 79 ++++++++++++++++++++++ dlls/uiautomationcore/uia_com_client.c | 13 ++++ 2 files changed, 92 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 0dd75ce862f..5bd8e52fddb 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -10620,6 +10620,79 @@ static void test_UiaFind(void) CoUninitialize(); }
+struct marshal_thread_data { + IUnknown *iface; + const GUID *iface_iid; + BOOL expect_proxy; + const char *file; + int line; + + IStream *marshal_stream; +}; + +static DWORD WINAPI interface_marshal_proxy_thread(LPVOID param) +{ + struct marshal_thread_data *data = (struct marshal_thread_data *)param; + IUnknown *proxy_iface, *unk, *unk2; + HRESULT hr; + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + proxy_iface = unk = unk2 = NULL; + hr = CoGetInterfaceAndReleaseStream(data->marshal_stream, data->iface_iid, (void **)&proxy_iface); + ok_(data->file, data->line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IUnknown_QueryInterface(data->iface, &IID_IUnknown, (void **)&unk); + ok_(data->file, data->line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(data->file, data->line)(!!unk, "unk == NULL\n"); + + hr = IUnknown_QueryInterface(proxy_iface, &IID_IUnknown, (void **)&unk2); + ok_(data->file, data->line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok_(data->file, data->line)(!!unk2, "unk2 == NULL\n"); + + if (data->expect_proxy) + ok_(data->file, data->line)(unk != unk2, "unk == unk2\n"); + else + ok_(data->file, data->line)(unk == unk2, "unk != unk2\n"); + + IUnknown_Release(proxy_iface); + IUnknown_Release(unk); + IUnknown_Release(unk2); + + CoUninitialize(); + return 0; +} + +#define check_interface_marshal_proxy_creation( iface, iid, expect_proxy ) \ + check_interface_marshal_proxy_creation_( (iface), (iid), (expect_proxy), __FILE__, __LINE__) +static void check_interface_marshal_proxy_creation_(IUnknown *iface, REFIID iid, BOOL expect_proxy, const char *file, + int line) +{ + struct marshal_thread_data data = { NULL, iid, expect_proxy, file, line }; + HANDLE thread; + HRESULT hr; + + hr = IUnknown_QueryInterface(iface, data.iface_iid, (void **)&data.iface); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CoMarshalInterThreadInterfaceInStream(data.iface_iid, data.iface, &data.marshal_stream); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + thread = CreateThread(NULL, 0, interface_marshal_proxy_thread, (void *)&data, 0, NULL); + while (MsgWaitForMultipleObjects(1, &thread, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + { + MSG msg; + while(PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + CloseHandle(thread); + + IUnknown_Release(data.iface); +} + static HWND create_test_hwnd(const char *class_name) { WNDCLASSA cls = { 0 }; @@ -10717,6 +10790,12 @@ static void test_ElementFromHandle(IUIAutomation *uia_iface, BOOL is_cui8) else ok(hr == E_NOINTERFACE, "Unexpected hr %#lx.\n", hr);
+ /* + * The IUIAutomationElement interface uses the free threaded marshaler, so + * no actual proxy interface will be created. + */ + check_interface_marshal_proxy_creation((IUnknown *)element, &IID_IUIAutomationElement, FALSE); + IUIAutomationElement_Release(element); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 75c722771c3..66326ad9574 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1186,6 +1186,8 @@ struct uia_element {
struct uia_cache_property *cached_props; int cached_props_count; + + IUnknown *marshal; };
static inline struct uia_element *impl_from_IUIAutomationElement9(IUIAutomationElement9 *iface) @@ -1203,6 +1205,8 @@ static HRESULT WINAPI uia_element_QueryInterface(IUIAutomationElement9 *iface, R IsEqualIID(riid, &IID_IUIAutomationElement6) || IsEqualIID(riid, &IID_IUIAutomationElement7) || IsEqualIID(riid, &IID_IUIAutomationElement8) || IsEqualIID(riid, &IID_IUIAutomationElement9)))) *ppv = iface; + else if (IsEqualIID(riid, &IID_IMarshal)) + return IUnknown_QueryInterface(element->marshal, riid, ppv); else return E_NOINTERFACE;
@@ -1235,6 +1239,7 @@ static ULONG WINAPI uia_element_Release(IUIAutomationElement9 *iface) VariantClear(&element->cached_props[i].prop_val); }
+ IUnknown_Release(element->marshal); heap_free(element->cached_props); UiaNodeRelease(element->node); heap_free(element); @@ -2484,6 +2489,7 @@ static const IUIAutomationElement9Vtbl uia_element_vtbl = { static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8, HUIANODE node) { struct uia_element *element = heap_alloc_zero(sizeof(*element)); + HRESULT hr;
*iface = NULL; if (!element) @@ -2494,6 +2500,13 @@ static HRESULT create_uia_element(IUIAutomationElement **iface, BOOL from_cui8, element->from_cui8 = from_cui8; element->node = node;
+ hr = CoCreateFreeThreadedMarshaler((IUnknown *)&element->IUIAutomationElement9_iface, &element->marshal); + if (FAILED(hr)) + { + heap_free(element); + return hr; + } + *iface = (IUIAutomationElement *)&element->IUIAutomationElement9_iface; return S_OK; }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_event.c | 93 ++++++++++++++++++----------- dlls/uiautomationcore/uia_private.h | 11 +++- 2 files changed, 69 insertions(+), 35 deletions(-)
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 801ccf4a705..84120ae90fc 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -217,12 +217,6 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) * as they're raised on a background thread after the event raising * function has returned. */ -struct uia_event_args -{ - struct UiaEventArgs simple_args; - LONG ref; -}; - static struct uia_event_args *create_uia_event_args(const struct uia_event_info *event_info) { struct uia_event_args *args = heap_alloc_zero(sizeof(*args)); @@ -733,8 +727,8 @@ static HRESULT create_uia_event(struct uia_event **out_event, LONG event_cookie, return S_OK; }
-static HRESULT create_clientside_uia_event(struct uia_event **out_event, int event_id, int scope, UiaEventCallback *cback, - SAFEARRAY *runtime_id) +static HRESULT create_clientside_uia_event(struct uia_event **out_event, int event_id, int scope, + UiaWineEventCallback *cback, void *cback_data, SAFEARRAY *runtime_id) { struct uia_event *event = NULL; static LONG next_event_cookie; @@ -748,7 +742,8 @@ static HRESULT create_clientside_uia_event(struct uia_event **out_event, int eve event->runtime_id = runtime_id; event->event_id = event_id; event->scope = scope; - event->u.clientside.cback = cback; + event->u.clientside.event_callback = cback; + event->u.clientside.callback_data = cback_data;
*out_event = event; return S_OK; @@ -1135,38 +1130,29 @@ exit: return hr; }
-/*********************************************************************** - * UiaAddEvent (uiautomationcore.@) - */ -HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope, - PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent) +static HRESULT uia_clientside_event_callback(struct uia_event *event, struct uia_event_args *args, + SAFEARRAY *cache_req, BSTR tree_struct) { - const struct uia_event_info *event_info = uia_event_info_from_id(event_id); - struct uia_event *event; - SAFEARRAY *sa; - HRESULT hr; + UiaEventCallback *event_callback = (UiaEventCallback *)event->u.clientside.callback_data;
- TRACE("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count, - cache_req, huiaevent); - - if (!huianode || !callback || !cache_req || !huiaevent) - return E_INVALIDARG; + event_callback(&args->simple_args, cache_req, tree_struct);
- if (!event_info) - WARN("No event information for event ID %d\n", event_id); + return S_OK; +}
- *huiaevent = NULL; - if (event_info && (event_info->event_arg_type == EventArgsType_PropertyChanged)) - { - FIXME("Property changed event registration currently unimplemented\n"); - return E_NOTIMPL; - } +static HRESULT uia_add_clientside_event(HUIANODE huianode, EVENTID event_id, enum TreeScope scope, PROPERTYID *prop_ids, + int prop_ids_count, struct UiaCacheRequest *cache_req, SAFEARRAY *rt_id, UiaWineEventCallback *cback, + void *cback_data, HUIAEVENT *huiaevent) +{ + struct uia_event *event; + SAFEARRAY *sa; + HRESULT hr;
- hr = UiaGetRuntimeId(huianode, &sa); + hr = SafeArrayCopy(rt_id, &sa); if (FAILED(hr)) return hr;
- hr = create_clientside_uia_event(&event, event_id, scope, callback, sa); + hr = create_clientside_uia_event(&event, event_id, scope, cback, cback_data, sa); if (FAILED(hr)) { SafeArrayDestroy(sa); @@ -1198,6 +1184,43 @@ exit: return hr; }
+/*********************************************************************** + * UiaAddEvent (uiautomationcore.@) + */ +HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope, + PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent) +{ + const struct uia_event_info *event_info = uia_event_info_from_id(event_id); + SAFEARRAY *sa; + HRESULT hr; + + TRACE("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count, + cache_req, huiaevent); + + if (!huianode || !callback || !cache_req || !huiaevent) + return E_INVALIDARG; + + if (!event_info) + WARN("No event information for event ID %d\n", event_id); + + *huiaevent = NULL; + if (event_info && (event_info->event_arg_type == EventArgsType_PropertyChanged)) + { + FIXME("Property changed event registration currently unimplemented\n"); + return E_NOTIMPL; + } + + hr = UiaGetRuntimeId(huianode, &sa); + if (FAILED(hr)) + return hr; + + hr = uia_add_clientside_event(huianode, event_id, scope, prop_ids, prop_ids_count, cache_req, sa, + uia_clientside_event_callback, (void *)callback, huiaevent); + SafeArrayDestroy(sa); + + return hr; +} + /*********************************************************************** * UiaRemoveEvent (uiautomationcore.@) */ @@ -1245,7 +1268,9 @@ static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct u &tree_struct); if (SUCCEEDED(hr)) { - event->u.clientside.cback(&args->simple_args, out_req, tree_struct); + hr = event->u.clientside.event_callback(event, args, out_req, tree_struct); + if (FAILED(hr)) + WARN("Event callback failed with hr %#lx\n", hr); SafeArrayDestroy(out_req); SysFreeString(tree_struct); } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 11cab86651c..928b36fbae6 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -95,6 +95,12 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider * return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); }
+struct uia_event_args +{ + struct UiaEventArgs simple_args; + LONG ref; +}; + enum uia_event_type { EVENT_TYPE_CLIENTSIDE, EVENT_TYPE_SERVERSIDE, @@ -124,7 +130,8 @@ struct uia_event { struct { struct UiaCacheRequest cache_req; - UiaEventCallback *cback; + HRESULT (*event_callback)(struct uia_event *, struct uia_event_args *, SAFEARRAY *, BSTR); + void *callback_data;
DWORD git_cookie; } clientside; @@ -137,6 +144,8 @@ struct uia_event } u; };
+typedef HRESULT UiaWineEventCallback(struct uia_event *, struct uia_event_args *, SAFEARRAY *, BSTR); + static inline void variant_init_bool(VARIANT *v, BOOL val) { V_VT(v) = VT_BOOL;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 8 +- dlls/uiautomationcore/uia_com_client.c | 113 +++++++++++++++++---- dlls/uiautomationcore/uia_event.c | 2 +- dlls/uiautomationcore/uia_private.h | 3 + 4 files changed, 99 insertions(+), 27 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 5bd8e52fddb..74bcd5c0e16 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13132,8 +13132,8 @@ static DWORD WINAPI uia_com_event_handler_test_thread(LPVOID param) set_com_event_data(&exp_node_desc); hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, UIA_LiveRegionChangedEventId); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine CHECK_CALLED(uia_com_event_callback); - todo_wine ok(ComEventData.last_call_tid == GetCurrentThreadId(), "Event handler called on unexpected thread %ld\n", + CHECK_CALLED(uia_com_event_callback); + ok(ComEventData.last_call_tid == GetCurrentThreadId(), "Event handler called on unexpected thread %ld\n", ComEventData.last_call_tid); CoUninitialize();
@@ -13146,7 +13146,7 @@ static DWORD WINAPI uia_com_event_handler_test_thread(LPVOID param) set_com_event_data(&exp_node_desc); hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, UIA_LiveRegionChangedEventId); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_com_event_callback); ok(ComEventData.last_call_tid != GetCurrentThreadId(), "Event handler called on unexpected thread %ld\n", ComEventData.last_call_tid); CoUninitialize(); @@ -13233,7 +13233,7 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati set_com_event_data(&exp_node_desc); hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, UIA_LiveRegionChangedEventId); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_com_event_callback);
/* * If no cache request is provided by the user in diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index 66326ad9574..b4545706238 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -950,26 +950,22 @@ static int uia_com_event_handler_id_compare(const void *key, const struct rb_ent }
struct uia_com_event { - IUnknown *handler_iface; + DWORD git_cookie; + HUIAEVENT event; + BOOL from_cui8;
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) +static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, SAFEARRAY *runtime_id, int event_id, + struct uia_com_event *event) { 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) @@ -993,7 +989,7 @@ static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, SAFEARRAY }
event_map->event_id = event_id; - event_map->handler_iface = event->handler_iface; + event_map->handler_iface = handler_iface; IUnknown_AddRef(event_map->handler_iface);
list_init(&event_map->handlers_list); @@ -1006,25 +1002,28 @@ static HRESULT uia_event_handlers_add_handler(IUnknown *handler_iface, SAFEARRAY
exit: LeaveCriticalSection(&com_event_handlers_cs); - if (FAILED(hr)) - { - IUnknown_Release(event->handler_iface); - heap_free(event); - }
return hr; }
+static void uia_event_handler_destroy(struct uia_com_event *event) +{ + list_remove(&event->event_handler_map_list_entry); + if (event->event) + UiaRemoveEvent(event->event); + if (event->git_cookie) + unregister_interface_in_git(event->git_cookie); + heap_free(event); +} + static void uia_event_handler_map_entry_destroy(struct uia_event_handler_map_entry *entry) { struct uia_com_event *event, *event2;
LIST_FOR_EACH_ENTRY_SAFE(event, event2, &entry->handlers_list, struct uia_com_event, event_handler_map_list_entry) { - list_remove(&event->event_handler_map_list_entry); - IUnknown_Release(event->handler_iface); + uia_event_handler_destroy(event); com_event_handlers.handler_count--; - heap_free(event); }
rb_remove(&com_event_handlers.handler_map, &entry->entry); @@ -1046,6 +1045,39 @@ static void uia_event_handlers_remove_handlers(IUnknown *handler_iface, SAFEARRA LeaveCriticalSection(&com_event_handlers_cs); }
+static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, + struct UiaCacheRequest *cache_req, LONG start_idx, SAFEARRAY *req_data, BSTR tree_struct); +static HRESULT uia_com_event_callback(struct uia_event *event, struct uia_event_args *args, + SAFEARRAY *cache_req, BSTR tree_struct) +{ + struct uia_com_event *com_event = (struct uia_com_event *)event->u.clientside.callback_data; + IUIAutomationEventHandler *handler; + IUIAutomationElement *elem; + BSTR tree_struct2; + HRESULT hr; + + /* Nothing matches the cache request view condition, do nothing. */ + if (!cache_req) + return S_OK; + + /* create_uia_element_from_cache_req frees the passed in BSTR. */ + tree_struct2 = SysAllocString(tree_struct); + hr = create_uia_element_from_cache_req(&elem, com_event->from_cui8, &event->u.clientside.cache_req, 0, cache_req, + tree_struct2); + if (FAILED(hr)) + return hr; + + hr = get_interface_in_git(&IID_IUIAutomationEventHandler, com_event->git_cookie, (IUnknown **)&handler); + if (SUCCEEDED(hr)) + { + hr = IUIAutomationEventHandler_HandleAutomationEvent(handler, elem, event->event_id); + IUIAutomationEventHandler_Release(handler); + } + IUIAutomationElement_Release(elem); + + return hr; +} + /* * IUIAutomationElementArray interface. */ @@ -1332,8 +1364,6 @@ static HRESULT set_find_params_struct(struct UiaFindParams *params, IUIAutomatio return S_OK; }
-static HRESULT create_uia_element_from_cache_req(IUIAutomationElement **iface, BOOL from_cui8, - struct UiaCacheRequest *cache_req, LONG start_idx, SAFEARRAY *req_data, BSTR tree_struct); static HRESULT WINAPI uia_element_FindFirstBuildCache(IUIAutomationElement9 *iface, enum TreeScope scope, IUIAutomationCondition *condition, IUIAutomationCacheRequest *cache_req, IUIAutomationElement **found) { @@ -3213,9 +3243,11 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, IUIAutomationElement *elem, enum TreeScope scope, IUIAutomationCacheRequest *cache_req, IUIAutomationEventHandler *handler) { + struct UiaCacheRequest *cache_req_struct; + struct uia_com_event *com_event = NULL; + SAFEARRAY *runtime_id = NULL; 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); @@ -3232,12 +3264,49 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface,
element = impl_from_IUIAutomationElement9((IUIAutomationElement9 *)elem); hr = UiaGetRuntimeId(element->node, &runtime_id); + if (FAILED(hr)) + { + IUnknown_Release(handler_iface); + return hr; + } + + if (!cache_req) + { + hr = create_uia_cache_request_iface(&cache_req); + if (FAILED(hr)) + goto exit; + } + else + IUIAutomationCacheRequest_AddRef(cache_req); + + hr = get_uia_cache_request_struct_from_iface(cache_req, &cache_req_struct); + if (FAILED(hr)) + goto exit; + + if (!(com_event = heap_alloc_zero(sizeof(*com_event)))) + { + hr = E_OUTOFMEMORY; + goto exit; + } + + com_event->from_cui8 = element->from_cui8; + list_init(&com_event->event_handler_map_list_entry); + hr = register_interface_in_git((IUnknown *)handler, &IID_IUIAutomationEventHandler, &com_event->git_cookie); if (FAILED(hr)) goto exit;
- hr = uia_event_handlers_add_handler(handler_iface, runtime_id, event_id); + hr = uia_add_clientside_event(element->node, event_id, scope, NULL, 0, cache_req_struct, runtime_id, + uia_com_event_callback, (void *)com_event, &com_event->event); + if (FAILED(hr)) + goto exit; + + hr = uia_event_handlers_add_handler(handler_iface, runtime_id, event_id, com_event);
exit: + if (FAILED(hr) && com_event) + uia_event_handler_destroy(com_event); + if (cache_req) + IUIAutomationCacheRequest_Release(cache_req); IUnknown_Release(handler_iface); SafeArrayDestroy(runtime_id);
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 84120ae90fc..693b0b7509b 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -1140,7 +1140,7 @@ static HRESULT uia_clientside_event_callback(struct uia_event *event, struct uia return S_OK; }
-static HRESULT uia_add_clientside_event(HUIANODE huianode, EVENTID event_id, enum TreeScope scope, PROPERTYID *prop_ids, +HRESULT uia_add_clientside_event(HUIANODE huianode, EVENTID event_id, enum TreeScope scope, PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, SAFEARRAY *rt_id, UiaWineEventCallback *cback, void *cback_data, HUIAEVENT *huiaevent) { diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 928b36fbae6..bbd2074a7f3 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -218,6 +218,9 @@ HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_i HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) DECLSPEC_HIDDEN; HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, struct uia_event *event) DECLSPEC_HIDDEN; +HRESULT uia_add_clientside_event(HUIANODE huianode, EVENTID event_id, enum TreeScope scope, PROPERTYID *prop_ids, + int prop_ids_count, struct UiaCacheRequest *cache_req, SAFEARRAY *rt_id, UiaWineEventCallback *cback, + void *cback_data, HUIAEVENT *huiaevent) DECLSPEC_HIDDEN;
/* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 145 +++++++++++++++++++++ 1 file changed, 145 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 74bcd5c0e16..59ae9535892 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13116,6 +13116,71 @@ static struct AutomationEventHandler AutomationEventHandler = 1, };
+/* + * IUIAutomationFocusChangedEventHandler. + */ +static struct FocusChangedHandler +{ + IUIAutomationFocusChangedEventHandler IUIAutomationFocusChangedEventHandler_iface; + LONG ref; + + BOOL event_handler_added; +} FocusChangedHandler; + +static inline struct FocusChangedHandler *impl_from_FocusChangedHandler(IUIAutomationFocusChangedEventHandler *iface) +{ + return CONTAINING_RECORD(iface, struct FocusChangedHandler, IUIAutomationFocusChangedEventHandler_iface); +} + +static HRESULT WINAPI FocusChangedHandler_QueryInterface(IUIAutomationFocusChangedEventHandler *iface, + REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IUIAutomationFocusChangedEventHandler) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IUIAutomationFocusChangedEventHandler_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI FocusChangedHandler_AddRef(IUIAutomationFocusChangedEventHandler* iface) +{ + struct FocusChangedHandler *handler = impl_from_FocusChangedHandler(iface); + return InterlockedIncrement(&handler->ref); +} + +static ULONG WINAPI FocusChangedHandler_Release(IUIAutomationFocusChangedEventHandler* iface) +{ + struct FocusChangedHandler *handler = impl_from_FocusChangedHandler(iface); + return InterlockedDecrement(&handler->ref); +} + +static HRESULT WINAPI FocusChangedHandler_HandleFocusChangedEvent(IUIAutomationFocusChangedEventHandler *iface, + IUIAutomationElement *sender) +{ + struct FocusChangedHandler *handler = impl_from_FocusChangedHandler(iface); + + if (handler->event_handler_added) + test_com_event_data(sender); + + return S_OK; +} + +static const IUIAutomationFocusChangedEventHandlerVtbl FocusChangedHandlerVtbl = { + FocusChangedHandler_QueryInterface, + FocusChangedHandler_AddRef, + FocusChangedHandler_Release, + FocusChangedHandler_HandleFocusChangedEvent, +}; + +static struct FocusChangedHandler FocusChangedHandler = +{ + { &FocusChangedHandlerVtbl }, + 1, +}; + static DWORD WINAPI uia_com_event_handler_test_thread(LPVOID param) { struct node_provider_desc exp_node_desc; @@ -13302,6 +13367,85 @@ static void test_IUIAutomationEventHandler(IUIAutomation *uia_iface, IUIAutomati IUIAutomationElement_Release(elem2); }
+static void test_IUIAutomationFocusChangedEventHandler(IUIAutomation *uia_iface) +{ + struct node_provider_desc exp_node_desc; + IUIAutomationElement *elem; + HRESULT hr; + + SetFocus(ComEventData.event_hwnd); + + /* + * FocusChangedEventHandlers are always registered on the desktop node + * with a scope of the entire desktop. + * + * All versions of Windows query the currently focused HWND when adding a + * new focus changed event handler, but behavior differs between versions: + * + * Win7-Win10v1507 queries for the focused provider and raises an event + * while also advising of events. + * + * Win10v1809+ will query the focused HWND, but doesn't advise of events + * or raise a focus event. Windows 11 will advise the provider of the + * focused HWND of events, but not any clientside providers. + */ + set_uia_hwnd_expects(6, 6, 6, 3, 0); + hr = IUIAutomation_AddFocusChangedEventHandler(uia_iface, NULL, + &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(FocusChangedHandler.ref > 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); + check_uia_hwnd_expects_at_most(6, 6, 6, 3, 0); + FocusChangedHandler.event_handler_added = TRUE; + + /* + * Focus changed event handlers are registered on the desktop with a scope + * of all elements, so all elements match regardless of runtime ID. + */ + initialize_provider(&Provider2, ProviderOptions_ServerSideProvider, NULL, TRUE); + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); + set_com_event_data(&exp_node_desc); + hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(uia_com_event_callback); + + /* + * Removing the focus changed event handler creates a desktop node - + * presumably to use to get a runtime ID for removal. + */ + set_uia_hwnd_expects(1, 1, 1, 0, 0); + hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface, + &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(FocusChangedHandler.ref == 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); + FocusChangedHandler.event_handler_added = FALSE; + check_uia_hwnd_expects(1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE, 0, FALSE); + + /* + * The focus changed event handler can also be removed by called + * RemoveAutomationEventHandler, which isn't documented. + */ + set_uia_hwnd_expects(6, 6, 6, 3, 0); + hr = IUIAutomation_AddFocusChangedEventHandler(uia_iface, NULL, + &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(FocusChangedHandler.ref > 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); + check_uia_hwnd_expects_at_most(6, 6, 6, 3, 0); + FocusChangedHandler.event_handler_added = TRUE; + + set_uia_hwnd_expects(1, 1, 1, 0, 0); + hr = IUIAutomation_GetRootElement(uia_iface, &elem); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_uia_hwnd_expects(1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE); + + hr = IUIAutomation_RemoveAutomationEventHandler(uia_iface, UIA_AutomationFocusChangedEventId, elem, + (IUIAutomationEventHandler *)&FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(FocusChangedHandler.ref == 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); + + IUIAutomationElement_Release(elem); +} + static void test_CUIAutomation_event_handlers(IUIAutomation *uia_iface) { IUIAutomationElement *elem; @@ -13334,6 +13478,7 @@ static void test_CUIAutomation_event_handlers(IUIAutomation *uia_iface) check_uia_hwnd_expects(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE);
test_IUIAutomationEventHandler(uia_iface, elem); + test_IUIAutomationFocusChangedEventHandler(uia_iface);
IUIAutomationElement_Release(elem); UiaRegisterProviderCallback(NULL);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 14 +- dlls/uiautomationcore/uia_com_client.c | 174 ++++++++++++++++----- 2 files changed, 138 insertions(+), 50 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 59ae9535892..af640dbe00f 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13392,8 +13392,8 @@ static void test_IUIAutomationFocusChangedEventHandler(IUIAutomation *uia_iface) set_uia_hwnd_expects(6, 6, 6, 3, 0); hr = IUIAutomation_AddFocusChangedEventHandler(uia_iface, NULL, &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(FocusChangedHandler.ref > 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(FocusChangedHandler.ref > 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); check_uia_hwnd_expects_at_most(6, 6, 6, 3, 0); FocusChangedHandler.event_handler_added = TRUE;
@@ -13407,7 +13407,7 @@ static void test_IUIAutomationFocusChangedEventHandler(IUIAutomation *uia_iface) set_com_event_data(&exp_node_desc); hr = UiaRaiseAutomationEvent(&Provider2.IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine CHECK_CALLED(uia_com_event_callback); + CHECK_CALLED(uia_com_event_callback);
/* * Removing the focus changed event handler creates a desktop node - @@ -13416,10 +13416,10 @@ static void test_IUIAutomationFocusChangedEventHandler(IUIAutomation *uia_iface) set_uia_hwnd_expects(1, 1, 1, 0, 0); hr = IUIAutomation_RemoveFocusChangedEventHandler(uia_iface, &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(FocusChangedHandler.ref == 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); FocusChangedHandler.event_handler_added = FALSE; - check_uia_hwnd_expects(1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE, 0, FALSE); + check_uia_hwnd_expects(1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
/* * The focus changed event handler can also be removed by called @@ -13428,8 +13428,8 @@ static void test_IUIAutomationFocusChangedEventHandler(IUIAutomation *uia_iface) set_uia_hwnd_expects(6, 6, 6, 3, 0); hr = IUIAutomation_AddFocusChangedEventHandler(uia_iface, NULL, &FocusChangedHandler.IUIAutomationFocusChangedEventHandler_iface); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(FocusChangedHandler.ref > 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(FocusChangedHandler.ref > 1, "Unexpected refcnt %ld\n", FocusChangedHandler.ref); check_uia_hwnd_expects_at_most(6, 6, 6, 3, 0); FocusChangedHandler.event_handler_added = TRUE;
diff --git a/dlls/uiautomationcore/uia_com_client.c b/dlls/uiautomationcore/uia_com_client.c index b4545706238..e0b71777ebe 100644 --- a/dlls/uiautomationcore/uia_com_client.c +++ b/dlls/uiautomationcore/uia_com_client.c @@ -1051,7 +1051,6 @@ static HRESULT uia_com_event_callback(struct uia_event *event, struct uia_event_ SAFEARRAY *cache_req, BSTR tree_struct) { struct uia_com_event *com_event = (struct uia_com_event *)event->u.clientside.callback_data; - IUIAutomationEventHandler *handler; IUIAutomationElement *elem; BSTR tree_struct2; HRESULT hr; @@ -1067,11 +1066,33 @@ static HRESULT uia_com_event_callback(struct uia_event *event, struct uia_event_ if (FAILED(hr)) return hr;
- hr = get_interface_in_git(&IID_IUIAutomationEventHandler, com_event->git_cookie, (IUnknown **)&handler); - if (SUCCEEDED(hr)) + switch (event->event_id) + { + case UIA_AutomationFocusChangedEventId: { - hr = IUIAutomationEventHandler_HandleAutomationEvent(handler, elem, event->event_id); - IUIAutomationEventHandler_Release(handler); + IUIAutomationFocusChangedEventHandler *handler; + + hr = get_interface_in_git(&IID_IUIAutomationFocusChangedEventHandler, com_event->git_cookie, (IUnknown **)&handler); + if (SUCCEEDED(hr)) + { + hr = IUIAutomationFocusChangedEventHandler_HandleFocusChangedEvent(handler, elem); + IUIAutomationFocusChangedEventHandler_Release(handler); + } + break; + } + + default: + { + IUIAutomationEventHandler *handler; + + hr = get_interface_in_git(&IID_IUIAutomationEventHandler, com_event->git_cookie, (IUnknown **)&handler); + if (SUCCEEDED(hr)) + { + hr = IUIAutomationEventHandler_HandleAutomationEvent(handler, elem, event->event_id); + IUIAutomationEventHandler_Release(handler); + } + break; + } } IUIAutomationElement_Release(elem);
@@ -3239,9 +3260,8 @@ static HRESULT WINAPI uia_iface_CreateNotCondition(IUIAutomation6 *iface, IUIAut return create_uia_not_condition_iface(out_condition, cond); }
-static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id, - IUIAutomationElement *elem, enum TreeScope scope, IUIAutomationCacheRequest *cache_req, - IUIAutomationEventHandler *handler) +static HRESULT uia_add_com_event_handler(IUIAutomation6 *iface, EVENTID event_id, IUIAutomationElement *elem, + enum TreeScope scope, IUIAutomationCacheRequest *cache_req, REFIID handler_riid, IUnknown *handler_unk) { struct UiaCacheRequest *cache_req_struct; struct uia_com_event *com_event = NULL; @@ -3250,23 +3270,11 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, IUnknown *handler_iface; 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)) { - IUnknown_Release(handler_iface); + IUnknown_Release(handler_unk); return hr; }
@@ -3291,7 +3299,13 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface,
com_event->from_cui8 = element->from_cui8; list_init(&com_event->event_handler_map_list_entry); - hr = register_interface_in_git((IUnknown *)handler, &IID_IUIAutomationEventHandler, &com_event->git_cookie); + + hr = IUnknown_QueryInterface(handler_unk, handler_riid, (void **)&handler_iface); + if (FAILED(hr)) + goto exit; + + hr = register_interface_in_git(handler_iface, handler_riid, &com_event->git_cookie); + IUnknown_Release(handler_iface); if (FAILED(hr)) goto exit;
@@ -3300,49 +3314,79 @@ static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, if (FAILED(hr)) goto exit;
- hr = uia_event_handlers_add_handler(handler_iface, runtime_id, event_id, com_event); + hr = uia_event_handlers_add_handler(handler_unk, runtime_id, event_id, com_event);
exit: if (FAILED(hr) && com_event) uia_event_handler_destroy(com_event); if (cache_req) IUIAutomationCacheRequest_Release(cache_req); - IUnknown_Release(handler_iface); SafeArrayDestroy(runtime_id);
return hr; }
-static HRESULT WINAPI uia_iface_RemoveAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id, - IUIAutomationElement *elem, IUIAutomationEventHandler *handler) +static HRESULT WINAPI uia_iface_AddAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id, + IUIAutomationElement *elem, enum TreeScope scope, IUIAutomationCacheRequest *cache_req, + IUIAutomationEventHandler *handler) { - struct uia_element *element; - IUnknown *handler_iface; - SAFEARRAY *runtime_id; + IUnknown *handler_unk; HRESULT hr;
- TRACE("%p, %d, %p, %p\n", iface, event_id, elem, handler); + TRACE("%p, %d, %p, %#x, %p, %p\n", iface, event_id, elem, scope, cache_req, handler);
if (!elem || !handler) - return S_OK; + return E_POINTER; + + if (event_id == UIA_AutomationFocusChangedEventId) + return E_INVALIDARG; + + hr = IUIAutomationEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_unk); + if (FAILED(hr)) + return hr; + + hr = uia_add_com_event_handler(iface, event_id, elem, scope, cache_req, &IID_IUIAutomationEventHandler, handler_unk); + IUnknown_Release(handler_unk); + + return hr; +} + +static HRESULT uia_remove_com_event_handler(EVENTID event_id, IUIAutomationElement *elem, IUnknown *handler_unk) +{ + struct uia_element *element; + SAFEARRAY *runtime_id; + HRESULT hr;
element = impl_from_IUIAutomationElement9((IUIAutomationElement9 *)elem); hr = UiaGetRuntimeId(element->node, &runtime_id); if (FAILED(hr) || !runtime_id) return hr;
- hr = IUIAutomationEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_iface); + uia_event_handlers_remove_handlers(handler_unk, runtime_id, event_id); + SafeArrayDestroy(runtime_id); + + return S_OK; +} + +static HRESULT WINAPI uia_iface_RemoveAutomationEventHandler(IUIAutomation6 *iface, EVENTID event_id, + IUIAutomationElement *elem, IUIAutomationEventHandler *handler) +{ + IUnknown *handler_unk; + HRESULT hr; + + TRACE("%p, %d, %p, %p\n", iface, event_id, elem, handler); + + if (!elem || !handler) + return S_OK; + + hr = IUIAutomationEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_unk); if (FAILED(hr)) - { - SafeArrayDestroy(runtime_id); return hr; - }
- uia_event_handlers_remove_handlers(handler_iface, runtime_id, event_id); - IUnknown_Release(handler_iface); - SafeArrayDestroy(runtime_id); + hr = uia_remove_com_event_handler(event_id, elem, handler_unk); + IUnknown_Release(handler_unk);
- return S_OK; + return hr; }
static HRESULT WINAPI uia_iface_AddPropertyChangedEventHandlerNativeArray(IUIAutomation6 *iface, @@ -3386,15 +3430,59 @@ static HRESULT WINAPI uia_iface_RemoveStructureChangedEventHandler(IUIAutomation static HRESULT WINAPI uia_iface_AddFocusChangedEventHandler(IUIAutomation6 *iface, IUIAutomationCacheRequest *cache_req, IUIAutomationFocusChangedEventHandler *handler) { - FIXME("%p, %p, %p: stub\n", iface, cache_req, handler); - return E_NOTIMPL; + IUIAutomationElement *elem; + IUnknown *handler_unk; + HRESULT hr; + + TRACE("%p, %p, %p\n", iface, cache_req, handler); + + if (!handler) + return E_POINTER; + + hr = IUIAutomationFocusChangedEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_unk); + if (FAILED(hr)) + return hr; + + hr = IUIAutomation6_GetRootElement(iface, &elem); + if (FAILED(hr)) + { + IUnknown_Release(handler_unk); + return hr; + } + + hr = uia_add_com_event_handler(iface, UIA_AutomationFocusChangedEventId, elem, TreeScope_SubTree, cache_req, + &IID_IUIAutomationFocusChangedEventHandler, handler_unk); + IUIAutomationElement_Release(elem); + IUnknown_Release(handler_unk); + + return hr; }
static HRESULT WINAPI uia_iface_RemoveFocusChangedEventHandler(IUIAutomation6 *iface, IUIAutomationFocusChangedEventHandler *handler) { - FIXME("%p, %p: stub\n", iface, handler); - return E_NOTIMPL; + IUIAutomationElement *elem; + IUnknown *handler_unk; + HRESULT hr; + + TRACE("%p, %p\n", iface, handler); + + hr = IUIAutomationFocusChangedEventHandler_QueryInterface(handler, &IID_IUnknown, (void **)&handler_unk); + if (FAILED(hr)) + return hr; + + hr = IUIAutomation6_GetRootElement(iface, &elem); + if (FAILED(hr)) + { + IUnknown_Release(handler_unk); + return hr; + } + + hr = uia_remove_com_event_handler(UIA_AutomationFocusChangedEventId, elem, handler_unk); + IUIAutomationElement_Release(elem); + IUnknown_Release(handler_unk); + + return hr; }
static HRESULT WINAPI uia_iface_RemoveAllEventHandlers(IUIAutomation6 *iface)
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_com_client.c:
- 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)) {
IUnknown_Release(handler_iface);
IUnknown_Release(handler_unk);
I don't think this Release has a matching AddRef anymore?