uiautomationcore/tests: Add tests for IProxyProviderWinEventHandler based MSAA to UIA event bridging.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com
-- v2: uiautomationcore: Make sure event thread is started when listening for WinEvents. uiautomationcore: Track HWNDs that need WinEvent translation. uiautomationcore: Add WinEvent hook on the UIA event thread. uiautomationcore: Check providers for an IProxyProviderWinEventHandler interface when attaching events. uiautomationcore/tests: Add tests for IProxyProviderWinEventHandler based MSAA to UIA event bridging.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 673 ++++++++++++++++++++- 1 file changed, 654 insertions(+), 19 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 842525b78fa..89757e2eb84 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -391,6 +391,7 @@ DEFINE_EXPECT(prov_callback_nonclient); DEFINE_EXPECT(prov_callback_proxy); DEFINE_EXPECT(prov_callback_parent_proxy); DEFINE_EXPECT(uia_event_callback); +DEFINE_EXPECT(uia_event_callback2); DEFINE_EXPECT(uia_com_event_callback); DEFINE_EXPECT(winproc_GETOBJECT_UiaRoot); DEFINE_EXPECT(child_winproc_GETOBJECT_UiaRoot); @@ -493,6 +494,34 @@ static void test_implements_interface_(IUnknown *unk, const GUID *iid, BOOL exp_ DEFINE_ACC_METHOD_EXPECT(get_locale); \ DEFINE_ACC_METHOD_EXPECT(get_attributes) \
+static DWORD msg_wait_for_all_events(HANDLE *event_handles, int event_handle_count, DWORD timeout_val) +{ + int events_handled = 0; + DWORD wait_res; + + while ((wait_res = MsgWaitForMultipleObjects(event_handle_count, (const HANDLE *)event_handles, FALSE, timeout_val, + QS_ALLINPUT)) <= (WAIT_OBJECT_0 + event_handle_count)) + { + if (wait_res == (WAIT_OBJECT_0 + event_handle_count)) + { + MSG msg; + + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + else + events_handled++; + + if (events_handled == event_handle_count) + break; + } + + return wait_res; +} + static struct Accessible { IAccessible IAccessible_iface; @@ -1555,6 +1584,19 @@ struct Provider_legacy_accessible_pattern_data DWORD role; };
+struct Provider_win_event_handler_data +{ + BOOL is_supported; + + DWORD exp_win_event_id; + HWND exp_win_event_hwnd; + LONG exp_win_event_obj_id; + LONG exp_win_event_child_id; + + IRawElementProviderSimple *responder_prov; + int responder_event; +}; + static struct Provider { IRawElementProviderSimple IRawElementProviderSimple_iface; @@ -1562,6 +1604,7 @@ static struct Provider IRawElementProviderFragmentRoot IRawElementProviderFragmentRoot_iface; IRawElementProviderHwndOverride IRawElementProviderHwndOverride_iface; IRawElementProviderAdviseEvents IRawElementProviderAdviseEvents_iface; + IProxyProviderWinEventHandler IProxyProviderWinEventHandler_iface; IValueProvider IValueProvider_iface; ILegacyIAccessibleProvider ILegacyIAccessibleProvider_iface; LONG ref; @@ -1591,6 +1634,7 @@ static struct Provider int embedded_frag_roots_count; int advise_events_added_event_id; int advise_events_removed_event_id; + struct Provider_win_event_handler_data win_event_handler_data; } Provider, Provider2, Provider_child, Provider_child2; static struct Provider Provider_hwnd, Provider_nc, Provider_proxy, Provider_proxy2, Provider_override; static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links); @@ -1662,6 +1706,7 @@ enum { HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, ADVISE_EVENTS_EVENT_ADDED, ADVISE_EVENTS_EVENT_REMOVED, + WINEVENT_HANDLER_RESPOND_TO_WINEVENT, };
static const char *prov_method_str[] = { @@ -1678,6 +1723,7 @@ static const char *prov_method_str[] = { "GetOverrideProviderForHwnd", "AdviseEventAdded", "AdviseEventRemoved", + "RespondToWinEvent", };
static const char *get_prov_method_str(int method) @@ -2061,6 +2107,8 @@ HRESULT WINAPI ProviderSimple_QueryInterface(IRawElementProviderSimple *iface, R *ppv = &This->IValueProvider_iface; else if (IsEqualIID(riid, &IID_ILegacyIAccessibleProvider)) *ppv = &This->ILegacyIAccessibleProvider_iface; + else if (This->win_event_handler_data.is_supported && IsEqualIID(riid, &IID_IProxyProviderWinEventHandler)) + *ppv = &This->IProxyProviderWinEventHandler_iface; else return E_NOINTERFACE;
@@ -2723,6 +2771,64 @@ static const IRawElementProviderAdviseEventsVtbl ProviderAdviseEventsVtbl = { ProviderAdviseEvents_AdviseEventRemoved, };
+static inline struct Provider *impl_from_ProviderWinEventHandler(IProxyProviderWinEventHandler *iface) +{ + return CONTAINING_RECORD(iface, struct Provider, IProxyProviderWinEventHandler_iface); +} + +static HRESULT WINAPI ProviderWinEventHandler_QueryInterface(IProxyProviderWinEventHandler *iface, REFIID riid, + void **ppv) +{ + struct Provider *Provider = impl_from_ProviderWinEventHandler(iface); + return IRawElementProviderSimple_QueryInterface(&Provider->IRawElementProviderSimple_iface, riid, ppv); +} + +static ULONG WINAPI ProviderWinEventHandler_AddRef(IProxyProviderWinEventHandler *iface) +{ + struct Provider *Provider = impl_from_ProviderWinEventHandler(iface); + return IRawElementProviderSimple_AddRef(&Provider->IRawElementProviderSimple_iface); +} + +static ULONG WINAPI ProviderWinEventHandler_Release(IProxyProviderWinEventHandler *iface) +{ + struct Provider *Provider = impl_from_ProviderWinEventHandler(iface); + return IRawElementProviderSimple_Release(&Provider->IRawElementProviderSimple_iface); +} + +static HRESULT WINAPI ProviderWinEventHandler_RespondToWinEvent(IProxyProviderWinEventHandler *iface, + DWORD event_id, HWND hwnd, LONG obj_id, LONG child_id, IProxyProviderWinEventSink *event_sink) +{ + struct Provider *This = impl_from_ProviderWinEventHandler(iface); + struct Provider_win_event_handler_data *data; + HRESULT hr; + + PROV_METHOD_TRACE(This, RespondToWinEvent); + data = &This->win_event_handler_data; + if ((data->exp_win_event_id != event_id) || (data->exp_win_event_hwnd != hwnd) || (data->exp_win_event_obj_id != obj_id) || + (data->exp_win_event_child_id != child_id)) + return S_OK; + + add_method_call(This, WINEVENT_HANDLER_RESPOND_TO_WINEVENT); + if (This->expected_tid) + ok(This->expected_tid == GetCurrentThreadId(), "Unexpected tid %ld\n", GetCurrentThreadId()); + This->last_call_tid = GetCurrentThreadId(); + + if (data->responder_prov) + { + hr = IProxyProviderWinEventSink_AddAutomationEvent(event_sink, data->responder_prov, data->responder_event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + return S_OK; +} + +static const IProxyProviderWinEventHandlerVtbl ProviderWinEventHandlerVtbl = { + ProviderWinEventHandler_QueryInterface, + ProviderWinEventHandler_AddRef, + ProviderWinEventHandler_Release, + ProviderWinEventHandler_RespondToWinEvent, +}; + static inline struct Provider *impl_from_ProviderValuePattern(IValueProvider *iface) { return CONTAINING_RECORD(iface, struct Provider, IValueProvider_iface); @@ -2921,6 +3027,7 @@ static struct Provider Provider = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2938,6 +3045,7 @@ static struct Provider Provider2 = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2955,6 +3063,7 @@ static struct Provider Provider_child = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2972,6 +3081,7 @@ static struct Provider Provider_child2 = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -2989,6 +3099,7 @@ static struct Provider Provider_hwnd = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -3006,6 +3117,7 @@ static struct Provider Provider_nc = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -3024,6 +3136,7 @@ static struct Provider Provider_proxy = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -3042,6 +3155,7 @@ static struct Provider Provider_proxy2 = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -3060,6 +3174,7 @@ static struct Provider Provider_override = { &ProviderFragmentRootVtbl }, { &ProviderHwndOverrideVtbl }, { &ProviderAdviseEventsVtbl }, + { &ProviderWinEventHandlerVtbl }, { &ProviderValuePatternVtbl }, { &ProviderLegacyIAccessiblePatternVtbl }, 1, @@ -3079,6 +3194,7 @@ static struct Provider Provider_override = { &ProviderFragmentRootVtbl }, \ { &ProviderHwndOverrideVtbl }, \ { &ProviderAdviseEventsVtbl }, \ + { &ProviderWinEventHandlerVtbl }, \ { &ProviderValuePatternVtbl }, \ { &ProviderLegacyIAccessiblePatternVtbl }, \ 1, \ @@ -10793,6 +10909,37 @@ static void set_property_override(struct Provider_prop_override *override, int p override->val = *val; }
+static void set_provider_win_event_handler_respond_prov(struct Provider *prov, IRawElementProviderSimple *responder_prov, + int responder_event) +{ + struct Provider_win_event_handler_data *data = &prov->win_event_handler_data; + + data->responder_prov = responder_prov; + data->responder_event = responder_event; +} + +static void set_provider_win_event_handler_win_event_expects(struct Provider *prov, DWORD exp_win_event_id, + HWND exp_win_event_hwnd, LONG exp_win_event_obj_id, LONG exp_win_event_child_id) +{ + struct Provider_win_event_handler_data *data = &prov->win_event_handler_data; + + data->exp_win_event_id = exp_win_event_id; + data->exp_win_event_hwnd = exp_win_event_hwnd; + data->exp_win_event_obj_id = exp_win_event_obj_id; + data->exp_win_event_child_id = exp_win_event_child_id; +} + +static void set_provider_runtime_id(struct Provider *prov, int val, int val2) +{ + prov->runtime_id[0] = val; + prov->runtime_id[1] = val2; +} + +static void initialize_provider_advise_events_ids(struct Provider *prov) +{ + prov->advise_events_added_event_id = prov->advise_events_removed_event_id = 0; +} + static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, BOOL initialize_nav_links) { prov->prov_opts = prov_opts; @@ -10812,6 +10959,7 @@ static void initialize_provider(struct Provider *prov, int prov_opts, HWND hwnd, prov->embedded_frag_roots = NULL; prov->embedded_frag_roots_count = 0; prov->advise_events_added_event_id = prov->advise_events_removed_event_id = 0; + memset(&prov->win_event_handler_data, 0, sizeof(prov->win_event_handler_data)); if (initialize_nav_links) { prov->frag_root = NULL; @@ -11875,6 +12023,14 @@ static HWND create_child_test_hwnd(const char *class_name, HWND parent) 0, 0, 50, 50, parent, NULL, NULL, NULL); }
+static void destroy_test_hwnd(HWND hwnd, const char *class_name, const char *child_class_name) +{ + DestroyWindow(hwnd); + UnregisterClassA(class_name, NULL); + if (child_class_name) + UnregisterClassA(child_class_name, NULL); +} + static IUIAutomationElement *create_test_element_from_hwnd(IUIAutomation *uia_iface, HWND hwnd, BOOL block_hwnd_provs) { IUIAutomationElement *element; @@ -14164,6 +14320,17 @@ static void check_uia_hwnd_expects_at_most(int proxy_cback_count, int base_hwnd_ CHECK_CALLED_AT_MOST(winproc_GETOBJECT_CLIENT, win_get_client_obj_count); }
+static void check_uia_hwnd_expects_at_least(int proxy_cback_count, BOOL proxy_cback_todo, + int base_hwnd_cback_count, BOOL base_hwnd_cback_todo, int nc_cback_count, BOOL nc_cback_todo, + int win_get_uia_obj_count, BOOL win_get_uia_obj_todo, int win_get_client_obj_count, BOOL win_get_client_obj_todo) +{ + todo_wine_if(proxy_cback_todo) CHECK_CALLED_AT_LEAST(prov_callback_proxy, proxy_cback_count); + todo_wine_if(base_hwnd_cback_todo) CHECK_CALLED_AT_LEAST(prov_callback_base_hwnd, base_hwnd_cback_count); + todo_wine_if(nc_cback_todo) CHECK_CALLED_AT_LEAST(prov_callback_nonclient, nc_cback_count); + todo_wine_if(win_get_uia_obj_todo) CHECK_CALLED_AT_LEAST(winproc_GETOBJECT_UiaRoot, win_get_uia_obj_count); + todo_wine_if(win_get_client_obj_todo) CHECK_CALLED_AT_LEAST(winproc_GETOBJECT_CLIENT, win_get_client_obj_count); +} + static struct ComEventData { struct node_provider_desc exp_node_desc; struct node_provider_desc exp_nested_node_desc; @@ -15551,47 +15718,67 @@ static struct EventData {
struct node_provider_desc exp_nested_node_desc; HANDLE event_handle; -} EventData; +} EventData, EventData2;
-static void set_event_data(LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, LONG exp_elems1, - struct node_provider_desc *exp_node_desc, const WCHAR *exp_tree_struct) +static void set_event_data_struct(struct EventData *data, LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, + LONG exp_elems1, struct node_provider_desc *exp_node_desc, const WCHAR *exp_tree_struct) { - EventData.exp_lbound[0] = exp_lbound0; - EventData.exp_lbound[1] = exp_lbound1; - EventData.exp_elems[0] = exp_elems0; - EventData.exp_elems[1] = exp_elems1; + data->exp_lbound[0] = exp_lbound0; + data->exp_lbound[1] = exp_lbound1; + data->exp_elems[0] = exp_elems0; + data->exp_elems[1] = exp_elems1; if (exp_node_desc) { int i;
- EventData.exp_node_desc = *exp_node_desc; + data->exp_node_desc = *exp_node_desc; for (i = 0; i < exp_node_desc->prov_count; i++) { if (exp_node_desc->nested_desc[i]) { - EventData.exp_nested_node_desc = *exp_node_desc->nested_desc[i]; - EventData.exp_node_desc.nested_desc[i] = &EventData.exp_nested_node_desc; + data->exp_nested_node_desc = *exp_node_desc->nested_desc[i]; + data->exp_node_desc.nested_desc[i] = &data->exp_nested_node_desc; break; } } } else - memset(&EventData.exp_node_desc, 0, sizeof(EventData.exp_node_desc)); - EventData.exp_tree_struct = exp_tree_struct; + memset(&data->exp_node_desc, 0, sizeof(data->exp_node_desc)); + data->exp_tree_struct = exp_tree_struct; }
-static void WINAPI uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct) +static void set_event_data(LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, LONG exp_elems1, + struct node_provider_desc *exp_node_desc, const WCHAR *exp_tree_struct) { - CHECK_EXPECT(uia_event_callback); + set_event_data_struct(&EventData, exp_lbound0, exp_lbound1, exp_elems0, exp_elems1, exp_node_desc, + exp_tree_struct); +}
- if (!EventData.exp_elems[0] && !EventData.exp_elems[1]) +#define check_event_data( data, args, req_data, tree_struct ) \ + check_event_data_( (data), (args), (req_data), (tree_struct), __FILE__, __LINE__) +static void check_event_data_(struct EventData *data, struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct, + const char *file, int line) +{ + if (!data->exp_elems[0] && !data->exp_elems[1]) ok(!req_data, "req_data != NULL\n"); else - test_cache_req_sa(req_data, EventData.exp_lbound, EventData.exp_elems, &EventData.exp_node_desc); + test_cache_req_sa_(req_data, data->exp_lbound, data->exp_elems, &data->exp_node_desc, file, line);
- ok(!wcscmp(tree_struct, EventData.exp_tree_struct), "tree structure %s\n", debugstr_w(tree_struct)); - if (EventData.event_handle) - SetEvent(EventData.event_handle); + ok(!wcscmp(tree_struct, data->exp_tree_struct), "tree structure %s\n", debugstr_w(tree_struct)); + if (data->event_handle) + SetEvent(data->event_handle); +} + +static void WINAPI uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct) +{ + CHECK_EXPECT(uia_event_callback); + check_event_data(&EventData, args, req_data, tree_struct); +} + +static void WINAPI uia_event_callback2(struct UiaEventArgs *args, SAFEARRAY *req_data, BSTR tree_struct) +{ + CHECK_EXPECT(uia_event_callback2); + check_event_data(&EventData2, args, req_data, tree_struct); }
enum { @@ -16516,6 +16703,453 @@ static void test_UiaHasServerSideProvider(void) UnregisterClassA("UiaHasServerSideProvider test class", NULL); }
+static const struct prov_method_sequence win_event_handler_seq[] = { + { &Provider_proxy, HWND_OVERRIDE_GET_OVERRIDE_PROVIDER, METHOD_TODO }, + { &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 */ + { 0 } +}; + +#define test_uia_event_win_event_mapping( win_event, hwnd, obj_id, child_id, event_handles, event_handle_count, \ + expect_event1, expect_event2, todo ) \ + test_uia_event_win_event_mapping_( (win_event), (hwnd), (obj_id), (child_id), (event_handles), (event_handle_count), \ + (expect_event1), (expect_event2), (todo), __FILE__, __LINE__) +static void test_uia_event_win_event_mapping_(DWORD win_event, HWND hwnd, LONG obj_id, LONG child_id, + HANDLE *event_handles, int event_handle_count, BOOL expect_event1, BOOL expect_event2, + BOOL todo, const char *file, int line) +{ + const BOOL exp_timeout = (!expect_event1 && !expect_event2); + DWORD timeout_val = exp_timeout ? 500 : 3000; + DWORD wait_res; + + SET_EXPECT_MULTI(uia_event_callback, !!expect_event1); + SET_EXPECT_MULTI(uia_event_callback2, !!expect_event2); + if (expect_event2) + SET_EXPECT(uia_event_callback2); + NotifyWinEvent(win_event, hwnd, obj_id, child_id); + + wait_res = msg_wait_for_all_events(event_handles, event_handle_count, timeout_val); + todo_wine_if(todo) ok_(file, line)((wait_res == WAIT_TIMEOUT) == exp_timeout, + "Unexpected result while waiting for event callback(s).\n"); + if (expect_event1) + todo_wine_if(todo) CHECK_CALLED(uia_event_callback); + if (expect_event2) + todo_wine_if(todo) CHECK_CALLED(uia_event_callback2); +} + +#define test_provider_event_advise_added( prov, event_id, todo) \ + test_provider_event_advise_added_( (prov), (event_id), (todo), __FILE__, __LINE__) +static void test_provider_event_advise_added_(struct Provider *prov, int event_id, BOOL todo, const char *file, int line) +{ + todo_wine_if (todo) ok_(file, line)(prov->advise_events_added_event_id == event_id, "%s: Unexpected advise event added, event ID %d.\n", + prov->prov_name, prov->advise_events_added_event_id); +} + +static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID param) +{ + struct UiaCacheRequest cache_req = { (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full }; + HWND hwnd[2] = { ((HWND *)param)[0], ((HWND *)param)[1] }; + struct node_provider_desc exp_node_desc; + HUIAEVENT event, event2; + HANDLE event_handles[2]; + HWND tmp_hwnd; + HUIANODE node; + HRESULT hr; + int i; + + method_sequences_enabled = FALSE; + CoInitializeEx(NULL, COINIT_MULTITHREADED); + for (i = 0; i < ARRAY_SIZE(event_handles); i++) + event_handles[i] = CreateEventW(NULL, FALSE, FALSE, NULL); + + set_uia_hwnd_expects(1, 1, 1, 2, 0); + hr = UiaNodeFromHandle(hwnd[0], &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!node, "Node == NULL.\n"); + check_uia_hwnd_expects_at_most(1, 1, 1, 2, 0); + + set_uia_hwnd_expects(2, 2, 2, 4, 0); + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* Windows 11 recreates HWND clientside providers for the node passed into UiaAddEvent. */ + check_uia_hwnd_expects_at_most(2, 2, 2, 4, 0); + + set_uia_hwnd_expects(2, 2, 2, 4, 0); + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback2, TreeScope_Subtree, NULL, 0, &cache_req, + &event2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + /* Windows 11 recreates HWND clientside providers for the node passed into UiaAddEvent. */ + check_uia_hwnd_expects_at_most(2, 2, 2, 4, 0); + UiaNodeRelease(node); + + /* + * Raise EVENT_OBJECT_FOCUS. If none of our clientside providers returned + * an IProxyProviderWinEventHandler interface when being advised of events + * being listened for, nothing happens. + */ + EventData.event_handle = event_handles[0]; + EventData2.event_handle = event_handles[1]; + test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, + ARRAY_SIZE(event_handles), FALSE, FALSE, FALSE); + + /* + * Return an IProxyProviderWinEventHandler interface on our clientside + * providers. WinEvents will now be listened for. If a provider returns a + * WinEvent handler interface, IRawElementProviderAdviseEvents will not be + * queried for or used. + */ + initialize_provider_advise_events_ids(&Provider_hwnd2); + initialize_provider_advise_events_ids(&Provider_nc2); + Provider_hwnd2.win_event_handler_data.is_supported = Provider_nc2.win_event_handler_data.is_supported = TRUE; + set_uia_hwnd_expects(1, 1, 1, 2, 0); + hr = UiaEventAddWindow(event, hwnd[0]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_provider_event_advise_added(&Provider_hwnd2, 0, TRUE); + test_provider_event_advise_added(&Provider_nc2, 0, TRUE); + check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE); + + /* + * WinEvents will now be listened for, however if our HWND has a + * serverside provider they will be ignored. + */ + 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); + + /* + * Get rid of our serverside provider and raise EVENT_OBJECT_FOCUS + * again. Now, our WinEvent handler interfaces will be invoked. + */ + prov_root = NULL; + set_provider_win_event_handler_win_event_expects(&Provider_hwnd2, EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF); + set_provider_win_event_handler_win_event_expects(&Provider_nc2, EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF); + + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); + set_provider_runtime_id(&Provider_child, UIA_RUNTIME_ID_PREFIX, HandleToUlong(hwnd[0])); + set_provider_win_event_handler_respond_prov(&Provider_hwnd2, &Provider_child.IRawElementProviderSimple_iface, + UIA_AutomationFocusChangedEventId); + + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider_child", TRUE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + + 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); + 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); + method_sequences_enabled = FALSE; + + /* + * Not all WinEvents are passed to our WinEvent responder interface - + * they're filtered by HWND. + */ + Provider_hwnd.win_event_handler_data.is_supported = Provider_nc.win_event_handler_data.is_supported = TRUE; + set_provider_win_event_handler_win_event_expects(&Provider_nc, EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF); + set_provider_win_event_handler_respond_prov(&Provider_nc, &Provider_child.IRawElementProviderSimple_iface, + UIA_AutomationFocusChangedEventId); + SET_EXPECT(uia_event_callback); + set_uia_hwnd_expects(0, 1, 1, 0, 0); + NotifyWinEvent(EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF); + if (msg_wait_for_all_events(event_handles, 1, 3000) == WAIT_OBJECT_0) + { + win_skip("Win10v1507 and below don't filter WinEvents by HWND, skipping further tests.\n"); + + CHECK_CALLED(uia_event_callback); + check_uia_hwnd_expects(0, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE); + hr = UiaRemoveEvent(event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = UiaRemoveEvent(event2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + goto skip_win_event_hwnd_filter_test; + } + + /* Clear expects/called values. */ + CHECK_CALLED_MULTI(uia_event_callback, 0); + + /* + * Child HWNDs of top level HWNDs that are within our scope are listened + * to by default. + */ + child_win_prov_root = NULL; + Provider_hwnd3.win_event_handler_data.is_supported = Provider_nc3.win_event_handler_data.is_supported = TRUE; + 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); + set_provider_win_event_handler_respond_prov(&Provider_nc3, &Provider_child.IRawElementProviderSimple_iface, + UIA_AutomationFocusChangedEventId); + + 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); + check_uia_hwnd_expects_at_least(0, FALSE, 1, TRUE, 1, TRUE, 1, TRUE, 0, FALSE); + todo_wine CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + + /* + * Child HWND now has a serverside provider, WinEvent is ignored. + */ + child_win_prov_root = &Provider2.IRawElementProviderSimple_iface; + + SET_EXPECT(winproc_GETOBJECT_UiaRoot); /* Only sent on Win11. */ + 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_AT_MOST(winproc_GETOBJECT_UiaRoot, 1); + + /* + * HWNDs owned by a top level HWND that is within our scope are ignored. + */ + child_win_prov_root = NULL; + tmp_hwnd = CreateWindowA("ProxyProviderWinEventHandler test child class", "Test child window 2", WS_POPUP, + 0, 0, 50, 50, hwnd[0], NULL, NULL, NULL); + Provider_nc3.hwnd = Provider_hwnd3.hwnd = tmp_hwnd; + test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, tmp_hwnd, OBJID_WINDOW, CHILDID_SELF, event_handles, + 1, FALSE, FALSE, FALSE); + DestroyWindow(tmp_hwnd); + + /* + * Add our test child HWND to event2. This only puts the child HWND within + * the scope of event2, it doesn't put the parent HWND within its scope. + */ + child_win_prov_root = &Provider2.IRawElementProviderSimple_iface; + Provider_nc3.hwnd = Provider_hwnd3.hwnd = hwnd[1]; + SET_EXPECT_MULTI(child_winproc_GETOBJECT_UiaRoot, 2); /* Only sent 2 times on Win11. */ + set_uia_hwnd_expects(0, 1, 1, 1, 0); + hr = UiaEventAddWindow(event2, hwnd[1]); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + check_uia_hwnd_expects_at_least(0, FALSE, 1, FALSE, 1, FALSE, 1, TRUE, 0, FALSE); + CHECK_CALLED(child_winproc_GETOBJECT_UiaRoot); + + /* + * Raise a WinEvent on our top level test HWND, will not invoke the + * callback on event2. + */ + 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); + + /* Raise a WinEvent on our test child HWND, both event callbacks invoked. */ + child_win_prov_root = NULL; + set_event_data_struct(&EventData2, 0, 0, 1, 1, &exp_node_desc, L"P)"); + + 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); + todo_wine 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = UiaRemoveEvent(event2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* + * Create an event on the desktop HWND. If a WinEvent handler interface is + * returned on a provider representing the desktop HWND, all visible + * top-level HWNDs at the time of advisement will be considered within + * scope. + */ + set_uia_hwnd_expects(1, 1, 1, 0, 0); + hr = UiaGetRootNode(&node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!node, "Node == NULL.\n"); + check_uia_hwnd_expects(1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE, 0, FALSE); + + Provider_proxy.win_event_handler_data.is_supported = TRUE; + Provider_hwnd.win_event_handler_data.is_supported = Provider_nc.win_event_handler_data.is_supported = TRUE; + set_provider_win_event_handler_win_event_expects(&Provider_nc, EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF); + set_provider_win_event_handler_win_event_expects(&Provider_hwnd, EVENT_OBJECT_FOCUS, GetDesktopWindow(), OBJID_WINDOW, CHILDID_SELF); + set_provider_win_event_handler_respond_prov(&Provider_nc, &Provider_child.IRawElementProviderSimple_iface, + UIA_AutomationFocusChangedEventId); + + /* Register a focus change event handler on the desktop HWND. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_provider_event_advise_added(&Provider_proxy, 0, TRUE); + test_provider_event_advise_added(&Provider_hwnd, 0, TRUE); + test_provider_event_advise_added(&Provider_nc, 0, TRUE); + + /* Raise WinEvent on the desktop HWND. */ + set_provider_runtime_id(&Provider_child, UIA_RUNTIME_ID_PREFIX, HandleToUlong(GetDesktopWindow())); + set_provider_win_event_handler_respond_prov(&Provider_hwnd, &Provider_child.IRawElementProviderSimple_iface, + 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); + check_uia_hwnd_expects(0, FALSE, 1, TRUE, 1, TRUE, 0, FALSE, 0, FALSE); + + /* + * Top-level HWND, a child of the desktop HWND. Will not have an event + * raised since it was not visible when the desktop providers were advised + * of an event being added. + */ + test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, + 1, FALSE, FALSE, FALSE); + + /* Test child hwnd, same deal. */ + test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[1], OBJID_WINDOW, CHILDID_SELF, event_handles, + 1, FALSE, FALSE, FALSE); + + /* + * Show window after calling UiaAddEvent(), does nothing. Window must be + * visible when provider is advised of an event being added. + */ + ShowWindow(hwnd[0], SW_SHOW); + test_uia_event_win_event_mapping(EVENT_OBJECT_FOCUS, hwnd[0], OBJID_WINDOW, CHILDID_SELF, event_handles, + 1, FALSE, FALSE, FALSE); + + hr = UiaRemoveEvent(event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* + * Create the event again, except this time our test HWND was visible when + * the desktop provider was advised that our event was being added. Now + * WinEvents on our test HWND will be handled. + */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_provider_event_advise_added(&Provider_hwnd, 0, TRUE); + test_provider_event_advise_added(&Provider_nc, 0, TRUE); + test_provider_event_advise_added(&Provider_proxy, 0, TRUE); + + /* 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); + check_uia_hwnd_expects(1, TRUE, 1, TRUE, 1, TRUE, 2, TRUE, 1, TRUE); + + /* 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); + + hr = UiaRemoveEvent(event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + UiaNodeRelease(node); + +skip_win_event_hwnd_filter_test: + /* + * Test default MSAA proxy WinEvent handler. + */ + prov_root = &Provider.IRawElementProviderSimple_iface; + set_uia_hwnd_expects(2, 1, 1, 2, 0); + hr = UiaNodeFromHandle(hwnd[0], &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!node, "Node == NULL.\n"); + check_uia_hwnd_expects_at_most(1, 1, 1, 2, 0); + + Provider_hwnd2.win_event_handler_data.is_supported = Provider_nc2.win_event_handler_data.is_supported = TRUE; + hr = UiaAddEvent(node, UIA_SystemAlertEventId, uia_event_callback, TreeScope_Subtree, NULL, 0, &cache_req, + &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + UiaNodeRelease(node); + + set_provider_win_event_handler_respond_prov(&Provider_hwnd2, NULL, 0); + set_provider_win_event_handler_win_event_expects(&Provider_hwnd2, 0, hwnd[0], 0, 0); + set_provider_win_event_handler_respond_prov(&Provider_nc2, NULL, 0); + set_provider_win_event_handler_win_event_expects(&Provider_nc2, 0, NULL, 0, 0); + + prov_root = NULL; + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); + add_provider_desc(&exp_node_desc, L"Main", NULL, TRUE); /* MSAA proxy. */ + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + + /* 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); + check_uia_hwnd_expects_at_most(1, 1, 1, 4, 5); + + hr = UiaRemoveEvent(event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + for (i = 0; i < ARRAY_SIZE(event_handles); i++) + CloseHandle(event_handles[i]); + method_sequences_enabled = TRUE; + CoUninitialize(); + return 0; +} + +static void test_uia_event_ProxyProviderWinEventHandler(void) +{ + HANDLE thread; + HWND hwnd[2]; + + /* + * Windows 7 behaves different than all other versions, just skip the + * tests. + */ + if (!UiaLookupId(AutomationIdentifierType_Property, &OptimizeForVisualContent_Property_GUID)) + { + win_skip("Skipping IProxyProviderWinEventSink tests for Win7\n"); + return; + } + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + hwnd[0] = create_test_hwnd("ProxyProviderWinEventHandler test class"); + hwnd[1] = create_child_test_hwnd("ProxyProviderWinEventHandler test child class", hwnd[0]); + + UiaRegisterProviderCallback(test_uia_provider_callback); + + /* Set clientside providers for our test windows and the desktop. */ + set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow()); + base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; + nc_prov = &Provider_nc.IRawElementProviderSimple_iface; + proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; + + set_clientside_providers_for_hwnd(NULL, &Provider_nc2, &Provider_hwnd2, hwnd[0]); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd[0], TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + Provider.ignore_hwnd_prop = TRUE; + + set_clientside_providers_for_hwnd(NULL, &Provider_nc3, &Provider_hwnd3, hwnd[1]); + initialize_provider(&Provider2, ProviderOptions_ServerSideProvider, hwnd[1], TRUE); + Provider2.frag_root = &Provider2.IRawElementProviderFragmentRoot_iface; + Provider2.ignore_hwnd_prop = TRUE; + + prov_root = &Provider.IRawElementProviderSimple_iface; + child_win_prov_root = &Provider2.IRawElementProviderSimple_iface; + + thread = CreateThread(NULL, 0, uia_proxy_provider_win_event_handler_test_thread, (void *)hwnd, 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); + + CoUninitialize(); + destroy_test_hwnd(hwnd[0], "ProxyProviderWinEventHandler test class", "ProxyProviderWinEventHandler test child class"); + UiaRegisterProviderCallback(NULL); +} + /* * Once a process returns a UI Automation provider with * UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This @@ -16590,6 +17224,7 @@ START_TEST(uiautomation) test_UiaNodeFromFocus(); test_UiaAddEvent(argv[0]); test_UiaHasServerSideProvider(); + test_uia_event_ProxyProviderWinEventHandler(); if (uia_dll) { pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible");
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 16 ++++++++-------- dlls/uiautomationcore/uia_client.c | 17 ++++++++++++----- 2 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 89757e2eb84..98990d3b19c 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -16812,8 +16812,8 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para set_uia_hwnd_expects(1, 1, 1, 2, 0); hr = UiaEventAddWindow(event, hwnd[0]); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - test_provider_event_advise_added(&Provider_hwnd2, 0, TRUE); - test_provider_event_advise_added(&Provider_nc2, 0, TRUE); + test_provider_event_advise_added(&Provider_hwnd2, 0, FALSE); + test_provider_event_advise_added(&Provider_nc2, 0, FALSE); check_uia_hwnd_expects_at_least(1, TRUE, 1, FALSE, 1, FALSE, 1, FALSE, 0, FALSE);
/* @@ -16982,9 +16982,9 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - test_provider_event_advise_added(&Provider_proxy, 0, TRUE); - test_provider_event_advise_added(&Provider_hwnd, 0, TRUE); - test_provider_event_advise_added(&Provider_nc, 0, TRUE); + test_provider_event_advise_added(&Provider_proxy, 0, FALSE); + test_provider_event_advise_added(&Provider_hwnd, 0, FALSE); + test_provider_event_advise_added(&Provider_nc, 0, FALSE);
/* Raise WinEvent on the desktop HWND. */ set_provider_runtime_id(&Provider_child, UIA_RUNTIME_ID_PREFIX, HandleToUlong(GetDesktopWindow())); @@ -17026,9 +17026,9 @@ static DWORD WINAPI uia_proxy_provider_win_event_handler_test_thread(LPVOID para hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - test_provider_event_advise_added(&Provider_hwnd, 0, TRUE); - test_provider_event_advise_added(&Provider_nc, 0, TRUE); - test_provider_event_advise_added(&Provider_proxy, 0, TRUE); + test_provider_event_advise_added(&Provider_hwnd, 0, FALSE); + test_provider_event_advise_added(&Provider_nc, 0, FALSE); + test_provider_event_advise_added(&Provider_proxy, 0, FALSE);
/* WinEvent handled. */ set_uia_hwnd_expects(1, 1, 1, 2, 1); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 57d1ca812f7..6ec675e197e 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1835,7 +1835,7 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT { struct uia_provider *prov = impl_from_IWineUiaProvider(iface); struct uia_event *event = (struct uia_event *)huiaevent; - IRawElementProviderFragmentRoot *elroot; + IRawElementProviderFragmentRoot *elroot = NULL; IRawElementProviderFragment *elfrag; SAFEARRAY *embedded_roots = NULL; HRESULT hr; @@ -1863,12 +1863,17 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT
if (elroot) { + IProxyProviderWinEventHandler *winevent_handler; IRawElementProviderAdviseEvents *advise_events;
- hr = IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents, - (void **)&advise_events); - IRawElementProviderFragmentRoot_Release(elroot); - if (SUCCEEDED(hr)) + if (!prov->return_nested_node && SUCCEEDED(IRawElementProviderFragmentRoot_QueryInterface(elroot, + &IID_IProxyProviderWinEventHandler, (void **)&winevent_handler))) + { + FIXME("MSAA to UIA event bridge currently unimplemented\n"); + IProxyProviderWinEventHandler_Release(winevent_handler); + } + else if (SUCCEEDED(IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents, + (void **)&advise_events))) { hr = uia_event_add_provider_event_adviser(advise_events, event); IRawElementProviderAdviseEvents_Release(advise_events); @@ -1918,6 +1923,8 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT }
exit: + if (elroot) + IRawElementProviderFragmentRoot_Release(elroot); IRawElementProviderFragment_Release(elfrag); SafeArrayDestroy(embedded_roots);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_event.c | 121 ++++++++++++++++++++++++++---- 1 file changed, 105 insertions(+), 16 deletions(-)
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 10dc1934844..04067f39e0e 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -50,6 +50,20 @@ static SAFEARRAY *uia_get_desktop_rt_id(void) return uia_desktop_node_rt_id; }
+static int win_event_to_uia_event_id(int win_event) +{ + switch (win_event) + { + case EVENT_OBJECT_FOCUS: return UIA_AutomationFocusChangedEventId; + case EVENT_SYSTEM_ALERT: return UIA_SystemAlertEventId; + + default: + break; + } + + return 0; +} + /* * UI Automation event map. */ @@ -247,6 +261,7 @@ struct uia_event_thread LONG ref;
struct list *event_queue; + HWINEVENTHOOK hook; };
#define WM_UIA_EVENT_THREAD_STOP (WM_USER + 1) @@ -264,12 +279,18 @@ static CRITICAL_SECTION event_thread_cs = { &event_thread_cs_debug, -1, 0, 0, 0, enum uia_queue_event_type { QUEUE_EVENT_TYPE_SERVERSIDE, QUEUE_EVENT_TYPE_CLIENTSIDE, + QUEUE_EVENT_TYPE_WIN_EVENT, };
struct uia_queue_event { struct list event_queue_entry; int queue_event_type; +}; + +struct uia_queue_uia_event +{ + struct uia_queue_event queue_entry;
struct uia_event_args *args; struct uia_event *event; @@ -285,14 +306,40 @@ struct uia_queue_event } u; };
-static void uia_event_queue_push(struct uia_queue_event *event) +struct uia_queue_win_event +{ + struct uia_queue_event queue_entry; + + HWINEVENTHOOK hook; + DWORD event_id; + HWND hwnd; + LONG obj_id; + LONG child_id; + DWORD thread_id; + DWORD event_time; +}; + +static void uia_event_queue_push(struct uia_queue_event *event, int queue_event_type) { + event->queue_event_type = queue_event_type; EnterCriticalSection(&event_thread_cs);
+ if (queue_event_type == QUEUE_EVENT_TYPE_WIN_EVENT) + { + struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)event; + + if (win_event->hook != event_thread.hook) + { + free(event); + goto exit; + } + } + assert(event_thread.event_queue); list_add_tail(event_thread.event_queue, &event->event_queue_entry); PostMessageW(event_thread.hwnd, WM_UIA_EVENT_THREAD_PROCESS_QUEUE, 0, 0);
+exit: LeaveCriticalSection(&event_thread_cs); }
@@ -322,7 +369,7 @@ static void uia_node_lresult_release(LRESULT lr)
static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args, struct uia_event *event); -static HRESULT uia_raise_clientside_event(struct uia_queue_event *event) +static HRESULT uia_raise_clientside_event(struct uia_queue_uia_event *event) { HUIANODE node, nav_start_node; HRESULT hr; @@ -354,7 +401,7 @@ static HRESULT uia_raise_clientside_event(struct uia_queue_event *event) return hr; }
-static HRESULT uia_raise_serverside_event(struct uia_queue_event *event) +static HRESULT uia_raise_serverside_event(struct uia_queue_uia_event *event) { HRESULT hr = S_OK; LRESULT lr, lr2; @@ -397,29 +444,70 @@ static void uia_event_thread_process_queue(struct list *event_queue) while (1) { struct uia_queue_event *event; - HRESULT hr; + HRESULT hr = S_OK;
if (!(event = uia_event_queue_pop(event_queue))) break;
- if (event->queue_event_type == QUEUE_EVENT_TYPE_SERVERSIDE) - hr = uia_raise_serverside_event(event); - else - hr = uia_raise_clientside_event(event); + switch (event->queue_event_type) + { + case QUEUE_EVENT_TYPE_SERVERSIDE: + case QUEUE_EVENT_TYPE_CLIENTSIDE: + { + struct uia_queue_uia_event *uia_event = (struct uia_queue_uia_event *)event; + + if (event->queue_event_type == QUEUE_EVENT_TYPE_SERVERSIDE) + hr = uia_raise_serverside_event(uia_event); + else + hr = uia_raise_clientside_event(uia_event); + + uia_event_args_release(uia_event->args); + IWineUiaEvent_Release(&uia_event->event->IWineUiaEvent_iface); + break; + } + + default: + break; + }
if (FAILED(hr)) WARN("Failed to raise event type %d with hr %#lx\n", event->queue_event_type, hr);
- uia_event_args_release(event->args); - IWineUiaEvent_Release(&event->event->IWineUiaEvent_iface); free(event); } }
+static void CALLBACK uia_event_thread_win_event_proc(HWINEVENTHOOK hook, DWORD event_id, HWND hwnd, LONG obj_id, + LONG child_id, DWORD thread_id, DWORD event_time) +{ + struct uia_queue_win_event *win_event; + + TRACE("%p, %ld, %p, %ld, %ld, %ld, %ld\n", hook, event_id, hwnd, obj_id, child_id, thread_id, event_time); + + if (!win_event_to_uia_event_id(event_id)) + return; + + if (!(win_event = calloc(1, sizeof(*win_event)))) + { + ERR("Failed to allocate uia_queue_win_event structure\n"); + return; + } + + win_event->hook = hook; + win_event->event_id = event_id; + win_event->hwnd = hwnd; + win_event->obj_id = obj_id; + win_event->child_id = child_id; + win_event->thread_id = thread_id; + win_event->event_time = event_time; + uia_event_queue_push(&win_event->queue_entry, QUEUE_EVENT_TYPE_WIN_EVENT); +} + static DWORD WINAPI uia_event_thread_proc(void *arg) { HANDLE initialized_event = arg; struct list event_queue; + HWINEVENTHOOK hook; HWND hwnd; MSG msg;
@@ -435,6 +523,8 @@ static DWORD WINAPI uia_event_thread_proc(void *arg)
event_thread.hwnd = hwnd; event_thread.event_queue = &event_queue; + event_thread.hook = hook = SetWinEventHook(EVENT_MIN, EVENT_MAX, 0, uia_event_thread_win_event_proc, 0, 0, + WINEVENT_OUTOFCONTEXT);
/* Initialization complete, thread can now process window messages. */ SetEvent(initialized_event); @@ -455,6 +545,7 @@ static DWORD WINAPI uia_event_thread_proc(void *arg)
TRACE("Shutting down UI Automation event thread.\n");
+ UnhookWinEvent(hook); DestroyWindow(hwnd); CoUninitialize(); FreeLibraryAndExitThread(huia_module, 0); @@ -664,7 +755,7 @@ static HRESULT WINAPI uia_event_set_event_data(IWineUiaEvent *iface, const GUID static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_node, VARIANT in_nav_start_node) { struct uia_event *event = impl_from_IWineUiaEvent(iface); - struct uia_queue_event *queue_event; + struct uia_queue_uia_event *queue_event; struct uia_event_args *args;
TRACE("%p, %s, %s\n", iface, debugstr_variant(&in_node), debugstr_variant(&in_nav_start_node)); @@ -680,7 +771,6 @@ static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_nod return E_OUTOFMEMORY; }
- queue_event->queue_event_type = QUEUE_EVENT_TYPE_CLIENTSIDE; queue_event->args = args; queue_event->event = event; queue_event->u.clientside.node = V_I4(&in_node); @@ -688,7 +778,7 @@ static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_nod queue_event->u.clientside.nav_start_node = V_I4(&in_nav_start_node);
IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); - uia_event_queue_push(queue_event); + uia_event_queue_push(&queue_event->queue_entry, QUEUE_EVENT_TYPE_CLIENTSIDE);
return S_OK; } @@ -1277,7 +1367,7 @@ static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct u } else { - struct uia_queue_event *queue_event; + struct uia_queue_uia_event *queue_event; HUIANODE node2, nav_start_node2;
if (!(queue_event = calloc(1, sizeof(*queue_event)))) @@ -1302,7 +1392,6 @@ static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct u } }
- queue_event->queue_event_type = QUEUE_EVENT_TYPE_SERVERSIDE; queue_event->args = args; queue_event->event = event; queue_event->u.serverside.node = node2; @@ -1310,7 +1399,7 @@ static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct u
InterlockedIncrement(&args->ref); IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); - uia_event_queue_push(queue_event); + uia_event_queue_push(&queue_event->queue_entry, QUEUE_EVENT_TYPE_SERVERSIDE); }
return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 4 ++ dlls/uiautomationcore/uia_event.c | 25 +++++++++ dlls/uiautomationcore/uia_private.h | 7 +++ dlls/uiautomationcore/uia_utils.c | 79 +++++++++++++++++++++++++++++ 4 files changed, 115 insertions(+)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 6ec675e197e..09e0d179172 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1870,6 +1870,9 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT &IID_IProxyProviderWinEventHandler, (void **)&winevent_handler))) { FIXME("MSAA to UIA event bridge currently unimplemented\n"); + hr = uia_event_add_win_event_hwnd(event, prov->hwnd); + if (FAILED(hr)) + WARN("Failed to add hwnd for win_event, hr %#lx\n", hr); IProxyProviderWinEventHandler_Release(winevent_handler); } else if (SUCCEEDED(IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents, @@ -1954,6 +1957,7 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl; prov->elprov = elprov; prov->ref = 1; + prov->hwnd = node->hwnd; node->prov[prov_type] = &prov->IWineUiaProvider_iface; if (!node->prov_count) node->creator_prov_type = prov_type; diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 04067f39e0e..796a74d86fc 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -64,6 +64,29 @@ static int win_event_to_uia_event_id(int win_event) return 0; }
+static BOOL CALLBACK uia_win_event_enum_top_level_hwnds(HWND hwnd, LPARAM lparam) +{ + struct rb_tree *hwnd_map = (struct rb_tree *)lparam; + HRESULT hr; + + if (!uia_hwnd_is_visible(hwnd)) + return TRUE; + + hr = uia_hwnd_map_add_hwnd(hwnd_map, hwnd); + if (FAILED(hr)) + WARN("Failed to add hwnd to map, hr %#lx\n", hr); + + return TRUE; +} + +HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) +{ + if (hwnd == GetDesktopWindow()) + EnumWindows(uia_win_event_enum_top_level_hwnds, (LPARAM)&event->u.clientside.win_event_hwnd_map); + + return uia_hwnd_map_add_hwnd(&event->u.clientside.win_event_hwnd_map, hwnd); +} + /* * UI Automation event map. */ @@ -658,6 +681,7 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) uia_cache_request_destroy(&event->u.clientside.cache_req); if (event->u.clientside.git_cookie) uia_stop_event_thread(); + uia_hwnd_map_destroy(&event->u.clientside.win_event_hwnd_map); } else { @@ -834,6 +858,7 @@ static HRESULT create_clientside_uia_event(struct uia_event **out_event, int eve event->scope = scope; event->u.clientside.event_callback = cback; event->u.clientside.callback_data = cback_data; + uia_hwnd_map_init(&event->u.clientside.win_event_hwnd_map);
*out_event = event; return S_OK; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index e8bb4845189..bd88dff30e3 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -95,6 +95,7 @@ struct uia_provider { BOOL return_nested_node; BOOL parent_check_ran; BOOL has_parent; + HWND hwnd; };
static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *iface) @@ -140,6 +141,7 @@ struct uia_event HRESULT (*event_callback)(struct uia_event *, struct uia_event_args *, SAFEARRAY *, BSTR); void *callback_data;
+ struct rb_tree win_event_hwnd_map; DWORD git_cookie; } clientside; struct { @@ -218,6 +220,7 @@ BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN; HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */ +HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) DECLSPEC_HIDDEN; HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_id, LONG event_cookie) DECLSPEC_HIDDEN; HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) DECLSPEC_HIDDEN; @@ -250,3 +253,7 @@ HRESULT uia_cache_request_clone(struct UiaCacheRequest *dst, struct UiaCacheRequ HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; 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; +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_utils.c b/dlls/uiautomationcore/uia_utils.c index 9f27d6a0b34..9840fe4fe40 100644 --- a/dlls/uiautomationcore/uia_utils.c +++ b/dlls/uiautomationcore/uia_utils.c @@ -384,3 +384,82 @@ int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type)
return 0; } + +/* + * HWND related helper functions. + */ +BOOL uia_hwnd_is_visible(HWND hwnd) +{ + RECT rect; + + if (!IsWindowVisible(hwnd)) + return FALSE; + + if (!GetWindowRect(hwnd, &rect)) + return FALSE; + + if ((rect.right - rect.left) <= 0 || (rect.bottom - rect.top) <= 0) + return FALSE; + + return TRUE; +} + +/* + * rbtree to efficiently store a collection of HWNDs. + */ +struct uia_hwnd_map_entry +{ + struct rb_entry entry; + HWND hwnd; +}; + +static int uia_hwnd_map_hwnd_compare(const void *key, const struct rb_entry *entry) +{ + struct uia_hwnd_map_entry *hwnd_entry = RB_ENTRY_VALUE(entry, struct uia_hwnd_map_entry, entry); + HWND hwnd = (HWND)key; + + return (hwnd_entry->hwnd > hwnd) - (hwnd_entry->hwnd < hwnd); +} + +static void uia_hwnd_map_free(struct rb_entry *entry, void *context) +{ + struct uia_hwnd_map_entry *hwnd_entry = RB_ENTRY_VALUE(entry, struct uia_hwnd_map_entry, entry); + + TRACE("Removing hwnd %p from map %p\n", hwnd_entry->hwnd, context); + free(hwnd_entry); +} + +static BOOL uia_hwnd_map_check_hwnd(struct rb_tree *hwnd_map, HWND hwnd) +{ + return !!rb_get(hwnd_map, hwnd); +} + +HRESULT uia_hwnd_map_add_hwnd(struct rb_tree *hwnd_map, HWND hwnd) +{ + struct uia_hwnd_map_entry *entry; + + if (uia_hwnd_map_check_hwnd(hwnd_map, hwnd)) + { + TRACE("hwnd %p already in map %p\n", hwnd, hwnd_map); + return S_OK; + } + + if (!(entry = calloc(1, sizeof(*entry)))) + return E_OUTOFMEMORY; + + TRACE("Adding hwnd %p to map %p\n", hwnd, hwnd_map); + entry->hwnd = hwnd; + rb_put(hwnd_map, hwnd, &entry->entry); + + return S_OK; +} + +void uia_hwnd_map_init(struct rb_tree *hwnd_map) +{ + rb_init(hwnd_map, uia_hwnd_map_hwnd_compare); +} + +void uia_hwnd_map_destroy(struct rb_tree *hwnd_map) +{ + rb_destroy(hwnd_map, uia_hwnd_map_free, hwnd_map); +}
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_event.c | 19 ++++++++++++++----- dlls/uiautomationcore/uia_private.h | 1 + 2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 796a74d86fc..96b6ac448aa 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -79,8 +79,12 @@ static BOOL CALLBACK uia_win_event_enum_top_level_hwnds(HWND hwnd, LPARAM lparam return TRUE; }
+static BOOL uia_clientside_event_start_event_thread(struct uia_event *event); HRESULT uia_event_add_win_event_hwnd(struct uia_event *event, HWND hwnd) { + if (!uia_clientside_event_start_event_thread(event)) + return E_FAIL; + if (hwnd == GetDesktopWindow()) EnumWindows(uia_win_event_enum_top_level_hwnds, (LPARAM)&event->u.clientside.win_event_hwnd_map);
@@ -630,6 +634,14 @@ static void uia_stop_event_thread(void) LeaveCriticalSection(&event_thread_cs); }
+static BOOL uia_clientside_event_start_event_thread(struct uia_event *event) +{ + if (!event->u.clientside.event_thread_started) + event->u.clientside.event_thread_started = uia_start_event_thread(); + + return event->u.clientside.event_thread_started; +} + /* * IWineUiaEvent interface. */ @@ -679,7 +691,7 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) if (event->event_type == EVENT_TYPE_CLIENTSIDE) { uia_cache_request_destroy(&event->u.clientside.cache_req); - if (event->u.clientside.git_cookie) + if (event->u.clientside.event_thread_started) uia_stop_event_thread(); uia_hwnd_map_destroy(&event->u.clientside.win_event_hwnd_map); } @@ -1160,16 +1172,13 @@ HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, */ if (!event->u.clientside.git_cookie) { - if (!uia_start_event_thread()) + if (!uia_clientside_event_start_event_thread(event)) return E_FAIL;
hr = register_interface_in_git((IUnknown *)&event->IWineUiaEvent_iface, &IID_IWineUiaEvent, &event->u.clientside.git_cookie); if (FAILED(hr)) - { - uia_stop_event_thread(); return hr; - } }
if (!(adv_events = calloc(1, sizeof(*adv_events)))) diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index bd88dff30e3..c298967be00 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -142,6 +142,7 @@ struct uia_event void *callback_data;
struct rb_tree win_event_hwnd_map; + BOOL event_thread_started; DWORD git_cookie; } clientside; struct {
On Mon Sep 25 12:01:10 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/3919/diffs?diff_id=71609&start_sha=964b1ea77e26882be7441a68a1d7ea247515e293#757856e6ad8b0244e53744faeb8594b6ec93b002_2813_2815)
I did a few things to help remedy potential spurious WinEvents causing test failures:
- Early return in `RespondToWinEvent()` if an event is delivered that we weren't listening for. - Switch all `EVENT_OBJECT_FOCUS` events to use `OBJID_WINDOW`. The system doesn't send focus events with this object ID.
This merge request was approved by Esme Povirk.