-- v2: uiautomationcore: Implement UiaEventAddWindow. uiautomationcore: Add support for advising providers of events on serverside events. uiautomationcore: Prevent creation of duplicate serverside events. uiautomationcore: Add support for creating serverside events. uiautomationcore/tests: Add tests for cross-process events.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 433 ++++++++++++++++++++- 1 file changed, 427 insertions(+), 6 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index efcba1c2b5f..7003d368df5 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -53,6 +53,9 @@ static HRESULT (WINAPI *pUiaDisconnectProvider)(IRawElementProviderSimple *); expect_ ## func--; \ }while(0)
+#define CALLED_COUNT(func) \ + called_ ## func + #define CHECK_CALLED(func) \ do { \ ok(called_ ## func, "expected " #func "\n"); \ @@ -65,6 +68,18 @@ static HRESULT (WINAPI *pUiaDisconnectProvider)(IRawElementProviderSimple *); expect_ ## func = called_ ## func = 0; \ }while(0)
+#define CHECK_CALLED_AT_LEAST(func, num) \ + do { \ + ok(called_ ## func >= num, "expected " #func " at least %d time(s) (got %d)\n", num, called_ ## func); \ + expect_ ## func = called_ ## func = 0; \ + }while(0) + +#define CHECK_CALLED_AT_MOST(func, num) \ + do { \ + ok(called_ ## func <= num, "expected " #func " at most %d time(s) (got %d)\n", num, called_ ## func); \ + expect_ ## func = called_ ## func = 0; \ + }while(0) + #define NAVDIR_INTERNAL_HWND 10 #define UIA_RUNTIME_ID_PREFIX 42
@@ -7912,6 +7927,7 @@ struct node_provider_desc { const WCHAR *prov_type[MAX_NODE_PROVIDERS]; const WCHAR *prov_name[MAX_NODE_PROVIDERS]; BOOL parent_link[MAX_NODE_PROVIDERS]; + struct node_provider_desc *nested_desc[MAX_NODE_PROVIDERS]; int prov_count; };
@@ -7922,15 +7938,22 @@ static void init_node_provider_desc(struct node_provider_desc *desc, DWORD pid, desc->hwnd = hwnd; }
-static void add_provider_desc(struct node_provider_desc *desc, const WCHAR *prov_type, const WCHAR *prov_name, - BOOL parent_link) +static void add_nested_provider_desc(struct node_provider_desc *desc, const WCHAR *prov_type, const WCHAR *prov_name, + BOOL parent_link, struct node_provider_desc *nested_desc) { desc->prov_type[desc->prov_count] = prov_type; desc->prov_name[desc->prov_count] = prov_name; desc->parent_link[desc->prov_count] = parent_link; + desc->nested_desc[desc->prov_count] = nested_desc; desc->prov_count++; }
+static void add_provider_desc(struct node_provider_desc *desc, const WCHAR *prov_type, const WCHAR *prov_name, + BOOL parent_link) +{ + add_nested_provider_desc(desc, prov_type, prov_name, parent_link, NULL); +} + #define test_node_provider_desc( desc, desc_str ) \ test_node_provider_desc_( (desc), (desc_str), __FILE__, __LINE__) static void test_node_provider_desc_(struct node_provider_desc *desc, BSTR desc_str, const char *file, int line) @@ -7939,7 +7962,18 @@ static void test_node_provider_desc_(struct node_provider_desc *desc, BSTR desc_
check_node_provider_desc_prefix_(desc_str, desc->pid, desc->hwnd, file, line); for (i = 0; i < desc->prov_count; i++) - check_node_provider_desc_(desc_str, desc->prov_type[i], desc->prov_name[i], desc->parent_link[i], FALSE, file, line); + { + if (desc->nested_desc[i]) + { + WCHAR buf[2048]; + + ok_(file, line)(get_nested_provider_desc(desc_str, desc->prov_type[i], desc->parent_link[i], buf), + "Failed to get nested provider description\n"); + test_node_provider_desc_(desc->nested_desc[i], buf, file, line); + } + else + check_node_provider_desc_(desc_str, desc->prov_type[i], desc->prov_name[i], desc->parent_link[i], FALSE, file, line); + } }
/* @@ -13766,6 +13800,9 @@ static struct EventData { LONG exp_elems[2]; struct node_provider_desc exp_node_desc; const WCHAR *exp_tree_struct; + + struct node_provider_desc exp_nested_node_desc; + HANDLE event_handle; } EventData;
static void set_event_data(LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, LONG exp_elems1, @@ -13776,7 +13813,20 @@ static void set_event_data(LONG exp_lbound0, LONG exp_lbound1, LONG exp_elems0, EventData.exp_elems[0] = exp_elems0; EventData.exp_elems[1] = exp_elems1; if (exp_node_desc) + { + int i; + EventData.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; + break; + } + } + } else memset(&EventData.exp_node_desc, 0, sizeof(EventData.exp_node_desc)); EventData.exp_tree_struct = exp_tree_struct; @@ -13792,6 +13842,280 @@ static void WINAPI uia_event_callback(struct UiaEventArgs *args, SAFEARRAY *req_ test_cache_req_sa(req_data, EventData.exp_lbound, EventData.exp_elems, &EventData.exp_node_desc);
ok(!wcscmp(tree_struct, EventData.exp_tree_struct), "tree structure %s\n", debugstr_w(tree_struct)); + if (EventData.event_handle) + SetEvent(EventData.event_handle); +} + +enum { + WM_UIA_TEST_RESET_EVENT_PROVIDERS = WM_APP, + WM_UIA_TEST_SET_EVENT_PROVIDER_DATA, + WM_UIA_TEST_RAISE_EVENT, + WM_UIA_TEST_RAISE_EVENT_RT_ID, + WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, + WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, +}; + +enum { + PROVIDER_ID, + PROVIDER2_ID, + PROVIDER_CHILD_ID, +}; + +static struct Provider *event_test_provs[] = { &Provider, &Provider2, &Provider_child }; +static struct Provider *get_event_test_prov(int idx) +{ + if (idx >= ARRAY_SIZE(event_test_provs)) + return NULL; + else + return event_test_provs[idx]; +} + +static void post_event_message(HWND hwnd, int msg, WPARAM wparam, int prov_id, int lparam_lower) +{ + PostMessageW(hwnd, msg, wparam, MAKELONG(lparam_lower, prov_id)); +} + +static void test_UiaAddEvent_client_proc(void) +{ + IRawElementProviderFragmentRoot *embedded_root = &Provider_hwnd2.IRawElementProviderFragmentRoot_iface; + struct node_provider_desc exp_node_desc, exp_nested_node_desc; + struct UiaCacheRequest cache_req; + HUIAEVENT event; + HUIANODE node; + BOOL is_win11; + HRESULT hr; + DWORD pid; + HWND hwnd; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + hwnd = FindWindowA("UiaAddEvent test class", "Test window"); + UiaRegisterProviderCallback(test_uia_provider_callback); + EventData.event_handle = CreateEventW(NULL, FALSE, FALSE, NULL); + + /* Provider_proxy/hwnd/nc are desktop providers. */ + set_clientside_providers_for_hwnd(&Provider_proxy, &Provider_nc, &Provider_hwnd, GetDesktopWindow()); + base_hwnd_prov = &Provider_hwnd.IRawElementProviderSimple_iface; + proxy_prov = &Provider_proxy.IRawElementProviderSimple_iface; + nc_prov = &Provider_nc.IRawElementProviderSimple_iface; + + /* + * Provider_nc2/Provider_hwnd2 are clientside providers for our test + * window. + */ + set_clientside_providers_for_hwnd(NULL, &Provider_nc2, &Provider_hwnd2, hwnd); + provider_add_child(&Provider_hwnd, &Provider_hwnd2); + + method_sequences_enabled = FALSE; + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + hr = UiaGetRootNode(&node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!node, "Node == NULL.\n"); + ok(Provider_proxy.ref == 2, "Unexpected refcnt %ld\n", Provider_proxy.ref); + ok(Provider_hwnd.ref == 2, "Unexpected refcnt %ld\n", Provider_hwnd.ref); + ok(Provider_nc.ref == 2, "Unexpected refcnt %ld\n", Provider_nc.ref); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); + CHECK_CALLED(prov_callback_proxy); + + PostMessageW(hwnd, WM_UIA_TEST_RESET_EVENT_PROVIDERS, 0, 0); + + /* Register an event on the desktop HWND with a scope of all elements. */ + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + event = NULL; + /* Only done on Win11. */ + SET_EXPECT_MULTI(prov_callback_base_hwnd, 2); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, + NULL, 0, &cache_req, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); + is_win11 = !!CALLED_COUNT(prov_callback_base_hwnd); + CHECK_CALLED_AT_MOST(prov_callback_base_hwnd, 2); + CHECK_CALLED_AT_MOST(prov_callback_nonclient, 2); + CHECK_CALLED_AT_MOST(prov_callback_proxy, 2); + method_sequences_enabled = TRUE; + + /* + * Raise event in another process, prior to calling UiaEventAddWindow. + * Event handler will not get triggered. + */ + post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT, HandleToUlong(hwnd), PROVIDER_ID, ProviderOptions_ServerSideProvider); + ok(WaitForSingleObject(EventData.event_handle, 300) == WAIT_TIMEOUT, "Wait for event_handle didn't timeout.\n"); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, 0, PROVIDER_ID, FALSE); + + /* Call UiaEventAddWindow, the event will now be connected. */ + SET_EXPECT_MULTI(prov_callback_base_hwnd, 2); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 3); + hr = UiaEventAddWindow(event, hwnd); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED(prov_callback_nonclient); + todo_wine CHECK_CALLED(prov_callback_proxy); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + + /* Successfully raise event. */ + GetWindowThreadProcessId(hwnd, &pid); + init_node_provider_desc(&exp_nested_node_desc, pid, hwnd); + add_provider_desc(&exp_nested_node_desc, L"Main", L"Provider", TRUE); + + init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), hwnd); + add_provider_desc(&exp_node_desc, L"Hwnd", L"Provider_hwnd2", TRUE); + add_provider_desc(&exp_node_desc, L"Nonclient", L"Provider_nc2", FALSE); + add_nested_provider_desc(&exp_node_desc, L"Main", NULL, FALSE, &exp_nested_node_desc); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT(prov_callback_nonclient); + SET_EXPECT(prov_callback_proxy); + SET_EXPECT(uia_event_callback); + post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT, HandleToUlong(hwnd), PROVIDER_ID, ProviderOptions_ServerSideProvider); + todo_wine ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); + todo_wine CHECK_CALLED(uia_event_callback); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED(prov_callback_nonclient); + todo_wine CHECK_CALLED(prov_callback_proxy); + + /* + * If a clientside provider raises an event, it stays within its own + * process. + */ + post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT, 0, PROVIDER2_ID, ProviderOptions_ClientSideProvider); + ok(WaitForSingleObject(EventData.event_handle, 300) == WAIT_TIMEOUT, "Wait for event_handle didn't timeout.\n"); + + /* Raise serverside event. */ + GetWindowThreadProcessId(hwnd, &pid); + init_node_provider_desc(&exp_node_desc, pid, NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + + SET_EXPECT(uia_event_callback); + post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT, 0, PROVIDER2_ID, ProviderOptions_ServerSideProvider); + todo_wine ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); + todo_wine CHECK_CALLED(uia_event_callback); + + hr = UiaRemoveEvent(event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + + PostMessageW(hwnd, WM_UIA_TEST_RESET_EVENT_PROVIDERS, 0, 0); + post_event_message(hwnd, WM_UIA_TEST_SET_EVENT_PROVIDER_DATA, HandleToUlong(hwnd), PROVIDER_ID, + ProviderOptions_ServerSideProvider); + + /* + * Register an event on the desktop node, except this time the scope + * doesn't include the desktop node itself. In this case, navigation will + * have to be done to confirm a provider that raised an event is a + * descendant of the desktop, but not the desktop itself. + * + * No need for UiaEventAddWindow this time, because Provider_hwnd2 is an + * embedded root. This method no longer works on Windows 11, which breaks + * the managed UI Automation API. We match the old behavior instead. + */ + Provider_hwnd.embedded_frag_roots = &embedded_root; + Provider_hwnd.embedded_frag_roots_count = 1; + Provider_hwnd2.frag_root = &Provider_hwnd2.IRawElementProviderFragmentRoot_iface; + + event = NULL; + /* Only done on Win11. */ + SET_EXPECT_MULTI(prov_callback_base_hwnd, 2); + SET_EXPECT_MULTI(prov_callback_nonclient, 3); + SET_EXPECT_MULTI(prov_callback_proxy, 3); + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Descendants, + NULL, 0, &cache_req, &event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine ok(!!event, "event == NULL\n"); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + CHECK_CALLED_AT_MOST(prov_callback_base_hwnd, 2); + CHECK_CALLED_AT_MOST(prov_callback_nonclient, 3); + CHECK_CALLED_AT_MOST(prov_callback_proxy, 3); + + if (is_win11) + { + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, 0, PROVIDER_ID, FALSE); + SET_EXPECT_MULTI(prov_callback_base_hwnd, 2); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 3); + hr = UiaEventAddWindow(event, hwnd); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + CHECK_CALLED_MULTI(prov_callback_base_hwnd, 2); + CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + CHECK_CALLED_MULTI(prov_callback_proxy, 3); + } + + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + + /* + * Starts navigation in server process, then completes navigation in the + * client process. + */ + GetWindowThreadProcessId(hwnd, &pid); + init_node_provider_desc(&exp_node_desc, pid, 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)"); + SET_EXPECT(prov_callback_base_hwnd); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 2); + SET_EXPECT(uia_event_callback); + + post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT, 0, PROVIDER_CHILD_ID, ProviderOptions_ServerSideProvider); + todo_wine ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED_MULTI(prov_callback_nonclient, 2); + todo_wine CHECK_CALLED_MULTI(prov_callback_proxy, 2); + todo_wine CHECK_CALLED(uia_event_callback); + + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + + /* + * Register an event on a node that won't require any navigation to reach. + */ + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, NULL, TRUE); + Provider.runtime_id[0] = 0x1337; + Provider.runtime_id[1] = 0xbeef; + hr = UiaNodeFromProvider(&Provider.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, + NULL, 0, &cache_req, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + + SET_EXPECT_MULTI(prov_callback_base_hwnd, 2); + SET_EXPECT_MULTI(prov_callback_nonclient, 2); + SET_EXPECT_MULTI(prov_callback_proxy, 3); + hr = UiaEventAddWindow(event, hwnd); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine CHECK_CALLED(prov_callback_base_hwnd); + todo_wine CHECK_CALLED(prov_callback_nonclient); + todo_wine CHECK_CALLED(prov_callback_proxy); + + /* Wrong runtime ID, no match. */ + post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT_RT_ID, 0xb33f, PROVIDER2_ID, 0x1337); + ok(WaitForSingleObject(EventData.event_handle, 500) == WAIT_TIMEOUT, "Wait for event_handle didn't timeout.\n"); + + /* Successfully raise event. */ + GetWindowThreadProcessId(hwnd, &pid); + init_node_provider_desc(&exp_node_desc, pid, NULL); + add_provider_desc(&exp_node_desc, L"Main", L"Provider2", TRUE); + set_event_data(0, 0, 1, 1, &exp_node_desc, L"P)"); + SET_EXPECT(uia_event_callback); + post_event_message(hwnd, WM_UIA_TEST_RAISE_EVENT_RT_ID, 0xbeef, PROVIDER2_ID, 0x1337); + todo_wine ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); + todo_wine CHECK_CALLED(uia_event_callback); + + hr = UiaRemoveEvent(event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + CloseHandle(EventData.event_handle); + CoUninitialize(); }
struct event_test_thread_data { @@ -13895,7 +14219,7 @@ static void test_UiaRaiseAutomationEvent_args(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); }
-static void test_UiaAddEvent(void) +static void test_UiaAddEvent(const char *name) { IRawElementProviderFragmentRoot *embedded_roots[2] = { &Provider_child.IRawElementProviderFragmentRoot_iface, &Provider_child2.IRawElementProviderFragmentRoot_iface }; @@ -13904,7 +14228,11 @@ static void test_UiaAddEvent(void) struct node_provider_desc exp_node_desc; struct UiaPropertyCondition prop_cond; struct UiaCacheRequest cache_req; + PROCESS_INFORMATION proc; + char cmdline[MAX_PATH]; + STARTUPINFOA startup; HUIAEVENT event; + DWORD exit_code; HUIANODE node; HANDLE thread; HRESULT hr; @@ -14283,10 +14611,101 @@ static void test_UiaAddEvent(void) hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); UiaNodeRelease(node); + CoUninitialize(); + + /* Cross process event tests. */ + UiaRegisterProviderCallback(NULL); + CoInitializeEx(NULL, COINIT_MULTITHREADED); + initialize_provider(&Provider, ProviderOptions_ServerSideProvider, hwnd, TRUE); + Provider.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + initialize_provider(&Provider_child, ProviderOptions_ServerSideProvider, NULL, TRUE); + provider_add_child(&Provider, &Provider_child); + + prov_root = &Provider.IRawElementProviderSimple_iface; + sprintf(cmdline, ""%s" uiautomation UiaAddEvent_client_proc", name); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + SET_EXPECT_MULTI(winproc_GETOBJECT_UiaRoot, 22); + /* Only sent on Win7. */ + SET_EXPECT(winproc_GETOBJECT_CLIENT); + + CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &proc); + while (MsgWaitForMultipleObjects(1, &proc.hProcess, FALSE, INFINITE, QS_ALLINPUT) != WAIT_OBJECT_0) + { + struct Provider *prov; + MSG msg = { 0 }; + + while (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + switch (msg.message) + { + case WM_UIA_TEST_RESET_EVENT_PROVIDERS: + { + int i; + + for (i = 0; i < ARRAY_SIZE(event_test_provs); i++) + initialize_provider(event_test_provs[i], ProviderOptions_ServerSideProvider, NULL, FALSE); + break; + } + + case WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED: + case WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED: + if (!(prov = get_event_test_prov(HIWORD(msg.lParam)))) + break; + + if (msg.message == WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED) + todo_wine_if(LOWORD(msg.lParam)) ok(prov->advise_events_added_event_id == msg.wParam, + "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); + else + todo_wine_if(LOWORD(msg.lParam)) ok(prov->advise_events_removed_event_id == msg.wParam, + "Unexpected advise event removed, event ID %d\n", Provider.advise_events_removed_event_id); + break; + + case WM_UIA_TEST_RAISE_EVENT: + case WM_UIA_TEST_RAISE_EVENT_RT_ID: + case WM_UIA_TEST_SET_EVENT_PROVIDER_DATA: + if (!(prov = get_event_test_prov(HIWORD(msg.lParam)))) + break; + + if ((msg.message == WM_UIA_TEST_RAISE_EVENT) || (msg.message == WM_UIA_TEST_SET_EVENT_PROVIDER_DATA)) + { + prov->prov_opts = LOWORD(msg.lParam); + prov->hwnd = UlongToHandle(msg.wParam); + prov->ignore_hwnd_prop = !!prov->hwnd; + } + else if (msg.message == WM_UIA_TEST_RAISE_EVENT_RT_ID) + { + prov->runtime_id[0] = LOWORD(msg.lParam); + prov->runtime_id[1] = msg.wParam; + } + + if (msg.message != WM_UIA_TEST_SET_EVENT_PROVIDER_DATA) + { + hr = UiaRaiseAutomationEvent(&prov->IRawElementProviderSimple_iface, UIA_AutomationFocusChangedEventId); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + break; + + default: + prov = NULL; + break; + } + + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + todo_wine CHECK_CALLED_AT_LEAST(winproc_GETOBJECT_UiaRoot, 5); + GetExitCodeProcess(proc.hProcess, &exit_code); + if (exit_code > 255) + ok(0, "unhandled exception %08x in child process %04x\n", (UINT)exit_code, (UINT)GetProcessId(proc.hProcess)); + else if (exit_code) + ok(0, "%u failures in child process\n", (UINT)exit_code); + CloseHandle(proc.hProcess);
method_sequences_enabled = TRUE; CoUninitialize(); - UiaRegisterProviderCallback(NULL); DestroyWindow(hwnd); UnregisterClassA("UiaAddEvent test class", NULL); } @@ -14340,6 +14759,8 @@ START_TEST(uiautomation) test_UiaNodeFromHandle_client_proc(); else if (!strcmp(argv[2], "UiaRegisterProviderCallback")) test_UiaRegisterProviderCallback(); + else if (!strcmp(argv[2], "UiaAddEvent_client_proc")) + test_UiaAddEvent_client_proc();
FreeLibrary(uia_dll); return; @@ -14361,7 +14782,7 @@ START_TEST(uiautomation) test_default_clientside_providers(); test_UiaGetRootNode(); test_UiaNodeFromFocus(); - test_UiaAddEvent(); + test_UiaAddEvent(argv[0]); 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 | 6 +- dlls/uiautomationcore/uia_classes.idl | 2 + dlls/uiautomationcore/uia_client.c | 49 +++++- dlls/uiautomationcore/uia_event.c | 184 +++++++++++++++++++-- dlls/uiautomationcore/uia_private.h | 32 +++- 5 files changed, 252 insertions(+), 21 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 7003d368df5..6d0e6f7c557 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -14027,8 +14027,8 @@ static void test_UiaAddEvent_client_proc(void) SET_EXPECT_MULTI(prov_callback_proxy, 3); hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Descendants, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); CHECK_CALLED_AT_MOST(prov_callback_base_hwnd, 2); CHECK_CALLED_AT_MOST(prov_callback_nonclient, 3); @@ -14070,7 +14070,7 @@ static void test_UiaAddEvent_client_proc(void) todo_wine CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE);
/* diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 79bf8556326..3f8e17d3d72 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -68,6 +68,7 @@ library UIA_wine_private object, uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba), pointer_default(unique), + oleautomation, ] interface IWineUiaEvent : IUnknown { @@ -102,5 +103,6 @@ library UIA_wine_private HRESULT get_prop_val([in]const GUID *prop_guid, [out, retval]VARIANT *ret_val); HRESULT disconnect(); HRESULT get_hwnd([out, retval]ULONG *out_hwnd); + HRESULT attach_event([out, retval]IWineUiaEvent **ret_event); } } diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index e1dd7d0bd23..5fe548bff18 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -539,6 +539,37 @@ static HRESULT WINAPI uia_node_get_hwnd(IWineUiaNode *iface, ULONG *out_hwnd) return S_OK; }
+static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, IWineUiaEvent **ret_event) +{ + struct uia_node *node = impl_from_IWineUiaNode(iface); + struct uia_event *event = NULL; + HRESULT hr; + + TRACE("%p, %p\n", node, ret_event); + + *ret_event = NULL; + hr = create_serverside_uia_event(&event); + if (FAILED(hr)) + return hr; + + hr = attach_event_to_node_provider(iface, 0, (HUIAEVENT)event); + if (FAILED(hr)) + { + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + return hr; + } + + /* + * Attach this nested node to the serverside event to keep the provider + * thread alive. + */ + IWineUiaNode_AddRef(iface); + event->u.serverside.node = iface; + *ret_event = &event->IWineUiaEvent_iface; + + return hr; +} + static const IWineUiaNodeVtbl uia_node_vtbl = { uia_node_QueryInterface, uia_node_AddRef, @@ -547,6 +578,7 @@ static const IWineUiaNodeVtbl uia_node_vtbl = { uia_node_get_prop_val, uia_node_disconnect, uia_node_get_hwnd, + uia_node_attach_event, };
static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface) @@ -2243,8 +2275,21 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface
static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent) { - FIXME("%p, %#Ix: stub\n", iface, huiaevent); - return E_NOTIMPL; + struct uia_nested_node_provider *prov = impl_from_nested_node_IWineUiaProvider(iface); + struct uia_event *event = (struct uia_event *)huiaevent; + IWineUiaEvent *remote_event = NULL; + HRESULT hr; + + TRACE("%p, %#Ix\n", iface, huiaevent); + + hr = IWineUiaNode_attach_event(prov->nested_node, &remote_event); + if (FAILED(hr) || !remote_event) + return hr; + + hr = uia_event_add_serverside_event_adviser(remote_event, event); + IWineUiaEvent_Release(remote_event); + + return hr; }
static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index a0b4ced1aa9..d87d2c87a03 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -225,7 +225,15 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) assert(!event->event_map_entry);
SafeArrayDestroy(event->runtime_id); - uia_cache_request_destroy(&event->cache_req); + if (event->event_type == EVENT_TYPE_CLIENTSIDE) + { + uia_cache_request_destroy(&event->u.clientside.cache_req); + if (event->u.clientside.mta_cookie) + CoDecrementMTAUsage(event->u.clientside.mta_cookie); + } + else if (event->u.serverside.node) + IWineUiaNode_Release(event->u.serverside.node); + for (i = 0; i < event->event_advisers_count; i++) IWineUiaEventAdviser_Release(event->event_advisers[i]); heap_free(event->event_advisers); @@ -298,12 +306,42 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int event->runtime_id = runtime_id; event->event_id = event_id; event->scope = scope; - event->cback = cback; + event->u.clientside.cback = cback; + event->event_type = EVENT_TYPE_CLIENTSIDE;
*out_event = event; return S_OK; }
+HRESULT create_serverside_uia_event(struct uia_event **out_event) +{ + struct uia_event *event = heap_alloc_zero(sizeof(*event)); + + *out_event = NULL; + if (!event) + return E_OUTOFMEMORY; + + event->IWineUiaEvent_iface.lpVtbl = &uia_event_vtbl; + event->ref = 1; + event->event_type = EVENT_TYPE_SERVERSIDE; + + *out_event = event; + return S_OK; +} + +static HRESULT uia_event_add_event_adviser(IWineUiaEventAdviser *adviser, struct uia_event *event) +{ + if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size, + event->event_advisers_count + 1, sizeof(*event->event_advisers))) + return E_OUTOFMEMORY; + + event->event_advisers[event->event_advisers_count] = adviser; + IWineUiaEventAdviser_AddRef(adviser); + event->event_advisers_count++; + + return S_OK; +} + /* * IWineUiaEventAdviser interface. */ @@ -438,19 +476,113 @@ HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *ad adv_events->advise_events = advise_events; IRawElementProviderAdviseEvents_AddRef(advise_events);
- if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size, - event->event_advisers_count + 1, sizeof(*event->event_advisers))) + hr = uia_event_add_event_adviser(&adv_events->IWineUiaEventAdviser_iface, event); + IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface); + + return hr; +} + +/* + * IWineUiaEventAdviser interface for serverside events. + */ +struct uia_serverside_event_adviser { + IWineUiaEventAdviser IWineUiaEventAdviser_iface; + LONG ref; + + IWineUiaEvent *event_iface; +}; + +static inline struct uia_serverside_event_adviser *impl_from_serverside_IWineUiaEventAdviser(IWineUiaEventAdviser *iface) +{ + return CONTAINING_RECORD(iface, struct uia_serverside_event_adviser, IWineUiaEventAdviser_iface); +} + +static HRESULT WINAPI uia_serverside_event_adviser_QueryInterface(IWineUiaEventAdviser *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IWineUiaEventAdviser) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IWineUiaEventAdviser_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_serverside_event_adviser_AddRef(IWineUiaEventAdviser *iface) +{ + struct uia_serverside_event_adviser *adv_events = impl_from_serverside_IWineUiaEventAdviser(iface); + ULONG ref = InterlockedIncrement(&adv_events->ref); + + TRACE("%p, refcount %ld\n", adv_events, ref); + return ref; +} + +static ULONG WINAPI uia_serverside_event_adviser_Release(IWineUiaEventAdviser *iface) +{ + struct uia_serverside_event_adviser *adv_events = impl_from_serverside_IWineUiaEventAdviser(iface); + ULONG ref = InterlockedDecrement(&adv_events->ref); + + TRACE("%p, refcount %ld\n", adv_events, ref); + if (!ref) { - IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface); - return E_OUTOFMEMORY; + IWineUiaEvent_Release(adv_events->event_iface); + heap_free(adv_events); } + return ref; +}
- event->event_advisers[event->event_advisers_count] = &adv_events->IWineUiaEventAdviser_iface; - event->event_advisers_count++; - +static HRESULT WINAPI uia_serverside_event_adviser_advise(IWineUiaEventAdviser *iface, BOOL advise_added, LONG_PTR huiaevent) +{ + FIXME("%p, %d, %#Ix: stub\n", iface, advise_added, huiaevent); return S_OK; }
+static const IWineUiaEventAdviserVtbl uia_serverside_event_adviser_vtbl = { + uia_serverside_event_adviser_QueryInterface, + uia_serverside_event_adviser_AddRef, + uia_serverside_event_adviser_Release, + uia_serverside_event_adviser_advise, +}; + +HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, struct uia_event *event) +{ + struct uia_serverside_event_adviser *adv_events; + HRESULT hr; + + /* + * Need to create a proxy IWineUiaEvent for our clientside event to use + * this serverside IWineUiaEvent proxy from the appropriate apartment. + */ + if (!event->u.clientside.git_cookie) + { + hr = CoIncrementMTAUsage(&event->u.clientside.mta_cookie); + if (FAILED(hr)) + return hr; + + hr = register_interface_in_git((IUnknown *)&event->IWineUiaEvent_iface, &IID_IWineUiaEvent, + &event->u.clientside.git_cookie); + if (FAILED(hr)) + { + CoDecrementMTAUsage(event->u.clientside.mta_cookie); + return hr; + } + } + + if (!(adv_events = heap_alloc_zero(sizeof(*adv_events)))) + return E_OUTOFMEMORY; + + adv_events->IWineUiaEventAdviser_iface.lpVtbl = &uia_serverside_event_adviser_vtbl; + adv_events->ref = 1; + adv_events->event_iface = serverside_event; + IWineUiaEvent_AddRef(serverside_event); + + hr = uia_event_add_event_adviser(&adv_events->IWineUiaEventAdviser_iface, event); + IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface); + + return hr; +} + /*********************************************************************** * UiaAddEvent (uiautomationcore.@) */ @@ -489,7 +621,7 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback return hr; }
- hr = uia_cache_request_clone(&event->cache_req, cache_req); + hr = uia_cache_request_clone(&event->u.clientside.cache_req, cache_req); if (FAILED(hr)) goto exit;
@@ -520,6 +652,7 @@ exit: HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) { struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); + IWineUiaEvent *event_iface; HRESULT hr;
TRACE("(%p)\n", event); @@ -527,8 +660,31 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) if (!event) return E_INVALIDARG;
- hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, FALSE); - IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + assert(event->event_type == EVENT_TYPE_CLIENTSIDE); + if (event->u.clientside.git_cookie) + { + hr = get_interface_in_git(&IID_IWineUiaEvent, event->u.clientside.git_cookie, (IUnknown **)&event_iface); + if (FAILED(hr)) + return hr; + + hr = unregister_interface_in_git(event->u.clientside.git_cookie); + if (FAILED(hr)) + { + IWineUiaEvent_Release(event_iface); + return hr; + } + + /* + * We want the release of the event_iface proxy to set the reference + * count to 0, so we release our reference here. + */ + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + } + else + event_iface = &event->IWineUiaEvent_iface; + + hr = IWineUiaEvent_advise_events(event_iface, FALSE); + IWineUiaEvent_Release(event_iface); if (FAILED(hr)) WARN("advise_events failed with hr %#lx\n", hr);
@@ -541,11 +697,11 @@ static HRESULT uia_event_invoke(HUIANODE node, struct UiaEventArgs *args, struct BSTR tree_struct; HRESULT hr;
- hr = UiaGetUpdatedCache(node, &event->cache_req, NormalizeState_View, NULL, &out_req, + hr = UiaGetUpdatedCache(node, &event->u.clientside.cache_req, NormalizeState_View, NULL, &out_req, &tree_struct); if (SUCCEEDED(hr)) { - event->cback(args, out_req, tree_struct); + event->u.clientside.cback(args, out_req, tree_struct); SafeArrayDestroy(out_req); SysFreeString(tree_struct); } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 18b97b74e65..4bd9281d486 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -93,6 +93,11 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider * return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); }
+enum uia_event_type { + EVENT_TYPE_CLIENTSIDE, + EVENT_TYPE_SERVERSIDE, +}; + struct uia_event { IWineUiaEvent IWineUiaEvent_iface; @@ -111,8 +116,29 @@ struct uia_event struct uia_event_map_entry *event_map_entry; LONG event_defunct;
- struct UiaCacheRequest cache_req; - UiaEventCallback *cback; + int event_type; + union + { + struct { + struct UiaCacheRequest cache_req; + UiaEventCallback *cback; + + /* + * This is temporarily used to keep the MTA alive prior to our + * introduction of a dedicated event thread. + */ + CO_MTA_USAGE_COOKIE mta_cookie; + DWORD git_cookie; + } clientside; + struct { + /* + * Similar to the client MTA cookie, used to keep the provider + * thread alive as a temporary measure before introducing the + * event thread. + */ + IWineUiaNode *node; + } serverside; + } u; };
static inline void variant_init_bool(VARIANT *v, BOOL val) @@ -170,8 +196,10 @@ BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN; HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */ +HRESULT create_serverside_uia_event(struct uia_event **out_event) DECLSPEC_HIDDEN; HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) DECLSPEC_HIDDEN; +HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, struct uia_event *event) DECLSPEC_HIDDEN;
/* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_classes.idl | 2 +- dlls/uiautomationcore/uia_client.c | 22 +++++-- dlls/uiautomationcore/uia_event.c | 92 +++++++++++++++++++++++---- dlls/uiautomationcore/uia_private.h | 7 +- dlls/uiautomationcore/uia_provider.c | 1 - 5 files changed, 100 insertions(+), 24 deletions(-)
diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 3f8e17d3d72..74ea1b1a244 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -103,6 +103,6 @@ library UIA_wine_private HRESULT get_prop_val([in]const GUID *prop_guid, [out, retval]VARIANT *ret_val); HRESULT disconnect(); HRESULT get_hwnd([out, retval]ULONG *out_hwnd); - HRESULT attach_event([out, retval]IWineUiaEvent **ret_event); + HRESULT attach_event([in]long proc_id, [in]long event_cookie, [out, retval]IWineUiaEvent **ret_event); } } diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 5fe548bff18..6d468b68705 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -539,23 +539,29 @@ static HRESULT WINAPI uia_node_get_hwnd(IWineUiaNode *iface, ULONG *out_hwnd) return S_OK; }
-static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, IWineUiaEvent **ret_event) +static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, long proc_id, long event_cookie, + IWineUiaEvent **ret_event) { struct uia_node *node = impl_from_IWineUiaNode(iface); struct uia_event *event = NULL; HRESULT hr;
- TRACE("%p, %p\n", node, ret_event); + TRACE("%p, %ld, %ld, %p\n", node, proc_id, event_cookie, ret_event);
*ret_event = NULL; - hr = create_serverside_uia_event(&event); + hr = create_serverside_uia_event(&event, proc_id, event_cookie); if (FAILED(hr)) return hr;
+ /* Newly created serverside event. */ + if (hr == S_OK) + *ret_event = &event->IWineUiaEvent_iface; + hr = attach_event_to_node_provider(iface, 0, (HUIAEVENT)event); if (FAILED(hr)) { IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + *ret_event = NULL; return hr; }
@@ -563,9 +569,11 @@ static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, IWineUiaEvent * * Attach this nested node to the serverside event to keep the provider * thread alive. */ - IWineUiaNode_AddRef(iface); - event->u.serverside.node = iface; - *ret_event = &event->IWineUiaEvent_iface; + if (*ret_event) + { + IWineUiaNode_AddRef(iface); + event->u.serverside.node = iface; + }
return hr; } @@ -2282,7 +2290,7 @@ static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *if
TRACE("%p, %#Ix\n", iface, huiaevent);
- hr = IWineUiaNode_attach_event(prov->nested_node, &remote_event); + hr = IWineUiaNode_attach_event(prov->nested_node, GetCurrentProcessId(), event->event_cookie, &remote_event); if (FAILED(hr) || !remote_event) return hr;
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index d87d2c87a03..8abb4d02556 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -57,6 +57,10 @@ static struct uia_event_map { struct rb_tree event_map; LONG event_count; + + /* rb_tree for serverside events, sorted by PID/event cookie. */ + struct rb_tree serverside_event_map; + LONG serverside_event_count; } uia_event_map;
struct uia_event_map_entry @@ -77,6 +81,22 @@ struct uia_event_map_entry struct list events_list; };
+struct uia_event_identifier { + LONG event_cookie; + LONG proc_id; +}; + +static int uia_serverside_event_id_compare(const void *key, const struct rb_entry *entry) +{ + struct uia_event *event = RB_ENTRY_VALUE(entry, struct uia_event, u.serverside.serverside_event_entry); + struct uia_event_identifier *event_id = (struct uia_event_identifier *)key; + + if (event_id->proc_id != event->u.serverside.proc_id) + return (event_id->proc_id > event->u.serverside.proc_id) - (event_id->proc_id < event->u.serverside.proc_id); + else + return (event_id->event_cookie > event->event_cookie) - (event_id->event_cookie < event->event_cookie); +} + static CRITICAL_SECTION event_map_cs; static CRITICAL_SECTION_DEBUG event_map_cs_debug = { @@ -231,8 +251,15 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) if (event->u.clientside.mta_cookie) CoDecrementMTAUsage(event->u.clientside.mta_cookie); } - else if (event->u.serverside.node) - IWineUiaNode_Release(event->u.serverside.node); + else + { + EnterCriticalSection(&event_map_cs); + rb_remove(&uia_event_map.serverside_event_map, &event->u.serverside.serverside_event_entry); + uia_event_map.serverside_event_count--; + LeaveCriticalSection(&event_map_cs); + if (event->u.serverside.node) + IWineUiaNode_Release(event->u.serverside.node); + }
for (i = 0; i < event->event_advisers_count; i++) IWineUiaEventAdviser_Release(event->event_advisers[i]); @@ -292,8 +319,7 @@ static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface) return CONTAINING_RECORD(iface, struct uia_event, IWineUiaEvent_iface); }
-static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int scope, UiaEventCallback *cback, - SAFEARRAY *runtime_id) +static HRESULT create_uia_event(struct uia_event **out_event, LONG event_cookie, int event_type) { struct uia_event *event = heap_alloc_zero(sizeof(*event));
@@ -303,30 +329,68 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int
event->IWineUiaEvent_iface.lpVtbl = &uia_event_vtbl; event->ref = 1; + event->event_cookie = event_cookie; + event->event_type = event_type; + *out_event = event; + + return S_OK; +} + +static HRESULT create_clientside_uia_event(struct uia_event **out_event, int event_id, int scope, UiaEventCallback *cback, + SAFEARRAY *runtime_id) +{ + struct uia_event *event = NULL; + static LONG next_event_cookie; + HRESULT hr; + + *out_event = NULL; + hr = create_uia_event(&event, InterlockedIncrement(&next_event_cookie), EVENT_TYPE_CLIENTSIDE); + if (FAILED(hr)) + return hr; + event->runtime_id = runtime_id; event->event_id = event_id; event->scope = scope; event->u.clientside.cback = cback; - event->event_type = EVENT_TYPE_CLIENTSIDE;
*out_event = event; return S_OK; }
-HRESULT create_serverside_uia_event(struct uia_event **out_event) +HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_id, LONG event_cookie) { - struct uia_event *event = heap_alloc_zero(sizeof(*event)); + struct uia_event_identifier event_identifier = { event_cookie, process_id }; + struct rb_entry *rb_entry; + struct uia_event *event; + HRESULT hr = S_OK;
+ /* + * Attempt to lookup an existing event for this PID/event_cookie. If there + * is one, return S_FALSE. + */ *out_event = NULL; - if (!event) - return E_OUTOFMEMORY; + EnterCriticalSection(&event_map_cs); + if (uia_event_map.serverside_event_count && (rb_entry = rb_get(&uia_event_map.serverside_event_map, &event_identifier))) + { + *out_event = RB_ENTRY_VALUE(rb_entry, struct uia_event, u.serverside.serverside_event_entry); + hr = S_FALSE; + goto exit; + }
- event->IWineUiaEvent_iface.lpVtbl = &uia_event_vtbl; - event->ref = 1; - event->event_type = EVENT_TYPE_SERVERSIDE; + hr = create_uia_event(&event, event_cookie, EVENT_TYPE_SERVERSIDE); + if (FAILED(hr)) + goto exit;
+ event->u.serverside.proc_id = process_id; + uia_event_map.serverside_event_count++; + if (uia_event_map.serverside_event_count == 1) + rb_init(&uia_event_map.serverside_event_map, uia_serverside_event_id_compare); + rb_put(&uia_event_map.serverside_event_map, &event_identifier, &event->u.serverside.serverside_event_entry); *out_event = event; - return S_OK; + +exit: + LeaveCriticalSection(&event_map_cs); + return hr; }
static HRESULT uia_event_add_event_adviser(IWineUiaEventAdviser *adviser, struct uia_event *event) @@ -614,7 +678,7 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback if (FAILED(hr)) return hr;
- hr = create_uia_event(&event, event_id, scope, callback, sa); + hr = create_clientside_uia_event(&event, event_id, scope, callback, sa); if (FAILED(hr)) { SafeArrayDestroy(sa); diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 4bd9281d486..c4f8f46abb0 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -21,6 +21,7 @@ #include "uiautomation.h" #include "uia_classes.h" #include "wine/list.h" +#include "wine/rbtree.h" #include "wine/heap.h"
extern HMODULE huia_module DECLSPEC_HIDDEN; @@ -116,6 +117,7 @@ struct uia_event struct uia_event_map_entry *event_map_entry; LONG event_defunct;
+ LONG event_cookie; int event_type; union { @@ -137,6 +139,9 @@ struct uia_event * event thread. */ IWineUiaNode *node; + + struct rb_entry serverside_event_entry; + LONG proc_id; } serverside; } u; }; @@ -196,7 +201,7 @@ BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN; HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_event.c */ -HRESULT create_serverside_uia_event(struct uia_event **out_event) 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; HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, struct uia_event *event) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index a00e9312ff7..faa7e2cb7fa 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -20,7 +20,6 @@ #include "ocidl.h"
#include "wine/debug.h" -#include "wine/rbtree.h" #include "initguid.h" #include "wine/iaccessible2.h"
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 4 +- dlls/uiautomationcore/uia_classes.idl | 2 + dlls/uiautomationcore/uia_event.c | 65 +++++++++++++++++++++- dlls/uiautomationcore/uia_private.h | 1 + 4 files changed, 67 insertions(+), 5 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 6d0e6f7c557..13f481a670e 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -14047,7 +14047,7 @@ static void test_UiaAddEvent_client_proc(void) CHECK_CALLED_MULTI(prov_callback_proxy, 3); }
- post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, FALSE);
/* * Starts navigation in server process, then completes navigation in the @@ -14071,7 +14071,7 @@ static void test_UiaAddEvent_client_proc(void)
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, FALSE);
/* * Register an event on a node that won't require any navigation to reach. diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 74ea1b1a244..651df86d9d2 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -73,6 +73,8 @@ library UIA_wine_private interface IWineUiaEvent : IUnknown { HRESULT advise_events([in]BOOL advise_added); + HRESULT set_event_data([in]const GUID *event_guid, [in]long scope, [in]VARIANT runtime_id, + [in]IWineUiaEvent *event_iface); }
[ diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 8abb4d02556..2cb2cff9ae2 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -257,6 +257,8 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) rb_remove(&uia_event_map.serverside_event_map, &event->u.serverside.serverside_event_entry); uia_event_map.serverside_event_count--; LeaveCriticalSection(&event_map_cs); + if (event->u.serverside.event_iface) + IWineUiaEvent_Release(event->u.serverside.event_iface); if (event->u.serverside.node) IWineUiaNode_Release(event->u.serverside.node); } @@ -292,7 +294,9 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_ if (!advise_added) { InterlockedIncrement(&event->event_defunct); - uia_event_map_entry_release(event->event_map_entry); + /* FIXME: Remove this check once we can raise serverside events. */ + if (event->event_type == EVENT_TYPE_CLIENTSIDE) + uia_event_map_entry_release(event->event_map_entry); event->event_map_entry = NULL;
for (i = 0; i < event->event_advisers_count; i++) @@ -304,11 +308,40 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_ return S_OK; }
+static HRESULT WINAPI uia_event_set_event_data(IWineUiaEvent *iface, const GUID *event_guid, long scope, + VARIANT runtime_id, IWineUiaEvent *event_iface) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + + TRACE("%p, %s, %ld, %s, %p\n", event, debugstr_guid(event_guid), scope, debugstr_variant(&runtime_id), event_iface); + + assert(event->event_type == EVENT_TYPE_SERVERSIDE); + + event->event_id = UiaLookupId(AutomationIdentifierType_Event, event_guid); + event->scope = scope; + if (V_VT(&runtime_id) == (VT_I4 | VT_ARRAY)) + { + HRESULT hr; + + hr = SafeArrayCopy(V_ARRAY(&runtime_id), &event->runtime_id); + if (FAILED(hr)) + { + WARN("Failed to copy runtime id, hr %#lx\n", hr); + return hr; + } + } + event->u.serverside.event_iface = event_iface; + IWineUiaEvent_AddRef(event_iface); + + return S_OK; +} + static const IWineUiaEventVtbl uia_event_vtbl = { uia_event_QueryInterface, uia_event_AddRef, uia_event_Release, uia_event_advise_events, + uia_event_set_event_data, };
static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface) @@ -598,8 +631,34 @@ static ULONG WINAPI uia_serverside_event_adviser_Release(IWineUiaEventAdviser *i
static HRESULT WINAPI uia_serverside_event_adviser_advise(IWineUiaEventAdviser *iface, BOOL advise_added, LONG_PTR huiaevent) { - FIXME("%p, %d, %#Ix: stub\n", iface, advise_added, huiaevent); - return S_OK; + struct uia_serverside_event_adviser *adv_events = impl_from_serverside_IWineUiaEventAdviser(iface); + struct uia_event *event_data = (struct uia_event *)huiaevent; + HRESULT hr; + + TRACE("%p, %d, %#Ix\n", adv_events, advise_added, huiaevent); + + if (advise_added) + { + const struct uia_event_info *event_info = uia_event_info_from_id(event_data->event_id); + VARIANT v; + + VariantInit(&v); + if (event_data->runtime_id) + { + V_VT(&v) = VT_I4 | VT_ARRAY; + V_ARRAY(&v) = event_data->runtime_id; + } + + hr = IWineUiaEvent_set_event_data(adv_events->event_iface, event_info->guid, event_data->scope, v, + &event_data->IWineUiaEvent_iface); + if (FAILED(hr)) + { + WARN("Failed to set event data on serverside event, hr %#lx\n", hr); + return hr; + } + } + + return IWineUiaEvent_advise_events(adv_events->event_iface, advise_added); }
static const IWineUiaEventAdviserVtbl uia_serverside_event_adviser_vtbl = { diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index c4f8f46abb0..60830c7c8a5 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -140,6 +140,7 @@ struct uia_event */ IWineUiaNode *node;
+ IWineUiaEvent *event_iface; struct rb_entry serverside_event_entry; LONG proc_id; } serverside;
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 | 2 +- dlls/uiautomationcore/uia_client.c | 19 +++-- dlls/uiautomationcore/uia_event.c | 95 ++++++++++++++++------ 4 files changed, 88 insertions(+), 44 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 13f481a670e..f7f16927b38 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13952,11 +13952,11 @@ static void test_UiaAddEvent_client_proc(void) SET_EXPECT_MULTI(prov_callback_nonclient, 2); SET_EXPECT_MULTI(prov_callback_proxy, 3); hr = UiaEventAddWindow(event, hwnd); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED(prov_callback_nonclient); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); todo_wine CHECK_CALLED(prov_callback_proxy); - post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_ADDED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, FALSE);
/* Successfully raise event. */ GetWindowThreadProcessId(hwnd, &pid); @@ -14000,7 +14000,7 @@ static void test_UiaAddEvent_client_proc(void)
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, TRUE); + post_event_message(hwnd, WM_UIA_TEST_CHECK_EVENT_ADVISE_REMOVED, UIA_AutomationFocusChangedEventId, PROVIDER_ID, FALSE);
PostMessageW(hwnd, WM_UIA_TEST_RESET_EVENT_PROVIDERS, 0, 0); post_event_message(hwnd, WM_UIA_TEST_SET_EVENT_PROVIDER_DATA, HandleToUlong(hwnd), PROVIDER_ID, @@ -14093,9 +14093,9 @@ static void test_UiaAddEvent_client_proc(void) SET_EXPECT_MULTI(prov_callback_nonclient, 2); SET_EXPECT_MULTI(prov_callback_proxy, 3); hr = UiaEventAddWindow(event, hwnd); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine CHECK_CALLED(prov_callback_base_hwnd); - todo_wine CHECK_CALLED(prov_callback_nonclient); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); todo_wine CHECK_CALLED(prov_callback_proxy);
/* Wrong runtime ID, no match. */ diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 651df86d9d2..25417d6c549 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -72,7 +72,7 @@ library UIA_wine_private ] interface IWineUiaEvent : IUnknown { - HRESULT advise_events([in]BOOL advise_added); + HRESULT advise_events([in]BOOL advise_added, [in]long adviser_start_idx); HRESULT set_event_data([in]const GUID *event_guid, [in]long scope, [in]VARIANT runtime_id, [in]IWineUiaEvent *event_iface); } diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 6d468b68705..e5cdec27e0b 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -544,6 +544,7 @@ static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, long proc_id, l { struct uia_node *node = impl_from_IWineUiaNode(iface); struct uia_event *event = NULL; + int old_event_advisers_count; HRESULT hr;
TRACE("%p, %ld, %ld, %p\n", node, proc_id, event_cookie, ret_event); @@ -557,6 +558,7 @@ static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, long proc_id, l if (hr == S_OK) *ret_event = &event->IWineUiaEvent_iface;
+ old_event_advisers_count = event->event_advisers_count; hr = attach_event_to_node_provider(iface, 0, (HUIAEVENT)event); if (FAILED(hr)) { @@ -575,6 +577,14 @@ static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, long proc_id, l event->u.serverside.node = iface; }
+ /* + * Pre-existing serverside event that has already had its initial + * advise call and gotten event data - if we've got new advisers, we need + * to advise them here. + */ + if (!(*ret_event) && event->event_id && (event->event_advisers_count != old_event_advisers_count)) + hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, TRUE, old_event_advisers_count); + return hr; }
@@ -3542,15 +3552,6 @@ exit: return hr; }
-/*********************************************************************** - * UiaEventAddWindow (uiautomationcore.@) - */ -HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd) -{ - FIXME("(%p, %p): stub\n", huiaevent, hwnd); - return E_NOTIMPL; -} - /*********************************************************************** * UiaEventRemoveWindow (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 2cb2cff9ae2..dd3578e7bd5 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -272,15 +272,15 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) return ref; }
-static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_added) +static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_added, long adviser_start_idx) { struct uia_event *event = impl_from_IWineUiaEvent(iface); HRESULT hr; int i;
- TRACE("%p, %d\n", event, advise_added); + TRACE("%p, %d, %ld\n", event, advise_added, adviser_start_idx);
- for (i = 0; i < event->event_advisers_count; i++) + for (i = adviser_start_idx; i < event->event_advisers_count; i++) { hr = IWineUiaEventAdviser_advise(event->event_advisers[i], advise_added, (UINT_PTR)event); if (FAILED(hr)) @@ -658,7 +658,7 @@ static HRESULT WINAPI uia_serverside_event_adviser_advise(IWineUiaEventAdviser * } }
- return IWineUiaEvent_advise_events(adv_events->event_iface, advise_added); + return IWineUiaEvent_advise_events(adv_events->event_iface, advise_added, 0); }
static const IWineUiaEventAdviserVtbl uia_serverside_event_adviser_vtbl = { @@ -706,6 +706,65 @@ HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, return hr; }
+static HRESULT uia_event_advise(struct uia_event *event, BOOL advise_added, long start_idx) +{ + IWineUiaEvent *event_iface; + HRESULT hr; + + if (event->u.clientside.git_cookie) + { + hr = get_interface_in_git(&IID_IWineUiaEvent, event->u.clientside.git_cookie, + (IUnknown **)&event_iface); + if (FAILED(hr)) + return hr; + } + else + { + event_iface = &event->IWineUiaEvent_iface; + IWineUiaEvent_AddRef(event_iface); + } + + hr = IWineUiaEvent_advise_events(event_iface, advise_added, start_idx); + IWineUiaEvent_Release(event_iface); + + return hr; +} + +/*********************************************************************** + * UiaEventAddWindow (uiautomationcore.@) + */ +HRESULT WINAPI UiaEventAddWindow(HUIAEVENT huiaevent, HWND hwnd) +{ + struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); + int old_event_advisers_count; + HUIANODE node; + HRESULT hr; + + TRACE("(%p, %p)\n", huiaevent, hwnd); + + if (!event) + return E_INVALIDARG; + + assert(event->event_type == EVENT_TYPE_CLIENTSIDE); + + hr = UiaNodeFromHandle(hwnd, &node); + if (FAILED(hr)) + return hr; + + old_event_advisers_count = event->event_advisers_count; + hr = attach_event_to_uia_node(node, event); + if (FAILED(hr)) + goto exit; + + if (event->event_advisers_count != old_event_advisers_count) + hr = uia_event_advise(event, TRUE, old_event_advisers_count); + +exit: + UiaNodeRelease(node); + + return hr; +} + /*********************************************************************** * UiaAddEvent (uiautomationcore.@) */ @@ -752,7 +811,7 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback if (FAILED(hr)) goto exit;
- hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, TRUE); + hr = uia_event_advise(event, TRUE, 0); if (FAILED(hr)) goto exit;
@@ -775,7 +834,6 @@ exit: HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) { struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); - IWineUiaEvent *event_iface; HRESULT hr;
TRACE("(%p)\n", event); @@ -784,33 +842,18 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) return E_INVALIDARG;
assert(event->event_type == EVENT_TYPE_CLIENTSIDE); + hr = uia_event_advise(event, FALSE, 0); + if (FAILED(hr)) + return hr; + if (event->u.clientside.git_cookie) { - hr = get_interface_in_git(&IID_IWineUiaEvent, event->u.clientside.git_cookie, (IUnknown **)&event_iface); - if (FAILED(hr)) - return hr; - hr = unregister_interface_in_git(event->u.clientside.git_cookie); if (FAILED(hr)) - { - IWineUiaEvent_Release(event_iface); return hr; - } - - /* - * We want the release of the event_iface proxy to set the reference - * count to 0, so we release our reference here. - */ - IWineUiaEvent_Release(&event->IWineUiaEvent_iface); } - else - event_iface = &event->IWineUiaEvent_iface; - - hr = IWineUiaEvent_advise_events(event_iface, FALSE); - IWineUiaEvent_Release(event_iface); - if (FAILED(hr)) - WARN("advise_events failed with hr %#lx\n", hr);
+ IWineUiaEvent_Release(&event->IWineUiaEvent_iface); return S_OK; }
On Mon Jun 19 09:58:43 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/3066/diffs?diff_id=52547&start_sha=c8c056d39a15a9e2dcae14aff82d1b547583cd7b#14d9c4bde8659a60721a64739d545cd66adf72c2_262_262)
Good catch, added a check for this in the current revision.
On Mon Jun 19 09:58:44 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/3066/diffs?diff_id=52547&start_sha=c8c056d39a15a9e2dcae14aff82d1b547583cd7b#14d9c4bde8659a60721a64739d545cd66adf72c2_383_384)
Good catch, should be fixed now in the current revision. :)
On Mon Jun 19 09:58:44 2023 +0000, Connor McAdams wrote:
changed this line in [version 2 of the diff](/wine/wine/-/merge_requests/3066/diffs?diff_id=52547&start_sha=c8c056d39a15a9e2dcae14aff82d1b547583cd7b#14d9c4bde8659a60721a64739d545cd66adf72c2_434_433)
Changed this in the current revision, I think it's better but I'll let you mark it as resolved just in case it's not.
This merge request was approved by Esme Povirk.