-- v3: 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 | 187 +++++++++++++++++++-- dlls/uiautomationcore/uia_private.h | 12 ++ 5 files changed, 188 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..963701fbff0 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,82 @@ 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; + HUIANODE node; +}; + +static void uia_event_queue_push(struct uia_queue_event *event) +{ + EnterCriticalSection(&event_thread_cs); + + assert(event_thread.event_queue); + list_add_tail(event_thread.event_queue, &event->event_queue_entry); + PostMessageW(event_thread.hwnd, WM_UIA_EVENT_THREAD_PROCESS_QUEUE, 0, 0); + + LeaveCriticalSection(&event_thread_cs); +} + +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; + LRESULT lr; + HRESULT hr; + + if (!(event = uia_event_queue_pop(event_queue))) + break; + + /* + * uia_lresult_from_node is expected to release the node here upon + * failure. + */ + if ((lr = uia_lresult_from_node(event->node))) + { + VARIANT v; + + 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 +353,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 +522,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 +540,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 +580,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 +1128,37 @@ 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; + + 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)) + 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); + struct uia_queue_event *queue_event; + + if (!(queue_event = heap_alloc_zero(sizeof(*queue_event)))) + return E_OUTOFMEMORY; + + queue_event->args = args; + queue_event->event = event; + queue_event->node = node; + + InterlockedIncrement(&args->ref); + IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); + IWineUiaNode_AddRef((IWineUiaNode *)node); + uia_event_queue_push(queue_event); }
return hr; @@ -1056,6 +1195,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 +1285,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 | 11 ++++- dlls/uiautomationcore/uia_private.h | 1 + 3 files changed, 77 insertions(+), 2 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 963701fbff0..a77fe3dd30f 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -1147,17 +1147,24 @@ static HRESULT uia_event_invoke(HUIANODE node, struct uia_event_args *args, stru else { struct uia_queue_event *queue_event; + HUIANODE node2;
if (!(queue_event = heap_alloc_zero(sizeof(*queue_event)))) return E_OUTOFMEMORY;
+ hr = clone_uia_node(node, &node2); + if (FAILED(hr)) + { + heap_free(queue_event); + return hr; + } + queue_event->args = args; queue_event->event = event; - queue_event->node = node; + queue_event->node = node2;
InterlockedIncrement(&args->ref); IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); - IWineUiaNode_AddRef((IWineUiaNode *)node); uia_event_queue_push(queue_event); }
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 | 114 ++++++++++++++++----- 2 files changed, 97 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 a77fe3dd30f..be3e422c313 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -267,13 +267,26 @@ static CRITICAL_SECTION_DEBUG event_thread_cs_debug = }; static CRITICAL_SECTION event_thread_cs = { &event_thread_cs_debug, -1, 0, 0, 0, 0 };
+enum uia_queue_event_type { + QUEUE_EVENT_TYPE_SERVERSIDE, + QUEUE_EVENT_TYPE_CLIENTSIDE, +}; + struct uia_queue_event { struct list event_queue_entry; + int queue_event_type;
struct uia_event_args *args; struct uia_event *event; - HUIANODE node; + union { + struct { + HUIANODE node; + } serverside; + struct { + LRESULT node; + } clientside; + } u; };
static void uia_event_queue_push(struct uia_queue_event *event) @@ -303,31 +316,73 @@ 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(event->u.clientside.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) +{ + HRESULT hr = S_OK; + LRESULT lr; + + /* + * uia_lresult_from_node is expected to release the node here upon + * failure. + */ + if ((lr = uia_lresult_from_node(event->u.serverside.node))) + { + VARIANT v; + + V_VT(&v) = VT_I4; + V_I4(&v) = lr; + hr = IWineUiaEvent_raise_event(event->event->u.serverside.event_iface, v); + if (FAILED(hr)) + { + IWineUiaNode *node; + + /* + * If the method returned failure, make sure we don't leave a + * dangling IWineUiaNode. + */ + if (SUCCEEDED(ObjectFromLresult(lr, &IID_IWineUiaNode, 0, (void **)&node))) + IWineUiaNode_Release(node); + } + } + else + hr = E_FAIL; + + return hr; +} + static void uia_event_thread_process_queue(struct list *event_queue) { while (1) { struct uia_queue_event *event; - LRESULT lr; HRESULT hr;
if (!(event = uia_event_queue_pop(event_queue))) break;
- /* - * uia_lresult_from_node is expected to release the node here upon - * failure. - */ - if ((lr = uia_lresult_from_node(event->node))) - { - VARIANT v; + if (event->queue_event_type == QUEUE_EVENT_TYPE_SERVERSIDE) + hr = uia_raise_serverside_event(event); + else + hr = uia_raise_clientside_event(event);
- 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 (FAILED(hr)) + WARN("Failed to raise event type %d with hr %#lx\n", event->queue_event_type, hr);
uia_event_args_release(event->args); IWineUiaEvent_Release(&event->event->IWineUiaEvent_iface); @@ -583,18 +638,30 @@ 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; - HRESULT hr; + struct uia_queue_event *queue_event; + struct uia_event_args *args;
- 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; + if (!(queue_event = heap_alloc_zero(sizeof(*queue_event)))) + return E_OUTOFMEMORY; + + if (!(args = create_uia_event_args(uia_event_info_from_id(event->event_id)))) + { + heap_free(queue_event); + return E_OUTOFMEMORY; + } + + queue_event->queue_event_type = QUEUE_EVENT_TYPE_CLIENTSIDE; + queue_event->args = args; + queue_event->event = event; + queue_event->u.clientside.node = V_I4(&in_node); + + IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); + uia_event_queue_push(queue_event);
- UiaNodeRelease(node); return S_OK; }
@@ -1159,9 +1226,10 @@ static HRESULT uia_event_invoke(HUIANODE node, struct uia_event_args *args, stru return hr; }
+ queue_event->queue_event_type = QUEUE_EVENT_TYPE_SERVERSIDE; queue_event->args = args; queue_event->event = event; - queue_event->node = node2; + queue_event->u.serverside.node = node2;
InterlockedIncrement(&args->ref); IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface);
This merge request was approved by Esme Povirk.