-- v2: uiautomationcore: Add support for invoking serverside event callbacks. uiautomationcore: Create a new node when raising serverside events. uiautomationcore: Add partial support for raising serverside events. uiautomationcore: Introduce custom uia_event_args structure. uiautomationcore: Introduce UI Automation event thread.
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 10 --- dlls/uiautomationcore/uia_event.c | 134 ++++++++++++++++++++++++++-- dlls/uiautomationcore/uia_private.h | 13 +-- 3 files changed, 127 insertions(+), 30 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index e5cdec27e0b..25e76db8128 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -567,16 +567,6 @@ static HRESULT WINAPI uia_node_attach_event(IWineUiaNode *iface, long proc_id, l return hr; }
- /* - * Attach this nested node to the serverside event to keep the provider - * thread alive. - */ - if (*ret_event) - { - IWineUiaNode_AddRef(iface); - 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 diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index dd3578e7bd5..df3f3b9c3a8 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -199,6 +199,119 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) } }
+/* + * UI Automation event thread. + */ +struct uia_event_thread +{ + HANDLE hthread; + HWND hwnd; + LONG ref; +}; + +#define WM_UIA_EVENT_THREAD_STOP (WM_USER + 1) +static struct uia_event_thread event_thread; +static CRITICAL_SECTION event_thread_cs; +static CRITICAL_SECTION_DEBUG event_thread_cs_debug = +{ + 0, 0, &event_thread_cs, + { &event_thread_cs_debug.ProcessLocksList, &event_thread_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": event_thread_cs") } +}; +static CRITICAL_SECTION event_thread_cs = { &event_thread_cs_debug, -1, 0, 0, 0, 0 }; + +static DWORD WINAPI uia_event_thread_proc(void *arg) +{ + HANDLE initialized_event = arg; + HWND hwnd; + MSG msg; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + hwnd = CreateWindowW(L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + if (!hwnd) + { + WARN("CreateWindow failed: %ld\n", GetLastError()); + CoUninitialize(); + FreeLibraryAndExitThread(huia_module, 1); + } + + event_thread.hwnd = hwnd; + + /* Initialization complete, thread can now process window messages. */ + SetEvent(initialized_event); + TRACE("Event thread started.\n"); + while (GetMessageW(&msg, NULL, 0, 0)) + { + if ((msg.hwnd == hwnd) && (msg.message == WM_UIA_EVENT_THREAD_STOP)) + break; + + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + TRACE("Shutting down UI Automation event thread.\n"); + + DestroyWindow(hwnd); + CoUninitialize(); + FreeLibraryAndExitThread(huia_module, 0); +} + +static BOOL uia_start_event_thread(void) +{ + BOOL started = TRUE; + + EnterCriticalSection(&event_thread_cs); + if (++event_thread.ref == 1) + { + HANDLE ready_event = NULL; + HANDLE events[2]; + HMODULE hmodule; + DWORD wait_obj; + + /* Increment DLL reference count. */ + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (const WCHAR *)uia_start_event_thread, &hmodule); + + events[0] = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!(event_thread.hthread = CreateThread(NULL, 0, uia_event_thread_proc, + ready_event, 0, NULL))) + { + FreeLibrary(hmodule); + started = FALSE; + goto exit; + } + + events[1] = event_thread.hthread; + wait_obj = WaitForMultipleObjects(2, events, FALSE, INFINITE); + if (wait_obj != WAIT_OBJECT_0) + { + CloseHandle(event_thread.hthread); + started = FALSE; + } + +exit: + if (ready_event) + CloseHandle(ready_event); + if (!started) + memset(&event_thread, 0, sizeof(event_thread)); + } + + LeaveCriticalSection(&event_thread_cs); + return started; +} + +static void uia_stop_event_thread(void) +{ + EnterCriticalSection(&event_thread_cs); + if (!--event_thread.ref) + { + PostMessageW(event_thread.hwnd, WM_UIA_EVENT_THREAD_STOP, 0, 0); + CloseHandle(event_thread.hthread); + memset(&event_thread, 0, sizeof(event_thread)); + } + LeaveCriticalSection(&event_thread_cs); +} + /* * IWineUiaEvent interface. */ @@ -248,8 +361,8 @@ 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.mta_cookie) - CoDecrementMTAUsage(event->u.clientside.mta_cookie); + if (event->u.clientside.git_cookie) + uia_stop_event_thread(); } else { @@ -259,8 +372,7 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) 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); + uia_stop_event_thread(); }
for (i = 0; i < event->event_advisers_count; i++) @@ -414,6 +526,13 @@ HRESULT create_serverside_uia_event(struct uia_event **out_event, LONG process_i if (FAILED(hr)) goto exit;
+ if (!uia_start_event_thread()) + { + heap_free(event); + hr = E_FAIL; + goto exit; + } + event->u.serverside.proc_id = process_id; uia_event_map.serverside_event_count++; if (uia_event_map.serverside_event_count == 1) @@ -679,15 +798,14 @@ HRESULT uia_event_add_serverside_event_adviser(IWineUiaEvent *serverside_event, */ if (!event->u.clientside.git_cookie) { - hr = CoIncrementMTAUsage(&event->u.clientside.mta_cookie); - if (FAILED(hr)) - return hr; + if (!uia_start_event_thread()) + return E_FAIL;
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); + uia_stop_event_thread(); return hr; } } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 60830c7c8a5..5aeab22d3ee 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -125,22 +125,11 @@ struct uia_event 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; - 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/uia_event.c | 52 ++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 7 deletions(-)
diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index df3f3b9c3a8..3516ea77cb8 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -199,6 +199,38 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) } }
+/* + * Functions for struct uia_event_args, a reference counted structure + * used to store event arguments. This is necessary for serverside events + * as they're raised on a background thread after the event raising + * function has returned. + */ +struct uia_event_args +{ + struct UiaEventArgs simple_args; + LONG ref; +}; + +static struct uia_event_args *create_uia_event_args(const struct uia_event_info *event_info) +{ + struct uia_event_args *args = heap_alloc_zero(sizeof(*args)); + + if (!args) + return NULL; + + args->simple_args.Type = event_info->event_arg_type; + args->simple_args.EventId = event_info->event_id; + args->ref = 1; + + return args; +} + +static void uia_event_args_release(struct uia_event_args *args) +{ + if (!InterlockedDecrement(&args->ref)) + heap_free(args); +} + /* * UI Automation event thread. */ @@ -975,7 +1007,7 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) return S_OK; }
-static HRESULT uia_event_invoke(HUIANODE node, struct UiaEventArgs *args, struct uia_event *event) +static HRESULT uia_event_invoke(HUIANODE node, struct uia_event_args *args, struct uia_event *event) { SAFEARRAY *out_req; BSTR tree_struct; @@ -985,7 +1017,7 @@ static HRESULT uia_event_invoke(HUIANODE node, struct UiaEventArgs *args, struct &tree_struct); if (SUCCEEDED(hr)) { - event->u.clientside.cback(args, out_req, tree_struct); + event->u.clientside.cback(&args->simple_args, out_req, tree_struct); SafeArrayDestroy(out_req); SysFreeString(tree_struct); } @@ -996,7 +1028,8 @@ static HRESULT uia_event_invoke(HUIANODE node, struct UiaEventArgs *args, struct /* * Check if the provider that raised the event matches this particular event. */ -static HRESULT uia_event_check_match(HUIANODE node, SAFEARRAY *rt_id, struct UiaEventArgs *args, struct uia_event *event) +static HRESULT uia_event_check_match(HUIANODE node, SAFEARRAY *rt_id, struct uia_event_args *args, + struct uia_event *event) { struct UiaPropertyCondition prop_cond = { ConditionType_Property, UIA_RuntimeIdPropertyId }; struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); @@ -1055,7 +1088,7 @@ static HRESULT uia_event_check_match(HUIANODE node, SAFEARRAY *rt_id, struct Uia return hr; }
-static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct UiaEventArgs *args) +static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct uia_event_args *args) { struct uia_event_map_entry *event_entry; enum ProviderOptions prov_opts = 0; @@ -1069,7 +1102,7 @@ static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct UiaEven return hr;
EnterCriticalSection(&event_map_cs); - if ((event_entry = uia_get_event_map_entry_for_event(args->EventId))) + if ((event_entry = uia_get_event_map_entry_for_event(args->simple_args.EventId))) InterlockedIncrement(&event_entry->refs); LeaveCriticalSection(&event_map_cs);
@@ -1120,7 +1153,7 @@ static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct UiaEven HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *elprov, EVENTID id) { const struct uia_event_info *event_info = uia_event_info_from_id(id); - struct UiaEventArgs args = { EventArgsType_Simple, id }; + struct uia_event_args *args; HRESULT hr;
TRACE("(%p, %d)\n", elprov, id); @@ -1138,7 +1171,12 @@ HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *elprov, EVENTI return S_OK; }
- hr = uia_raise_event(elprov, &args); + args = create_uia_event_args(event_info); + if (!args) + return E_OUTOFMEMORY; + + hr = uia_raise_event(elprov, args); + uia_event_args_release(args); if (FAILED(hr)) return hr;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 4 +- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 14 +- dlls/uiautomationcore/uia_event.c | 195 +++++++++++++++++++-- dlls/uiautomationcore/uia_private.h | 12 ++ 5 files changed, 196 insertions(+), 30 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f7f16927b38..788eb1a4eba 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13976,8 +13976,8 @@ static void test_UiaAddEvent_client_proc(void) 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); + CHECK_CALLED(prov_callback_base_hwnd); + CHECK_CALLED(prov_callback_nonclient); todo_wine CHECK_CALLED(prov_callback_proxy);
/* diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 25417d6c549..8eb9998f945 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -75,6 +75,7 @@ library UIA_wine_private 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); + HRESULT raise_event([in]VARIANT in_node); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 25e76db8128..492ca0e52de 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1266,17 +1266,6 @@ static ULONG WINAPI uia_provider_Release(IWineUiaProvider *iface) return ref; }
-static void get_variant_for_node(HUIANODE node, VARIANT *v) -{ -#ifdef _WIN64 - V_VT(v) = VT_I8; - V_I8(v) = (UINT64)node; -#else - V_VT(v) = VT_I4; - V_I4(v) = (UINT32)node; -#endif -} - static HRESULT get_variant_for_elprov_node(IRawElementProviderSimple *elprov, BOOL out_nested, VARIANT *v) { @@ -2171,7 +2160,6 @@ static ULONG WINAPI uia_nested_node_provider_Release(IWineUiaProvider *iface) return ref; }
-static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode); static HRESULT WINAPI uia_nested_node_provider_get_prop_val(IWineUiaProvider *iface, const struct uia_prop_info *prop_info, VARIANT *ret_val) { @@ -2430,7 +2418,7 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU return S_OK; }
-static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) +HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) { struct uia_node *node; HRESULT hr; diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 3516ea77cb8..ee65bb7b893 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -79,6 +79,7 @@ struct uia_event_map_entry * ignored. */ struct list events_list; + struct list serverside_events_list; };
struct uia_event_identifier { @@ -146,6 +147,7 @@ static HRESULT uia_event_map_add_event(struct uia_event *event)
event_entry->event_id = event->event_id; list_init(&event_entry->events_list); + list_init(&event_entry->serverside_events_list);
if (!uia_event_map.event_count) rb_init(&uia_event_map.event_map, uia_event_map_id_compare); @@ -154,7 +156,10 @@ static HRESULT uia_event_map_add_event(struct uia_event *event) }
IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); - list_add_head(&event_entry->events_list, &event->event_list_entry); + if (event->event_type == EVENT_TYPE_SERVERSIDE) + list_add_head(&event_entry->serverside_events_list, &event->event_list_entry); + else + list_add_head(&event_entry->events_list, &event->event_list_entry); InterlockedIncrement(&event_entry->refs);
event->event_map_entry = event_entry; @@ -195,6 +200,13 @@ static void uia_event_map_entry_release(struct uia_event_map_entry *entry) IWineUiaEvent_Release(&event->IWineUiaEvent_iface); }
+ LIST_FOR_EACH_SAFE(cursor, cursor2, &entry->serverside_events_list) + { + struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + } + heap_free(entry); } } @@ -239,9 +251,12 @@ struct uia_event_thread HANDLE hthread; HWND hwnd; LONG ref; + + struct list *event_queue; };
#define WM_UIA_EVENT_THREAD_STOP (WM_USER + 1) +#define WM_UIA_EVENT_THREAD_PROCESS_QUEUE (WM_USER + 2) static struct uia_event_thread event_thread; static CRITICAL_SECTION event_thread_cs; static CRITICAL_SECTION_DEBUG event_thread_cs_debug = @@ -252,12 +267,96 @@ static CRITICAL_SECTION_DEBUG event_thread_cs_debug = }; static CRITICAL_SECTION event_thread_cs = { &event_thread_cs_debug, -1, 0, 0, 0, 0 };
+struct uia_queue_event +{ + struct list event_queue_entry; + + struct uia_event_args *args; + struct uia_event *event; + VARIANT node; +}; + +static HRESULT uia_event_queue_post_event(struct uia_event *event, VARIANT node, struct uia_event_args *args) +{ + struct uia_queue_event *queue_event = heap_alloc_zero(sizeof(*queue_event)); + + if (!queue_event) + return E_OUTOFMEMORY; + + IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); + InterlockedIncrement(&args->ref); + queue_event->args = args; + queue_event->event = event; + queue_event->node = node; + + EnterCriticalSection(&event_thread_cs); + + assert(event_thread.event_queue); + list_add_tail(event_thread.event_queue, &queue_event->event_queue_entry); + PostMessageW(event_thread.hwnd, WM_UIA_EVENT_THREAD_PROCESS_QUEUE, 0, 0); + + LeaveCriticalSection(&event_thread_cs); + + return S_OK; +} + +static struct uia_queue_event *uia_event_queue_pop(struct list *event_queue) +{ + struct uia_queue_event *queue_event = NULL; + + EnterCriticalSection(&event_thread_cs); + + if (!list_empty(event_queue)) + { + queue_event = LIST_ENTRY(list_head(event_queue), struct uia_queue_event, event_queue_entry); + list_remove(list_head(event_queue)); + } + + LeaveCriticalSection(&event_thread_cs); + return queue_event; +} + +static void uia_event_thread_process_queue(struct list *event_queue) +{ + while (1) + { + struct uia_queue_event *event; + HUIANODE node; + HRESULT hr; + + if (!(event = uia_event_queue_pop(event_queue))) + break; + + hr = UiaHUiaNodeFromVariant(&event->node, &node); + if (SUCCEEDED(hr)) + { + LRESULT lr; + VARIANT v; + + if ((lr = uia_lresult_from_node(node))) + { + V_VT(&v) = VT_I4; + V_I4(&v) = lr; + hr = IWineUiaEvent_raise_event(event->event->u.serverside.event_iface, v); + if (FAILED(hr)) + WARN("IWineUiaEvent_raise_event failed with hr %#lx\n", hr); + } + } + + uia_event_args_release(event->args); + IWineUiaEvent_Release(&event->event->IWineUiaEvent_iface); + heap_free(event); + } +} + static DWORD WINAPI uia_event_thread_proc(void *arg) { HANDLE initialized_event = arg; + struct list event_queue; HWND hwnd; MSG msg;
+ list_init(&event_queue); CoInitializeEx(NULL, COINIT_MULTITHREADED); hwnd = CreateWindowW(L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); if (!hwnd) @@ -268,14 +367,20 @@ static DWORD WINAPI uia_event_thread_proc(void *arg) }
event_thread.hwnd = hwnd; + event_thread.event_queue = &event_queue;
/* Initialization complete, thread can now process window messages. */ SetEvent(initialized_event); TRACE("Event thread started.\n"); while (GetMessageW(&msg, NULL, 0, 0)) { - if ((msg.hwnd == hwnd) && (msg.message == WM_UIA_EVENT_THREAD_STOP)) - break; + if ((msg.hwnd == hwnd) && ((msg.message == WM_UIA_EVENT_THREAD_STOP) || + (msg.message == WM_UIA_EVENT_THREAD_PROCESS_QUEUE))) + { + uia_event_thread_process_queue(&event_queue); + if (msg.message == WM_UIA_EVENT_THREAD_STOP) + break; + }
TranslateMessage(&msg); DispatchMessageW(&msg); @@ -431,6 +536,17 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_ return hr; }
+ /* + * First call to advise events on a serverside provider, add it to the + * events list so it can be raised. + */ + if (!adviser_start_idx && advise_added && event->event_type == EVENT_TYPE_SERVERSIDE) + { + hr = uia_event_map_add_event(event); + if (FAILED(hr)) + WARN("Failed to add event to event map, hr %#lx\n", hr); + } + /* * Once we've advised of removal, no need to keep the advisers around. * We can also release our reference to the event map. @@ -438,9 +554,7 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_ if (!advise_added) { InterlockedIncrement(&event->event_defunct); - /* 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); + uia_event_map_entry_release(event->event_map_entry); event->event_map_entry = NULL;
for (i = 0; i < event->event_advisers_count; i++) @@ -480,12 +594,31 @@ static HRESULT WINAPI uia_event_set_event_data(IWineUiaEvent *iface, const GUID return S_OK; }
+static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_node) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + HUIANODE node; + HRESULT hr; + + FIXME("%p, %s: stub\n", iface, debugstr_variant(&in_node)); + + assert(event->event_type != EVENT_TYPE_SERVERSIDE); + + hr = uia_node_from_lresult((LRESULT)V_I4(&in_node), &node); + if (FAILED(hr)) + return hr; + + UiaNodeRelease(node); + 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, + uia_event_raise_event, };
static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface) @@ -1009,17 +1142,31 @@ HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent)
static HRESULT uia_event_invoke(HUIANODE node, struct uia_event_args *args, struct uia_event *event) { - SAFEARRAY *out_req; - BSTR tree_struct; - HRESULT hr; + HRESULT hr = S_OK;
- hr = UiaGetUpdatedCache(node, &event->u.clientside.cache_req, NormalizeState_View, NULL, &out_req, - &tree_struct); - if (SUCCEEDED(hr)) + if (event->event_type == EVENT_TYPE_CLIENTSIDE) + { + SAFEARRAY *out_req; + BSTR tree_struct; + + hr = UiaGetUpdatedCache(node, &event->u.clientside.cache_req, NormalizeState_View, NULL, &out_req, + &tree_struct); + if (SUCCEEDED(hr)) + { + event->u.clientside.cback(&args->simple_args, out_req, tree_struct); + SafeArrayDestroy(out_req); + SysFreeString(tree_struct); + } + } + else { - event->u.clientside.cback(&args->simple_args, out_req, tree_struct); - SafeArrayDestroy(out_req); - SysFreeString(tree_struct); + VARIANT v; + + get_variant_for_node(node, &v); + IWineUiaNode_AddRef((IWineUiaNode *)node); + hr = uia_event_queue_post_event(event, v, args); + if (FAILED(hr)) + UiaNodeRelease(node); }
return hr; @@ -1056,6 +1203,12 @@ static HRESULT uia_event_check_match(HUIANODE node, SAFEARRAY *rt_id, struct uia if (!(event->scope & (TreeScope_Descendants | TreeScope_Children))) return S_OK;
+ if (event->event_type == EVENT_TYPE_SERVERSIDE) + { + FIXME("Matching serverside events through navigation currently unimplemented\n"); + return S_OK; + } + V_VT(&prop_cond.Value) = VT_I4 | VT_ARRAY; V_ARRAY(&prop_cond.Value) = event->runtime_id;
@@ -1140,6 +1293,18 @@ static HRESULT uia_raise_event(IRawElementProviderSimple *elprov, struct uia_eve break; }
+ if (prov_opts & ProviderOptions_ServerSideProvider) + { + LIST_FOR_EACH_SAFE(cursor, cursor2, &event_entry->serverside_events_list) + { + struct uia_event *event = LIST_ENTRY(cursor, struct uia_event, event_list_entry); + + hr = uia_event_check_match(node, sa, args, event); + if (FAILED(hr)) + break; + } + } + uia_event_map_entry_release(event_entry); SafeArrayDestroy(sa); UiaNodeRelease(node); diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 5aeab22d3ee..325206c6d90 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -148,6 +148,17 @@ static inline void variant_init_i4(VARIANT *v, int val) V_I4(v) = val; }
+static inline void get_variant_for_node(HUIANODE node, VARIANT *v) +{ +#ifdef _WIN64 + V_VT(v) = VT_I8; + V_I8(v) = (UINT64)node; +#else + V_VT(v) = VT_I4; + V_I4(v) = (UINT32)node; +#endif +} + static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T count, SIZE_T size) { SIZE_T max_capacity, new_capacity; @@ -184,6 +195,7 @@ HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPE HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers) DECLSPEC_HIDDEN; +HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) DECLSPEC_HIDDEN; HRESULT uia_condition_check(HUIANODE node, struct UiaCondition *condition) DECLSPEC_HIDDEN; BOOL uia_condition_matched(HRESULT hr) DECLSPEC_HIDDEN;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 67 +++++++++++++++++++++++++++++ dlls/uiautomationcore/uia_event.c | 10 +++-- dlls/uiautomationcore/uia_private.h | 1 + 3 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 492ca0e52de..5fe3078bec1 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -673,6 +673,73 @@ static HRESULT prepare_uia_node(struct uia_node *node) return S_OK; }
+static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, int prov_type); +HRESULT clone_uia_node(HUIANODE in_node, HUIANODE *out_node) +{ + struct uia_node *in_node_data = impl_from_IWineUiaNode((IWineUiaNode *)in_node); + struct uia_node *node; + HRESULT hr = S_OK; + int i; + + *out_node = NULL; + if (in_node_data->nested_node) + { + FIXME("Cloning of nested nodes currently unimplemented\n"); + return E_NOTIMPL; + } + + for (i = 0; i < PROV_TYPE_COUNT; i++) + { + if (in_node_data->prov[i] && is_nested_node_provider(in_node_data->prov[i])) + { + FIXME("Cloning of nested node providers currently unimplemented\n"); + return E_NOTIMPL; + } + } + + if (!(node = heap_alloc_zero(sizeof(*node)))) + return E_OUTOFMEMORY; + + node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl; + list_init(&node->prov_thread_list_entry); + list_init(&node->node_map_list_entry); + node->ref = 1; + node->hwnd = in_node_data->hwnd; + + for (i = 0; i < PROV_TYPE_COUNT; i++) + { + struct uia_provider *in_prov_data; + + if (!in_node_data->prov[i]) + continue; + + in_prov_data = impl_from_IWineUiaProvider(in_node_data->prov[i]); + hr = create_wine_uia_provider(node, in_prov_data->elprov, i); + if (FAILED(hr)) + goto exit; + + if (in_node_data->git_cookie[i]) + { + hr = register_interface_in_git((IUnknown *)node->prov[i], &IID_IWineUiaProvider, &node->git_cookie[i]); + if (FAILED(hr)) + goto exit; + } + } + + node->parent_link_idx = in_node_data->parent_link_idx; + node->creator_prov_idx = in_node_data->creator_prov_idx; + node->creator_prov_type = in_node_data->creator_prov_type; + + *out_node = (void *)&node->IWineUiaNode_iface; + TRACE("Created clone node %p from node %p\n", *out_node, in_node); + +exit: + if (FAILED(hr)) + IWineUiaNode_Release(&node->IWineUiaNode_iface); + + return hr; +} + static BOOL node_creator_is_parent_link(struct uia_node *node) { if (node->creator_prov_idx == node->parent_link_idx) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index ee65bb7b893..5f079ab88cf 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -1160,13 +1160,17 @@ static HRESULT uia_event_invoke(HUIANODE node, struct uia_event_args *args, stru } else { + HUIANODE node2; VARIANT v;
- get_variant_for_node(node, &v); - IWineUiaNode_AddRef((IWineUiaNode *)node); + hr = clone_uia_node(node, &node2); + if (FAILED(hr)) + return hr; + + get_variant_for_node(node2, &v); hr = uia_event_queue_post_event(event, v, args); if (FAILED(hr)) - UiaNodeRelease(node); + UiaNodeRelease(node2); }
return hr; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 325206c6d90..727c0be6894 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -192,6 +192,7 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c /* uia_client.c */ int get_node_provider_type_at_idx(struct uia_node *node, int idx) DECLSPEC_HIDDEN; HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) DECLSPEC_HIDDEN; +HRESULT clone_uia_node(HUIANODE in_node, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT navigate_uia_node(struct uia_node *node, int nav_dir, HUIANODE *out_node) DECLSPEC_HIDDEN; HRESULT create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers) DECLSPEC_HIDDEN;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 12 ++-- dlls/uiautomationcore/uia_event.c | 79 +++++++++++++++------- 2 files changed, 62 insertions(+), 29 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 788eb1a4eba..4845fe7ab33 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13974,8 +13974,8 @@ static void test_UiaAddEvent_client_proc(void) 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); + ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); + CHECK_CALLED(uia_event_callback); CHECK_CALLED(prov_callback_base_hwnd); CHECK_CALLED(prov_callback_nonclient); todo_wine CHECK_CALLED(prov_callback_proxy); @@ -13995,8 +13995,8 @@ static void test_UiaAddEvent_client_proc(void)
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); + ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); + CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); @@ -14109,8 +14109,8 @@ static void test_UiaAddEvent_client_proc(void) 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); + ok(!WaitForSingleObject(EventData.event_handle, 2000), "Wait for event_handle failed.\n"); + CHECK_CALLED(uia_event_callback);
hr = UiaRemoveEvent(event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 5f079ab88cf..df7a76fdaf6 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -316,32 +316,63 @@ static struct uia_queue_event *uia_event_queue_pop(struct list *event_queue) return queue_event; }
+static HRESULT uia_event_invoke(HUIANODE node, struct uia_event_args *args, struct uia_event *event); +static HRESULT uia_raise_clientside_event(struct uia_queue_event *event) +{ + HUIANODE node; + HRESULT hr; + + hr = uia_node_from_lresult((LRESULT)V_I4(&event->node), &node); + if (SUCCEEDED(hr)) + { + hr = uia_event_invoke(node, event->args, event->event); + UiaNodeRelease(node); + } + + return hr; +} + +static HRESULT uia_raise_serverside_event(struct uia_queue_event *event) +{ + HUIANODE node; + HRESULT hr; + + hr = UiaHUiaNodeFromVariant(&event->node, &node); + if (SUCCEEDED(hr)) + { + LRESULT lr; + VARIANT v; + + if ((lr = uia_lresult_from_node(node))) + { + V_VT(&v) = VT_I4; + V_I4(&v) = lr; + hr = IWineUiaEvent_raise_event(event->event->u.serverside.event_iface, v); + if (FAILED(hr)) + WARN("IWineUiaEvent_raise_event failed with hr %#lx\n", hr); + } + } + + return hr; +} + static void uia_event_thread_process_queue(struct list *event_queue) { while (1) { struct uia_queue_event *event; - HUIANODE node; HRESULT hr;
if (!(event = uia_event_queue_pop(event_queue))) break;
- hr = UiaHUiaNodeFromVariant(&event->node, &node); - if (SUCCEEDED(hr)) - { - LRESULT lr; - VARIANT v; - - if ((lr = uia_lresult_from_node(node))) - { - V_VT(&v) = VT_I4; - V_I4(&v) = lr; - hr = IWineUiaEvent_raise_event(event->event->u.serverside.event_iface, v); - if (FAILED(hr)) - WARN("IWineUiaEvent_raise_event failed with hr %#lx\n", hr); - } - } + if (event->event->event_type == EVENT_TYPE_CLIENTSIDE) + hr = uia_raise_clientside_event(event); + else + hr = uia_raise_serverside_event(event); + + if (FAILED(hr)) + WARN("Event raising function failed with hr %#lx\n", hr);
uia_event_args_release(event->args); IWineUiaEvent_Release(&event->event->IWineUiaEvent_iface); @@ -597,19 +628,21 @@ static HRESULT WINAPI uia_event_set_event_data(IWineUiaEvent *iface, const GUID static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_node) { struct uia_event *event = impl_from_IWineUiaEvent(iface); - HUIANODE node; + struct uia_event_args *args; HRESULT hr;
- FIXME("%p, %s: stub\n", iface, debugstr_variant(&in_node)); + TRACE("%p, %s\n", iface, debugstr_variant(&in_node));
assert(event->event_type != EVENT_TYPE_SERVERSIDE);
- hr = uia_node_from_lresult((LRESULT)V_I4(&in_node), &node); - if (FAILED(hr)) - return hr; + args = create_uia_event_args(uia_event_info_from_id(event->event_id)); + if (!args) + return E_OUTOFMEMORY;
- UiaNodeRelease(node); - return S_OK; + hr = uia_event_queue_post_event(event, in_node, args); + uia_event_args_release(args); + + return hr; }
static const IWineUiaEventVtbl uia_event_vtbl = {
On Tue Jun 20 19:51:39 2023 +0000, Esme Povirk wrote:
I think the hwnd should be checked here, or the message window should be given a WndProc that responds to this with PostQuitMessage.
I added a check for the hwnd here in the latest revision. I'll let you mark this as resolved if this is adequate, if you'd rather I go with the PostQuitMessage approach instead let me know.
On Wed Jun 21 12:22:14 2023 +0000, Connor McAdams wrote:
The idea of storing it in a VARIANT is that we can use one generic structure/variable for both clientside and serverside events - a serverside event posts a VARIANT containing an HUIANODE pointer, and a clientside event posts a VARIANT containing an lresult that can be converted into an HUIANODE. I spent time thinking about this when I designed it, there's a possibility of a delay when converting the lresult into a node. This could cause events to be put into the queue out of sequence, e.g:
- Event 1 arrives, calls `uia_node_from_lresult()`
- Event 2 arrives, calls `uia_node_from_lresult()`
- `uia_node_from_lresult()` returns for event 2, it gets posted to the queue.
- `uia_node_from_lresult()` returns for event 1, it gets posted to the queue.
So IMO, it's best to just post the lresult for the node to the queue quickly and let the event thread do the conversion. We could also add different structures for each, or a union depending on the type of event in the queue.
I thought about this a bit yesterday before pushing this branch, it would be nice to have a corresponding node AddRef inside of `uia_event_queue_post_event()`, but that'd add a bit of ugliness here IMO. We'd have to check the event type, get the node out of the variant, and then addref it. I guess if we had separate functions/structures for posting serverside vs clientside events to the queue it would be cleaner to do.
On Wed Jun 21 13:09:21 2023 +0000, Connor McAdams wrote:
I thought about this a bit yesterday before pushing this branch, it would be nice to have a corresponding node AddRef inside of `uia_event_queue_post_event()`, but that'd add a bit of ugliness here IMO. We'd have to check the event type, get the node out of the variant, and then addref it. I guess if we had separate functions/structures for posting serverside vs clientside events to the queue it would be cleaner to do.
I kinda assumed when you added a UiaEventArgs field that it would eventually be a union.
AFAICT you're only using the VARIANT to store a ULONG_PTR. It doesn't carry any information about what kind of value you're actually storing in it, nor does it make it easier to free the value.