-- v2: uiautomationcore/tests: Add another test for IProxyProviderWinEventHandler child HWND scope checking. uiautomationcore: Add support for raising events passed to our IProxyProviderWinEventSink interface. uiautomationcore: Pass WinEvent data to IProxyProviderWinEventHandler::RespondToWinEvent. uiautomationcore: Add support for marking the root IAccessible as known on non-root IAccessibles upon proxy provider creation. uiautomationcore: Add support for ignoring ProviderOptions_UseComThreading when creating an HUIANODE. uiautomationcore: Create HUIANODE for WinEvents that should invoke IProxyProviderWinEventHandler::RespondToWinEvent. uiautomationcore: Check if we should try to invoke IProxyProviderWinEventHandler::RespondToWinEvent for registered UIA events. uiautomationcore: Introduce uia_event_for_each function for iterating through registered events.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_event.c | 141 ++++++++++++++++++------------ 1 file changed, 85 insertions(+), 56 deletions(-)
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 96b6ac448aa..d493ab0a243 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -252,6 +252,51 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) } }
+typedef HRESULT UiaWineEventForEachCallback(struct uia_event *, void *); +static HRESULT uia_event_for_each(int event_id, UiaWineEventForEachCallback *callback, void *user_data, + BOOL clientside_only) +{ + struct uia_event_map_entry *event_entry; + HRESULT hr = S_OK; + int i; + + EnterCriticalSection(&event_map_cs); + if ((event_entry = uia_get_event_map_entry_for_event(event_id))) + InterlockedIncrement(&event_entry->refs); + LeaveCriticalSection(&event_map_cs); + + if (!event_entry) + return S_OK; + + for (i = 0; i < 2; i++) + { + struct list *events = !i ? &event_entry->events_list : &event_entry->serverside_events_list; + struct list *cursor, *cursor2; + + if (i && clientside_only) + break; + + LIST_FOR_EACH_SAFE(cursor, cursor2, events) + { + struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + + /* Event is no longer valid. */ + if (InterlockedCompareExchange(&event->event_defunct, 0, 0) != 0) + continue; + + hr = callback(event, user_data); + if (FAILED(hr)) + goto exit; + } + } + +exit: + if (FAILED(hr)) + WARN("Event callback failed with hr %#lx\n", hr); + uia_event_map_entry_release(event_entry); + return hr; +} + /* * Functions for struct uia_event_args, a reference counted structure * used to store event arguments. This is necessary for serverside events @@ -1525,73 +1570,57 @@ static HRESULT uia_event_check_match(HUIANODE node, HUIANODE nav_start_node, SAF return hr; }
-static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct uia_event_args *args) +struct uia_elprov_event_data { - struct uia_event_map_entry *event_entry; - enum ProviderOptions prov_opts = 0; - struct list *cursor, *cursor2; - HUIANODE node; - SAFEARRAY *sa; - HRESULT hr; - - hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts); - if (FAILED(hr)) - return hr; + IRawElementProviderSimple *elprov; + struct uia_event_args *args; + BOOL clientside_only;
- EnterCriticalSection(&event_map_cs); - if ((event_entry = uia_get_event_map_entry_for_event(args->simple_args.EventId))) - InterlockedIncrement(&event_entry->refs); - LeaveCriticalSection(&event_map_cs); + SAFEARRAY *rt_id; + HUIANODE node; +};
- if (!event_entry) - return S_OK; +static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *data) +{ + struct uia_elprov_event_data *event_data = (struct uia_elprov_event_data *)data; + HRESULT hr;
- /* - * For events raised on server-side providers, we don't want to add any - * clientside HWND providers. - */ - if (prov_opts & ProviderOptions_ServerSideProvider) - hr = create_uia_node_from_elprov(elprov, &node, FALSE); - else - hr = create_uia_node_from_elprov(elprov, &node, TRUE); - if (FAILED(hr)) + if (!event_data->node) { - uia_event_map_entry_release(event_entry); - return hr; - } + /* + * For events raised on server-side providers, we don't want to add any + * clientside HWND providers. + */ + hr = create_uia_node_from_elprov(event_data->elprov, &event_data->node, event_data->clientside_only); + if (FAILED(hr)) + return hr;
- hr = UiaGetRuntimeId(node, &sa); - if (FAILED(hr)) - { - uia_event_map_entry_release(event_entry); - UiaNodeRelease(node); - return hr; + hr = UiaGetRuntimeId(event_data->node, &event_data->rt_id); + if (FAILED(hr)) + return hr; }
- LIST_FOR_EACH_SAFE(cursor, cursor2, &event_entry->events_list) - { - struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + return uia_event_check_match(event_data->node, event_data->node, event_data->rt_id, event_data->args, event); +}
- hr = uia_event_check_match(node, node, sa, args, event); - if (FAILED(hr)) - break; - } +static HRESULT uia_raise_elprov_event(IRawElementProviderSimple *elprov, struct uia_event_args *args) +{ + struct uia_elprov_event_data event_data = { elprov, args }; + enum ProviderOptions prov_opts = 0; + HRESULT hr;
- if (prov_opts & ProviderOptions_ServerSideProvider) - { - LIST_FOR_EACH_SAFE(cursor, cursor2, &event_entry->serverside_events_list) - { - struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts); + if (FAILED(hr)) + return hr;
- hr = uia_event_check_match(node, node, sa, args, event); - if (FAILED(hr)) - break; - } - } + event_data.clientside_only = !(prov_opts & ProviderOptions_ServerSideProvider); + hr = uia_event_for_each(args->simple_args.EventId, uia_raise_elprov_event_callback, (void *)&event_data, + event_data.clientside_only); + if (FAILED(hr)) + WARN("uia_event_for_each failed with hr %#lx\n", hr);
- uia_event_map_entry_release(event_entry); - SafeArrayDestroy(sa); - UiaNodeRelease(node); + UiaNodeRelease(event_data.node); + SafeArrayDestroy(event_data.rt_id);
return hr; } @@ -1624,7 +1653,7 @@ HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *elprov, EVENTI if (!args) return E_OUTOFMEMORY;
- hr = uia_raise_event(elprov, args); + hr = uia_raise_elprov_event(elprov, args); uia_event_args_release(args); if (FAILED(hr)) return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 12 ++++---- dlls/uiautomationcore/uia_event.c | 34 ++++++++++++++++++++++ dlls/uiautomationcore/uia_private.h | 2 ++ dlls/uiautomationcore/uia_provider.c | 11 ++----- dlls/uiautomationcore/uia_utils.c | 7 ++++- 5 files changed, 51 insertions(+), 15 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 98990d3b19c..01d9448fdfc 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -16823,7 +16823,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(winproc_GETOBJECT_UiaRoot, 2); /* Only called twice on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, FALSE, FALSE, FALSE); - todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot);
/* * Get rid of our serverside provider and raise EVENT_OBJECT_FOCUS @@ -16848,7 +16848,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para 1, TRUE, FALSE, TRUE); if (CALLED_COUNT(winproc_GETOBJECT_CLIENT)) ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE); + check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE); method_sequences_enabled = FALSE;
/* @@ -16895,7 +16895,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); - todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
/* * Child HWND now has a serverside provider, WinEvent is ignored. @@ -16906,7 +16906,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); /* Only sent 2 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, FALSE, FALSE, FALSE); - todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); CHECK_CALLED_AT_MOST(winproc_GETOBJECT_UiaRoot, 1);
/* @@ -16940,7 +16940,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE, 1, TRUE); + check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE);
/* Raise a WinEvent on our test child HWND, both event callbacks invoked. */ child_win_prov_root = NULL; @@ -16950,7 +16950,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 8); /* Only sent 8 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, ARRAY_SIZE(event_handles), TRUE, TRUE, TRUE); - todo_wine CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); + CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); check_uia_hwnd_expects_at_least(0, FALSE, 2, TRUE, 2, TRUE, 2, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index d493ab0a243..86bdc7bd006 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -511,6 +511,31 @@ static HRESULT uia_raise_serverside_event(struct uia_queue_uia_event *event) return hr; }
+static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) +{ + struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + + if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd)) + { + /* Top level HWND that isn't within our WinEvent HWND scope. */ + if (uia_is_top_level_hwnd(win_event->hwnd)) + return S_OK; + + /* This isn't a top-level HWND, check if its parent or root HWND is within our scope. */ + if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, GetAncestor(win_event->hwnd, GA_PARENT)) && + !uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, GetAncestor(win_event->hwnd, GA_ROOT))) + return S_OK; + } + + /* Has a native serverside provider, no need to do WinEvent translation. */ + if (UiaHasServerSideProvider(win_event->hwnd)) + return S_OK; + + FIXME("IProxyProviderWinEventHandler usage is currently unimplemented.\n"); + + return S_OK; +} + static void uia_event_thread_process_queue(struct list *event_queue) { while (1) @@ -538,6 +563,15 @@ static void uia_event_thread_process_queue(struct list *event_queue) break; }
+ case QUEUE_EVENT_TYPE_WIN_EVENT: + { + struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)event; + + hr = uia_event_for_each(win_event_to_uia_event_id(win_event->event_id), uia_win_event_for_each_callback, + (void *)win_event, TRUE); + break; + } + default: break; } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index c298967be00..218392ef06c 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -255,6 +255,8 @@ HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *el HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN; BOOL uia_hwnd_is_visible(HWND hwnd) DECLSPEC_HIDDEN; +BOOL uia_is_top_level_hwnd(HWND hwnd) DECLSPEC_HIDDEN; +BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; HRESULT uia_hwnd_map_add_hwnd(struct rb_tree *hwnd_map, HWND hwnd) DECLSPEC_HIDDEN; void uia_hwnd_map_init(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; void uia_hwnd_map_destroy(struct rb_tree *hwnd_map) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 269ce63a2fd..6a3b2d061fe 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1545,11 +1545,6 @@ static HRESULT uia_send_message_timeout(HWND hwnd, UINT msg, WPARAM wparam, LPAR return S_OK; }
-static BOOL is_top_level_hwnd(HWND hwnd) -{ - return GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(); -} - static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type) { LONG_PTR style, ex_style; @@ -1576,7 +1571,7 @@ static HRESULT get_uia_control_type_for_hwnd(HWND hwnd, int *control_type) }
/* Non top-level HWNDs are considered panes as well. */ - if (!is_top_level_hwnd(hwnd)) + if (!uia_is_top_level_hwnd(hwnd)) *control_type = UIA_PaneControlTypeId; else *control_type = UIA_WindowControlTypeId; @@ -1816,7 +1811,7 @@ static HRESULT WINAPI base_hwnd_fragment_Navigate(IRawElementProviderFragment *i * Top level owned windows have their owner window as a parent instead * of the desktop window. */ - if (is_top_level_hwnd(base_hwnd_prov->hwnd) && (owner = GetWindow(base_hwnd_prov->hwnd, GW_OWNER))) + if (uia_is_top_level_hwnd(base_hwnd_prov->hwnd) && (owner = GetWindow(base_hwnd_prov->hwnd, GW_OWNER))) parent = owner; else parent = GetAncestor(base_hwnd_prov->hwnd, GA_PARENT); @@ -1866,7 +1861,7 @@ static HRESULT WINAPI base_hwnd_fragment_get_BoundingRectangle(IRawElementProvid memset(ret_val, 0, sizeof(*ret_val));
/* Top level minimized window - Return empty rect. */ - if (is_top_level_hwnd(base_hwnd_prov->hwnd) && IsIconic(base_hwnd_prov->hwnd)) + if (uia_is_top_level_hwnd(base_hwnd_prov->hwnd) && IsIconic(base_hwnd_prov->hwnd)) return S_OK;
if (!GetWindowRect(base_hwnd_prov->hwnd, &rect)) diff --git a/dlls/uiautomationcore/uia_utils.c b/dlls/uiautomationcore/uia_utils.c index 9840fe4fe40..866358f97c8 100644 --- a/dlls/uiautomationcore/uia_utils.c +++ b/dlls/uiautomationcore/uia_utils.c @@ -404,6 +404,11 @@ BOOL uia_hwnd_is_visible(HWND hwnd) return TRUE; }
+BOOL uia_is_top_level_hwnd(HWND hwnd) +{ + return GetAncestor(hwnd, GA_PARENT) == GetDesktopWindow(); +} + /* * rbtree to efficiently store a collection of HWNDs. */ @@ -429,7 +434,7 @@ static void uia_hwnd_map_free(struct rb_entry *entry, void *context) free(hwnd_entry); }
-static BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) +BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) { return !!rb_get(hwnd_map, hwnd); }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 42 ++++++++++----- dlls/uiautomationcore/uia_event.c | 62 ++++++++++++++++++++++ 2 files changed, 90 insertions(+), 14 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 01d9448fdfc..22c08e9e3fa 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1761,6 +1761,15 @@ static struct prov_method_sequence *sequence; { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
+#define NODE_CREATE_SEQ_TODO(prov) \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, \ + /* Win10v1507 and below call this. */ \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ + { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_TODO }, \ + { prov , PROV_GET_PROPERTY_VALUE, METHOD_TODO }, \ + { prov , FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ \ + { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ + #define NODE_CREATE_SEQ_OPTIONAL(prov) \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ /* Win10v1507 and below call this. */ \ @@ -16708,11 +16717,17 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ - { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, - { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, - NODE_CREATE_SEQ(&Provider_child), - { &Provider_child, FRAG_GET_RUNTIME_ID }, - { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ + /* + * The following two are currently only done on Wine. Windows doesn't do + * this because the node created is never passed out of the event thread. + */ + { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, + { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, + NODE_CREATE_SEQ_TODO(&Provider_child), + { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ { 0 } };
@@ -16846,9 +16861,8 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - if (CALLED_COUNT(winproc_GETOBJECT_CLIENT)) - ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE); + ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); + check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE); method_sequences_enabled = FALSE;
/* @@ -16894,7 +16908,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 4); /* Only sent 4 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE); CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
/* @@ -16940,7 +16954,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects_at_least(1, TRUE, 1, TRUE, 1, TRUE, 1, FALSE, 1, TRUE); + check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE);
/* Raise a WinEvent on our test child HWND, both event callbacks invoked. */ child_win_prov_root = NULL; @@ -16951,7 +16965,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, ARRAY_SIZE(event_handles), TRUE, TRUE, TRUE); CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); - check_uia_hwnd_expects_at_least(0, FALSE, 2, TRUE, 2, TRUE, 2, TRUE, 0, FALSE); + check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 2, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -16993,7 +17007,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(0, 1, 1, 0, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 0, FALSE, 0, FALSE); + check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
/* * Top-level HWND, a child of the desktop HWND. Will not have an event @@ -17034,14 +17048,14 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 2, 1); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects(1, TRUE, 1, TRUE, 1, TRUE, 2, TRUE, 1, TRUE); + check_uia_hwnd_expects(1, TRUE, 1, FALSE, 1, FALSE, 2, FALSE, 1, FALSE);
/* Child HWNDs of our test window are handled as well. */ SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); set_uia_hwnd_expects(0, 1, 1, 1, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, 1, TRUE, FALSE, TRUE); - check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); + check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 86bdc7bd006..6ccc66b2cc6 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -511,9 +511,57 @@ static HRESULT uia_raise_serverside_event(struct uia_queue_uia_event *event) return hr; }
+static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawElementProviderSimple **ret_elprov) +{ + IRawElementProviderSimple *elprov; + IAccessible *acc; + int child_id; + HRESULT hr; + + *ret_elprov = NULL; + hr = AccessibleObjectFromWindow(hwnd, OBJID_CLIENT, &IID_IAccessible, (void **)&acc); + if (FAILED(hr)) + return hr; + + child_id = in_child_id; + if (in_child_id != CHILDID_SELF) + { + IDispatch *disp; + VARIANT cid; + + disp = NULL; + variant_init_i4(&cid, in_child_id); + hr = IAccessible_get_accChild(acc, cid, &disp); + if (FAILED(hr)) + TRACE("get_accChild failed with %#lx!\n", hr); + + if (SUCCEEDED(hr) && disp) + { + IAccessible_Release(acc); + hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void **)&acc); + IDispatch_Release(disp); + if (FAILED(hr)) + return hr; + + child_id = CHILDID_SELF; + } + } + + hr = create_msaa_provider(acc, child_id, hwnd, in_child_id == CHILDID_SELF, &elprov); + IAccessible_Release(acc); + if (FAILED(hr)) + return hr; + + *ret_elprov = elprov; + return S_OK; +} + static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) { struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + IRawElementProviderSimple *elprov; + HUIANODE node; + HRESULT hr;
if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd)) { @@ -531,8 +579,22 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (UiaHasServerSideProvider(win_event->hwnd)) return S_OK;
+ /* + * Regardless of the object ID of the WinEvent, OBJID_CLIENT is queried + * for the HWND with the same child ID as the WinEvent. + */ + hr = create_msaa_provider_from_hwnd(win_event->hwnd, win_event->child_id, &elprov); + if (FAILED(hr)) + return hr; + + hr = create_uia_node_from_elprov(elprov, &node, TRUE); + IRawElementProviderSimple_Release(elprov); + if (FAILED(hr)) + return hr; + FIXME("IProxyProviderWinEventHandler usage is currently unimplemented.\n");
+ UiaNodeRelease(node); return S_OK; }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 6 ------ dlls/uiautomationcore/uia_client.c | 15 ++++++++++----- dlls/uiautomationcore/uia_event.c | 4 ++-- dlls/uiautomationcore/uia_private.h | 4 +++- dlls/uiautomationcore/uia_provider.c | 4 ++-- 5 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 22c08e9e3fa..a178487e420 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -16717,12 +16717,6 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ - /* - * The following two are currently only done on Wine. Windows doesn't do - * this because the node created is never passed out of the event thread. - */ - { &Provider_nc2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, - { &Provider_hwnd2, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, NODE_CREATE_SEQ_TODO(&Provider_child), diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 09e0d179172..36157ee70ba 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -613,6 +613,8 @@ static HRESULT create_uia_node(struct uia_node **out_node, int node_flags) node->ignore_clientside_hwnd_provs = TRUE; if (node_flags & NODE_FLAG_NO_PREPARE) node->no_prepare = TRUE; + if (node_flags & NODE_FLAG_IGNORE_COM_THREADING) + node->ignore_com_threading = TRUE;
*out_node = node; return S_OK; @@ -664,6 +666,9 @@ static HRESULT prepare_uia_node(struct uia_node *node) prov_idx++; }
+ if (node->ignore_com_threading) + return S_OK; + for (i = 0; i < PROV_TYPE_COUNT; i++) { enum ProviderOptions prov_opts; @@ -1361,7 +1366,7 @@ static HRESULT get_variant_for_elprov_node(IRawElementProviderSimple *elprov, BO
VariantInit(v);
- hr = create_uia_node_from_elprov(elprov, &node, !refuse_hwnd_providers); + hr = create_uia_node_from_elprov(elprov, &node, !refuse_hwnd_providers, 0); IRawElementProviderSimple_Release(elprov); if (SUCCEEDED(hr)) { @@ -1910,7 +1915,7 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT if (FAILED(hr)) goto exit;
- hr = create_uia_node_from_elprov(elprov, &node, !prov->refuse_hwnd_node_providers); + hr = create_uia_node_from_elprov(elprov, &node, !prov->refuse_hwnd_node_providers, 0); IRawElementProviderSimple_Release(elprov); if (SUCCEEDED(hr)) { @@ -1969,7 +1974,7 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid
static HRESULT uia_get_providers_for_hwnd(struct uia_node *node); HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, - BOOL get_hwnd_providers) + BOOL get_hwnd_providers, int node_flags) { static const int unsupported_prov_opts = ProviderOptions_ProviderOwnsSetFocus | ProviderOptions_HasNativeIAccessible | ProviderOptions_UseClientCoordinates; @@ -1998,7 +2003,7 @@ HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE else prov_type = PROV_TYPE_MAIN;
- hr = create_uia_node(&node, 0); + hr = create_uia_node(&node, node_flags); if (FAILED(hr)) return hr;
@@ -2040,7 +2045,7 @@ HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE * if (!elprov || !huianode) return E_INVALIDARG;
- return create_uia_node_from_elprov(elprov, huianode, TRUE); + return create_uia_node_from_elprov(elprov, huianode, TRUE, 0); }
/* diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 6ccc66b2cc6..220ad83a30d 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -587,7 +587,7 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (FAILED(hr)) return hr;
- hr = create_uia_node_from_elprov(elprov, &node, TRUE); + hr = create_uia_node_from_elprov(elprov, &node, TRUE, NODE_FLAG_IGNORE_COM_THREADING); IRawElementProviderSimple_Release(elprov); if (FAILED(hr)) return hr; @@ -1687,7 +1687,7 @@ static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *da * For events raised on server-side providers, we don't want to add any * clientside HWND providers. */ - hr = create_uia_node_from_elprov(event_data->elprov, &event_data->node, event_data->clientside_only); + hr = create_uia_node_from_elprov(event_data->elprov, &event_data->node, event_data->clientside_only, 0); if (FAILED(hr)) return hr;
diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 218392ef06c..7ac2335dd77 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -57,6 +57,7 @@ enum uia_node_prov_type { enum uia_node_flags { NODE_FLAG_IGNORE_CLIENTSIDE_HWND_PROVS = 0x01, NODE_FLAG_NO_PREPARE = 0x02, + NODE_FLAG_IGNORE_COM_THREADING = 0x04, };
struct uia_node { @@ -74,6 +75,7 @@ struct uia_node { BOOL nested_node; BOOL disconnected; int creator_prov_type; + BOOL ignore_com_threading; BOOL ignore_clientside_hwnd_provs;
struct list prov_thread_list_entry; @@ -212,7 +214,7 @@ HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPE HRESULT clone_uia_node(HUIANODE in_node, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, - BOOL get_hwnd_providers) DECLSPEC_HIDDEN; + BOOL get_hwnd_providers, int node_flags) DECLSPEC_HIDDEN; HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) DECLSPEC_HIDDEN; HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) DECLSPEC_HIDDEN; BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 6a3b2d061fe..22eba343511 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -2275,7 +2275,7 @@ LRESULT WINAPI UiaReturnRawElementProvider(HWND hwnd, WPARAM wparam, return 0; }
- hr = create_uia_node_from_elprov(elprov, &node, FALSE); + hr = create_uia_node_from_elprov(elprov, &node, FALSE, 0); if (FAILED(hr)) { WARN("Failed to create HUIANODE with hr %#lx\n", hr); @@ -2296,7 +2296,7 @@ HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov)
TRACE("(%p)\n", elprov);
- hr = create_uia_node_from_elprov(elprov, &node, FALSE); + hr = create_uia_node_from_elprov(elprov, &node, FALSE, 0); if (FAILED(hr)) return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 2 +- dlls/uiautomationcore/uia_event.c | 2 +- dlls/uiautomationcore/uia_private.h | 4 ++-- dlls/uiautomationcore/uia_provider.c | 25 ++++++++++++++----------- 4 files changed, 18 insertions(+), 15 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 36157ee70ba..1ff3e1a9a46 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -3013,7 +3013,7 @@ static SAFEARRAY WINAPI *default_uia_provider_callback(HWND hwnd, enum ProviderT if (FAILED(hr) || !acc) break;
- hr = create_msaa_provider(acc, CHILDID_SELF, hwnd, TRUE, &elprov); + hr = create_msaa_provider(acc, CHILDID_SELF, hwnd, TRUE, TRUE, &elprov); if (FAILED(hr)) WARN("Failed to create MSAA proxy provider with hr %#lx\n", hr);
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 220ad83a30d..08f89899c0d 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -547,7 +547,7 @@ static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawEl } }
- hr = create_msaa_provider(acc, child_id, hwnd, in_child_id == CHILDID_SELF, &elprov); + hr = create_msaa_provider(acc, child_id, hwnd, TRUE, in_child_id == CHILDID_SELF, &elprov); IAccessible_Release(acc); if (FAILED(hr)) return hr; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 7ac2335dd77..abd03ce308e 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -243,8 +243,8 @@ HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov) void uia_stop_provider_thread(void) DECLSPEC_HIDDEN; void uia_provider_thread_remove_node(HUIANODE node) DECLSPEC_HIDDEN; LRESULT uia_lresult_from_node(HUIANODE huianode) DECLSPEC_HIDDEN; -HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL known_root_acc, - IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN; +HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL root_acc_known, + BOOL is_root_acc, IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN;
/* uia_utils.c */ HRESULT register_interface_in_git(IUnknown *iface, REFIID riid, DWORD *ret_cookie) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 22eba343511..917d21d8a82 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -855,7 +855,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, else acc = msaa_prov->acc;
- hr = create_msaa_provider(acc, CHILDID_SELF, NULL, FALSE, &elprov); + hr = create_msaa_provider(acc, CHILDID_SELF, NULL, FALSE, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -886,7 +886,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, if (FAILED(hr) || !acc) break;
- hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov); + hr = create_msaa_provider(acc, child_id, NULL, FALSE, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -936,7 +936,7 @@ static HRESULT WINAPI msaa_fragment_Navigate(IRawElementProviderFragment *iface, if (FAILED(hr) || !acc) break;
- hr = create_msaa_provider(acc, child_id, NULL, FALSE, &elprov); + hr = create_msaa_provider(acc, child_id, NULL, FALSE, FALSE, &elprov); if (SUCCEEDED(hr)) { struct msaa_provider *prov = impl_from_msaa_provider(elprov); @@ -1031,7 +1031,7 @@ static HRESULT WINAPI msaa_fragment_get_FragmentRoot(IRawElementProviderFragment if (FAILED(hr) || !acc) return hr;
- hr = create_msaa_provider(acc, CHILDID_SELF, msaa_prov->hwnd, TRUE, &elprov); + hr = create_msaa_provider(acc, CHILDID_SELF, msaa_prov->hwnd, TRUE, TRUE, &elprov); IAccessible_Release(acc); if (FAILED(hr)) return hr; @@ -1154,7 +1154,7 @@ static HRESULT msaa_acc_get_focus(struct msaa_provider *prov, struct msaa_provid } }
- hr = create_msaa_provider(focus_acc, focus_cid, hwnd, FALSE, &elprov); + hr = create_msaa_provider(focus_acc, focus_cid, hwnd, FALSE, FALSE, &elprov); IAccessible_Release(focus_acc); if (SUCCEEDED(hr)) *out_prov = impl_from_msaa_provider(elprov); @@ -1176,7 +1176,7 @@ static HRESULT WINAPI msaa_fragment_root_GetFocus(IRawElementProviderFragmentRoo if (V_I4(&msaa_prov->cid) != CHILDID_SELF) return S_OK;
- hr = create_msaa_provider(msaa_prov->acc, CHILDID_SELF, msaa_prov->hwnd, FALSE, &elprov); + hr = create_msaa_provider(msaa_prov->acc, CHILDID_SELF, msaa_prov->hwnd, FALSE, FALSE, &elprov); if (FAILED(hr)) return hr;
@@ -1440,8 +1440,8 @@ static const IProxyProviderWinEventHandlerVtbl msaa_winevent_handler_vtbl = { msaa_winevent_handler_RespondToWinEvent, };
-HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL known_root_acc, - IRawElementProviderSimple **elprov) +HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL root_acc_known, + BOOL is_root_acc, IRawElementProviderSimple **elprov) { struct msaa_provider *msaa_prov = calloc(1, sizeof(*msaa_prov));
@@ -1470,8 +1470,11 @@ HRESULT create_msaa_provider(IAccessible *acc, LONG child_id, HWND hwnd, BOOL kn else msaa_prov->hwnd = hwnd;
- if (known_root_acc) - msaa_prov->root_acc_check_ran = msaa_prov->is_root_acc = TRUE; + if (root_acc_known) + { + msaa_prov->root_acc_check_ran = TRUE; + msaa_prov->is_root_acc = is_root_acc; + }
*elprov = &msaa_prov->IRawElementProviderSimple_iface;
@@ -1515,7 +1518,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, LONG child_id, DWORD if (!hwnd) return E_FAIL;
- return create_msaa_provider(acc, child_id, hwnd, FALSE, elprov); + return create_msaa_provider(acc, child_id, hwnd, FALSE, FALSE, elprov); }
static HRESULT uia_get_hr_for_last_error(void)
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 16 ++- dlls/uiautomationcore/uia_classes.idl | 3 + dlls/uiautomationcore/uia_client.c | 44 +++++++ dlls/uiautomationcore/uia_event.c | 133 ++++++++++++++++++++- dlls/uiautomationcore/uia_private.h | 3 + 5 files changed, 192 insertions(+), 7 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index a178487e420..d1e276b0b00 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -449,6 +449,11 @@ static void test_implements_interface_(IUnknown *unk, const GUID *iid, BOOL exp_ IUnknown_Release(unk2); }
+#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); + #define DEFINE_ACC_METHOD_EXPECT(method) \ int expect_ ## method , called_ ## method
@@ -2824,6 +2829,11 @@ static HRESULT WINAPI ProviderWinEventHandler_RespondToWinEvent(IProxyProviderWi
if (data->responder_prov) { + /* + * The IProxyProviderWinEventSink interface uses the free threaded + * marshaler, so no proxy will be created in-process. + */ + check_interface_marshal_proxy_creation((IUnknown *)event_sink, &IID_IProxyProviderWinEventSink, FALSE); hr = IProxyProviderWinEventSink_AddAutomationEvent(event_sink, data->responder_prov, data->responder_event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } @@ -11976,8 +11986,6 @@ static DWORD WINAPI interface_marshal_proxy_thread(LPVOID param) 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) { @@ -16717,8 +16725,8 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ - { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, - { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, + { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, + { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, NODE_CREATE_SEQ_TODO(&Provider_child), { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index b1dea2d414d..22d9d650502 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -19,6 +19,7 @@ #pragma makedep regtypelib
import "oaidl.idl"; +import "uiautomationcore.idl";
struct uia_prop_info { const GUID *guid; @@ -92,6 +93,8 @@ library UIA_wine_private HRESULT navigate([in]int nav_dir, [out, retval]VARIANT *ret_val); HRESULT get_focus([out, retval]VARIANT *ret_val); HRESULT attach_event([in]LONG_PTR huiaevent); + HRESULT respond_to_win_event([in]DWORD win_event, [in]ULONG hwnd, [in]LONG obj_id, [in]LONG child_id, + [in]IProxyProviderWinEventSink *sink); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 1ff3e1a9a46..f7b602ab2ed 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -378,6 +378,22 @@ static HRESULT attach_event_to_node_provider(IWineUiaNode *node, int idx, HUIAEV return hr; }
+HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD win_event, HWND hwnd, LONG obj_id, + LONG child_id, IProxyProviderWinEventSink *sink) +{ + IWineUiaProvider *prov; + HRESULT hr; + + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_respond_to_win_event(prov, win_event, HandleToUlong(hwnd), obj_id, child_id, sink); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1939,6 +1955,23 @@ exit: return hr; }
+static HRESULT WINAPI uia_provider_respond_to_win_event(IWineUiaProvider *iface, DWORD win_event, ULONG hwnd, LONG obj_id, + LONG child_id, IProxyProviderWinEventSink *sink) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + IProxyProviderWinEventHandler *handler; + HRESULT hr; + + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IProxyProviderWinEventHandler, (void **)&handler); + if (FAILED(hr)) + return S_OK; + + hr = IProxyProviderWinEventHandler_RespondToWinEvent(handler, win_event, UlongToHandle(hwnd), obj_id, child_id, sink); + IProxyProviderWinEventHandler_Release(handler); + + return hr; +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1949,6 +1982,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_navigate, uia_provider_get_focus, uia_provider_attach_event, + uia_provider_respond_to_win_event, };
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2388,6 +2422,15 @@ static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *if return hr; }
+static HRESULT WINAPI uia_nested_node_provider_respond_to_win_event(IWineUiaProvider *iface, DWORD win_event, ULONG hwnd, + LONG obj_id, LONG child_id, IProxyProviderWinEventSink *sink) +{ + FIXME("%p, %#lx, #%lx, %#lx, %#lx, %p: stub\n", iface, win_event, hwnd, obj_id, child_id, sink); + /* This should not be called. */ + assert(0); + return E_FAIL; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2398,6 +2441,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_navigate, uia_nested_node_provider_get_focus, uia_nested_node_provider_attach_event, + uia_nested_node_provider_respond_to_win_event, };
static BOOL is_nested_node_provider(IWineUiaProvider *iface) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 08f89899c0d..4e28d99ac49 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -20,7 +20,6 @@
#include "wine/debug.h" #include "wine/rbtree.h" -#include "assert.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
@@ -323,6 +322,118 @@ static void uia_event_args_release(struct uia_event_args *args) free(args); }
+/* + * IProxyProviderWinEventSink interface implementation. + */ +struct uia_proxy_win_event_sink { + IProxyProviderWinEventSink IProxyProviderWinEventSink_iface; + LONG ref; + + int event_id; + IUnknown *marshal; +}; + +static inline struct uia_proxy_win_event_sink *impl_from_IProxyProviderWinEventSink(IProxyProviderWinEventSink *iface) +{ + return CONTAINING_RECORD(iface, struct uia_proxy_win_event_sink, IProxyProviderWinEventSink_iface); +} + +static HRESULT WINAPI uia_proxy_win_event_sink_QueryInterface(IProxyProviderWinEventSink *iface, REFIID riid, void **obj) +{ + struct uia_proxy_win_event_sink *sink = impl_from_IProxyProviderWinEventSink(iface); + + *obj = NULL; + if (IsEqualIID(riid, &IID_IProxyProviderWinEventSink) || IsEqualIID(riid, &IID_IUnknown)) + *obj = iface; + else if (IsEqualIID(riid, &IID_IMarshal)) + return IUnknown_QueryInterface(sink->marshal, riid, obj); + else + return E_NOINTERFACE; + + IProxyProviderWinEventSink_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_proxy_win_event_sink_AddRef(IProxyProviderWinEventSink *iface) +{ + struct uia_proxy_win_event_sink *sink = impl_from_IProxyProviderWinEventSink(iface); + ULONG ref = InterlockedIncrement(&sink->ref); + + TRACE("%p, refcount %ld\n", sink, ref); + + return ref; +} + +static ULONG WINAPI uia_proxy_win_event_sink_Release(IProxyProviderWinEventSink *iface) +{ + struct uia_proxy_win_event_sink *sink = impl_from_IProxyProviderWinEventSink(iface); + ULONG ref = InterlockedDecrement(&sink->ref); + + TRACE("%p, refcount %ld\n", sink, ref); + + if (!ref) + { + IUnknown_Release(sink->marshal); + free(sink); + } + + return ref; +} + +static HRESULT WINAPI uia_proxy_win_event_sink_AddAutomationPropertyChangedEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, PROPERTYID prop_id, VARIANT new_value) +{ + FIXME("%p, %p, %d, %s: stub\n", iface, elprov, prop_id, debugstr_variant(&new_value)); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_proxy_win_event_sink_AddAutomationEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, EVENTID event_id) +{ + FIXME("%p, %p, %d: stub\n", iface, elprov, event_id); + return S_OK; +} + +static HRESULT WINAPI uia_proxy_win_event_sink_AddStructureChangedEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, enum StructureChangeType structure_change_type, SAFEARRAY *runtime_id) +{ + FIXME("%p, %p, %d, %p: stub\n", iface, elprov, structure_change_type, runtime_id); + return E_NOTIMPL; +} + +static const IProxyProviderWinEventSinkVtbl uia_proxy_event_sink_vtbl = { + uia_proxy_win_event_sink_QueryInterface, + uia_proxy_win_event_sink_AddRef, + uia_proxy_win_event_sink_Release, + uia_proxy_win_event_sink_AddAutomationPropertyChangedEvent, + uia_proxy_win_event_sink_AddAutomationEvent, + uia_proxy_win_event_sink_AddStructureChangedEvent, +}; + +static HRESULT create_proxy_win_event_sink(struct uia_proxy_win_event_sink **out_sink, int event_id) +{ + struct uia_proxy_win_event_sink *sink = calloc(1, sizeof(*sink)); + HRESULT hr; + + *out_sink = NULL; + if (!sink) + return E_OUTOFMEMORY; + + sink->IProxyProviderWinEventSink_iface.lpVtbl = &uia_proxy_event_sink_vtbl; + sink->ref = 1; + sink->event_id = event_id; + + hr = CoCreateFreeThreadedMarshaler((IUnknown *)&sink->IProxyProviderWinEventSink_iface, &sink->marshal); + if (FAILED(hr)) + { + free(sink); + return hr; + } + + *out_sink = sink; + return S_OK; +} + /* * UI Automation event thread. */ @@ -559,9 +670,12 @@ static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawEl static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) { struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + struct uia_proxy_win_event_sink *sink; IRawElementProviderSimple *elprov; + struct uia_node *node_data; HUIANODE node; HRESULT hr; + int i;
if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd)) { @@ -592,10 +706,23 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (FAILED(hr)) return hr;
- FIXME("IProxyProviderWinEventHandler usage is currently unimplemented.\n"); + hr = create_proxy_win_event_sink(&sink, event->event_id); + if (SUCCEEDED(hr)) + { + node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + for (i = 0; i < node_data->prov_count; i++) + { + hr = respond_to_win_event_on_node_provider((IWineUiaNode *)node, i, win_event->event_id, win_event->hwnd, win_event->obj_id, + win_event->child_id, &sink->IProxyProviderWinEventSink_iface); + if (FAILED(hr)) + break; + } + + IProxyProviderWinEventSink_Release(&sink->IProxyProviderWinEventSink_iface); + }
UiaNodeRelease(node); - return S_OK; + return hr; }
static void uia_event_thread_process_queue(struct list *event_queue) diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index abd03ce308e..8a750bf3ae1 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -22,6 +22,7 @@ #include "uia_classes.h" #include "wine/list.h" #include "wine/rbtree.h" +#include "assert.h"
extern HMODULE huia_module DECLSPEC_HIDDEN;
@@ -210,6 +211,8 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c
/* uia_client.c */ int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN; +HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD win_event, HWND hwnd, LONG obj_id, + LONG child_id, IProxyProviderWinEventSink *sink) DECLSPEC_HIDDEN; HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPEC_HIDDEN; HRESULT clone_uia_node(HUIANODE in_node, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) DECLSPEC_HIDDEN;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 31 +++----- dlls/uiautomationcore/uia_event.c | 91 ++++++++++++++++++---- 2 files changed, 89 insertions(+), 33 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index d1e276b0b00..160c7e0f47a 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -1766,15 +1766,6 @@ static struct prov_method_sequence *sequence; { prov , FRAG_NAVIGATE }, /* NavigateDirection_Parent */ \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \
-#define NODE_CREATE_SEQ_TODO(prov) \ - { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, \ - /* Win10v1507 and below call this. */ \ - { prov , PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ \ - { prov , PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_TODO }, \ - { prov , PROV_GET_PROPERTY_VALUE, METHOD_TODO }, \ - { prov , FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ \ - { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL } \ - #define NODE_CREATE_SEQ_OPTIONAL(prov) \ { prov , PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, \ /* Win10v1507 and below call this. */ \ @@ -16727,9 +16718,9 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, - NODE_CREATE_SEQ_TODO(&Provider_child), - { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, - { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ + NODE_CREATE_SEQ(&Provider_child), + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_ProviderDescriptionPropertyId */ { 0 } };
@@ -16862,7 +16853,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para method_sequences_enabled = TRUE; set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); ok_method_sequence(win_event_handler_seq, "win_event_handler_seq"); check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE); method_sequences_enabled = FALSE; @@ -16909,7 +16900,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(0, 1, 1, 2, 0); SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 4); /* Only sent 4 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE); CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot);
@@ -16955,7 +16946,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para */ set_uia_hwnd_expects(1, 1, 1, 4, 3); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 1, FALSE);
/* Raise a WinEvent on our test child HWND, both event callbacks invoked. */ @@ -16965,7 +16956,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(0, 2, 2, 4, 0); SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 8); /* Only sent 8 times on Win11. */ test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, - ARRAY_SIZE(event_handles), TRUE, TRUE, TRUE); + ARRAY_SIZE(event_handles), TRUE, TRUE, FALSE); CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 2, TRUE, 0, FALSE);
@@ -17008,7 +16999,7 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para UIA_AutomationFocusChangedEventId); set_uia_hwnd_expects(0, 1, 1, 0, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE);
/* @@ -17049,14 +17040,14 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para /* WinEvent handled. */ set_uia_hwnd_expects(1, 1, 1, 2, 1); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects(1, TRUE, 1, FALSE, 1, FALSE, 2, FALSE, 1, FALSE);
/* Child HWNDs of our test window are handled as well. */ SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); set_uia_hwnd_expects(0, 1, 1, 1, 0); test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE);
hr = UiaRemoveEvent(event); @@ -17094,7 +17085,7 @@ skip_win_event_hwnd_filter_test: /* WinEvent handled by default MSAA proxy provider. */ set_uia_hwnd_expects(1, 1, 1, 4, 5); test_uia_event_win_event_mapping(EVENT_SYSTEM_ALERT, hwnd[0], OBJID_CLIENT, 2, event_handles, - 1, TRUE, FALSE, TRUE); + 1, TRUE, FALSE, FALSE); check_uia_hwnd_expects_at_most(1, 1, 1, 4, 5);
hr = UiaRemoveEvent(event); diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 4e28d99ac49..f2516490f3d 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -322,6 +322,32 @@ static void uia_event_args_release(struct uia_event_args *args) free(args); }
+struct event_sink_event +{ + struct list event_sink_list_entry; + + IRawElementProviderSimple *elprov; + struct uia_event_args *args; +}; + +static HRESULT uia_event_sink_list_add_event(struct list *sink_events, IRawElementProviderSimple *elprov, + struct uia_event_args *args) +{ + struct event_sink_event *sink_event = calloc(1, sizeof(*sink_event)); + + if (!sink_event) + return E_OUTOFMEMORY; + + IRawElementProviderSimple_AddRef(elprov); + InterlockedIncrement(&args->ref); + + sink_event->elprov = elprov; + sink_event->args = args; + list_add_tail(sink_events, &sink_event->event_sink_list_entry); + + return S_OK; +} + /* * IProxyProviderWinEventSink interface implementation. */ @@ -331,6 +357,8 @@ struct uia_proxy_win_event_sink {
int event_id; IUnknown *marshal; + LONG sink_defunct; + struct list sink_events; };
static inline struct uia_proxy_win_event_sink *impl_from_IProxyProviderWinEventSink(IProxyProviderWinEventSink *iface) @@ -373,6 +401,7 @@ static ULONG WINAPI uia_proxy_win_event_sink_Release(IProxyProviderWinEventSink
if (!ref) { + assert(list_empty(&sink->sink_events)); IUnknown_Release(sink->marshal); free(sink); } @@ -390,8 +419,23 @@ static HRESULT WINAPI uia_proxy_win_event_sink_AddAutomationPropertyChangedEvent static HRESULT WINAPI uia_proxy_win_event_sink_AddAutomationEvent(IProxyProviderWinEventSink *iface, IRawElementProviderSimple *elprov, EVENTID event_id) { - FIXME("%p, %p, %d: stub\n", iface, elprov, event_id); - return S_OK; + struct uia_proxy_win_event_sink *sink = impl_from_IProxyProviderWinEventSink(iface); + struct uia_event_args *args; + HRESULT hr = S_OK; + + TRACE("%p, %p, %d\n", iface, elprov, event_id); + + if (event_id != sink->event_id) + return S_OK; + + args = create_uia_event_args(uia_event_info_from_id(event_id)); + if (!args) + return E_OUTOFMEMORY; + + if (InterlockedCompareExchange(&sink->sink_defunct, 0, 0) == 0) + hr = uia_event_sink_list_add_event(&sink->sink_events, elprov, args); + uia_event_args_release(args); + return hr; }
static HRESULT WINAPI uia_proxy_win_event_sink_AddStructureChangedEvent(IProxyProviderWinEventSink *iface, @@ -422,6 +466,7 @@ static HRESULT create_proxy_win_event_sink(struct uia_proxy_win_event_sink **out sink->IProxyProviderWinEventSink_iface.lpVtbl = &uia_proxy_event_sink_vtbl; sink->ref = 1; sink->event_id = event_id; + list_init(&sink->sink_events);
hr = CoCreateFreeThreadedMarshaler((IUnknown *)&sink->IProxyProviderWinEventSink_iface, &sink->marshal); if (FAILED(hr)) @@ -667,9 +712,21 @@ static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawEl return S_OK; }
+struct uia_elprov_event_data +{ + IRawElementProviderSimple *elprov; + struct uia_event_args *args; + BOOL clientside_only; + + SAFEARRAY *rt_id; + HUIANODE node; +}; + +static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *data); static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) { struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + struct event_sink_event *sink_event, *sink_event2; struct uia_proxy_win_event_sink *sink; IRawElementProviderSimple *elprov; struct uia_node *node_data; @@ -714,10 +771,28 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da { hr = respond_to_win_event_on_node_provider((IWineUiaNode *)node, i, win_event->event_id, win_event->hwnd, win_event->obj_id, win_event->child_id, &sink->IProxyProviderWinEventSink_iface); - if (FAILED(hr)) + if (FAILED(hr) || !list_empty(&sink->sink_events)) break; }
+ InterlockedIncrement(&sink->sink_defunct); + LIST_FOR_EACH_ENTRY_SAFE(sink_event, sink_event2, &sink->sink_events, struct event_sink_event, event_sink_list_entry) + { + struct uia_elprov_event_data event_data = { sink_event->elprov, sink_event->args, TRUE }; + list_remove(&sink_event->event_sink_list_entry); + + hr = uia_raise_elprov_event_callback(event, (void *)&event_data); + if (FAILED(hr)) + WARN("uia_raise_elprov_event_callback failed with hr %#lx\n", hr); + + UiaNodeRelease(event_data.node); + SafeArrayDestroy(event_data.rt_id); + + IRawElementProviderSimple_Release(sink_event->elprov); + uia_event_args_release(sink_event->args); + free(sink_event); + } + IProxyProviderWinEventSink_Release(&sink->IProxyProviderWinEventSink_iface); }
@@ -1793,16 +1868,6 @@ static HRESULT uia_event_check_match(HUIANODE node, HUIANODE nav_start_node, SAF return hr; }
-struct uia_elprov_event_data -{ - IRawElementProviderSimple *elprov; - struct uia_event_args *args; - BOOL clientside_only; - - SAFEARRAY *rt_id; - HUIANODE node; -}; - static HRESULT uia_raise_elprov_event_callback(struct uia_event *event, void *data) { struct uia_elprov_event_data *event_data = (struct uia_elprov_event_data *)data;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 160c7e0f47a..012fda27738 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -16960,6 +16960,29 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 2, TRUE, 0, FALSE);
+ /* + * Raise a WinEvent on a child of our test child HWND. For event2, this + * is within scope because its direct parent HWND is within scope. For + * event, it is within scope because its root HWND is within scope. + */ + tmp_hwnd = CreateWindowA("ProxyProviderWinEventHandler test child class", "Test child window 2", WS_CHILD, + 0, 0, 50, 50, hwnd[1], NULL, NULL, NULL); + set_provider_win_event_handler_win_event_expects(&Provider_nc3, EVENT_OBJECT_FOCUS, tmp_hwnd, OBJID_WINDOW, CHILDID_SELF); + set_provider_win_event_handler_win_event_expects(&Provider_hwnd3, EVENT_OBJECT_FOCUS, tmp_hwnd, OBJID_WINDOW, CHILDID_SELF); + Provider_nc3.hwnd = Provider_hwnd3.hwnd = tmp_hwnd; + + set_uia_hwnd_expects(0, 2, 2, 0, 0); + SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 12); /* Only sent 12 times on Win11. */ + test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, tmp_hwnd, OBJID_WINDOW, CHILDID_SELF, event_handles, + ARRAY_SIZE(event_handles), TRUE, TRUE, FALSE); + CHECK_CALLED_AT_LEAST(child_winproc_GETOBJECT_UiaRoot, 2); + check_uia_hwnd_expects_at_least(0, FALSE, 2, FALSE, 2, FALSE, 0, FALSE, 0, FALSE); + + DestroyWindow(tmp_hwnd); + Provider_nc3.hwnd = Provider_hwnd3.hwnd = hwnd[1]; + set_provider_win_event_handler_win_event_expects(&Provider_nc3, EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF); + set_provider_win_event_handler_win_event_expects(&Provider_hwnd3, EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF); + hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr);
On Tue Sep 26 18:24:06 2023 +0000, Esme Povirk wrote:
Is this correct even if the parent is also not a top-level HWND?
I added a new test commit for this, which also exposed another case I didn't handle. But yes, for non top-level HWNDs, it checks if either the direct parent is in the map, or if the root HWND is.
On Wed Sep 27 18:55:01 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/3956/diffs?diff_id=72318&start_sha=5abb569c4438f4069c5bb17a686483cdc1badcc3#14d9c4bde8659a60721a64739d545cd66adf72c2_645_790)
Added a check in the `AddAutomationEvent` method to avoid adding events that we aren't interested in.
On Wed Sep 27 18:55:01 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/3956/diffs?diff_id=72318&start_sha=5abb569c4438f4069c5bb17a686483cdc1badcc3#14d9c4bde8659a60721a64739d545cd66adf72c2_1080_1199)
I made the object dynamically allocated now, so this shouldn't be a concern anymore.
On Wed Sep 27 18:55:02 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/3956/diffs?diff_id=72318&start_sha=5abb569c4438f4069c5bb17a686483cdc1badcc3#14d9c4bde8659a60721a64739d545cd66adf72c2_1110_1199)
Moved the events list into the actual sink object itself, so this shouldn't be a problem anymore either.
On Fri Sep 29 16:58:50 2023 +0000, Connor McAdams wrote:
I added a new test commit for this, which also exposed another case I didn't handle. But yes, for non top-level HWNDs, it checks if either the direct parent is in the map, or if the root HWND is.
What about an ancestor HWND that's neither the immediate parent nor the root?
On Fri Sep 29 17:00:25 2023 +0000, Connor McAdams wrote:
Added a check in the `AddAutomationEvent` method to avoid adding events that we aren't interested in.
The check being elsewhere doesn't change the behavior, and the behavior is what I'm unsure about. Does an IProxyProviderWinEventSink instance really only allow raising a single eventid?
On Fri Sep 29 17:00:25 2023 +0000, Esme Povirk wrote:
The check being elsewhere doesn't change the behavior, and the behavior is what I'm unsure about. Does an IProxyProviderWinEventSink instance really only allow raising a single eventid?
You can pass another event ID, but whatever you pass to the sink has to match the current HUIAEVENT being evaluated. When a WinEvent is received, i.e `EVENT_OBJECT_FOCUS`, each registered `UIA_AutomationFocusChangedEventId` HUIAEVENT passes an `IProxyProviderWinEventSink` to the `IProxyProviderWinEventHandler` on the node. The event passed to the sink is then evaluated to check if it matches the HUIAEVENT. Passing a different event ID is an immediate 'non-match' condition.
On Fri Sep 29 17:09:34 2023 +0000, Connor McAdams wrote:
You can pass another event ID, but whatever you pass to the sink has to match the current HUIAEVENT being evaluated. When a WinEvent is received, i.e `EVENT_OBJECT_FOCUS`, each registered `UIA_AutomationFocusChangedEventId` HUIAEVENT passes an `IProxyProviderWinEventSink` to the `IProxyProviderWinEventHandler` on the node. The event passed to the sink is then evaluated to check if it matches the HUIAEVENT. Passing a different event ID is an immediate 'non-match' condition.
I think in theory here, you're able to have a single WinEvent mapped to multiple UIA events. And in that case, you'd have a unified `RespondToWinEvent()` implementation that just added all events you intend to raise for the translation to the sink each time, and if one matches the event currently being evaluated it proceeds.
https://learn.microsoft.com/en-us/windows/win32/api/uiautomationclient/nf-ui...