1
0
Fork 0
mirror of synced 2025-03-07 03:53:26 +01:00

uiautomationcore: Add WinEvent hook on the UIA event thread.

Signed-off-by: Connor McAdams <cmcadams@codeweavers.com>
This commit is contained in:
Connor McAdams 2023-09-13 12:48:10 -04:00 committed by Alexandre Julliard
parent 336503f3c9
commit 2a2c4cd7b9

View file

@ -50,6 +50,20 @@ static SAFEARRAY *uia_get_desktop_rt_id(void)
return uia_desktop_node_rt_id; return uia_desktop_node_rt_id;
} }
static int win_event_to_uia_event_id(int win_event)
{
switch (win_event)
{
case EVENT_OBJECT_FOCUS: return UIA_AutomationFocusChangedEventId;
case EVENT_SYSTEM_ALERT: return UIA_SystemAlertEventId;
default:
break;
}
return 0;
}
/* /*
* UI Automation event map. * UI Automation event map.
*/ */
@ -247,6 +261,7 @@ struct uia_event_thread
LONG ref; LONG ref;
struct list *event_queue; struct list *event_queue;
HWINEVENTHOOK hook;
}; };
#define WM_UIA_EVENT_THREAD_STOP (WM_USER + 1) #define WM_UIA_EVENT_THREAD_STOP (WM_USER + 1)
@ -264,12 +279,18 @@ static CRITICAL_SECTION event_thread_cs = { &event_thread_cs_debug, -1, 0, 0, 0,
enum uia_queue_event_type { enum uia_queue_event_type {
QUEUE_EVENT_TYPE_SERVERSIDE, QUEUE_EVENT_TYPE_SERVERSIDE,
QUEUE_EVENT_TYPE_CLIENTSIDE, QUEUE_EVENT_TYPE_CLIENTSIDE,
QUEUE_EVENT_TYPE_WIN_EVENT,
}; };
struct uia_queue_event struct uia_queue_event
{ {
struct list event_queue_entry; struct list event_queue_entry;
int queue_event_type; int queue_event_type;
};
struct uia_queue_uia_event
{
struct uia_queue_event queue_entry;
struct uia_event_args *args; struct uia_event_args *args;
struct uia_event *event; struct uia_event *event;
@ -285,14 +306,40 @@ struct uia_queue_event
} u; } u;
}; };
static void uia_event_queue_push(struct uia_queue_event *event) struct uia_queue_win_event
{ {
struct uia_queue_event queue_entry;
HWINEVENTHOOK hook;
DWORD event_id;
HWND hwnd;
LONG obj_id;
LONG child_id;
DWORD thread_id;
DWORD event_time;
};
static void uia_event_queue_push(struct uia_queue_event *event, int queue_event_type)
{
event->queue_event_type = queue_event_type;
EnterCriticalSection(&event_thread_cs); EnterCriticalSection(&event_thread_cs);
if (queue_event_type == QUEUE_EVENT_TYPE_WIN_EVENT)
{
struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)event;
if (win_event->hook != event_thread.hook)
{
free(event);
goto exit;
}
}
assert(event_thread.event_queue); assert(event_thread.event_queue);
list_add_tail(event_thread.event_queue, &event->event_queue_entry); list_add_tail(event_thread.event_queue, &event->event_queue_entry);
PostMessageW(event_thread.hwnd, WM_UIA_EVENT_THREAD_PROCESS_QUEUE, 0, 0); PostMessageW(event_thread.hwnd, WM_UIA_EVENT_THREAD_PROCESS_QUEUE, 0, 0);
exit:
LeaveCriticalSection(&event_thread_cs); LeaveCriticalSection(&event_thread_cs);
} }
@ -322,7 +369,7 @@ static void uia_node_lresult_release(LRESULT lr)
static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args, static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct uia_event_args *args,
struct uia_event *event); struct uia_event *event);
static HRESULT uia_raise_clientside_event(struct uia_queue_event *event) static HRESULT uia_raise_clientside_event(struct uia_queue_uia_event *event)
{ {
HUIANODE node, nav_start_node; HUIANODE node, nav_start_node;
HRESULT hr; HRESULT hr;
@ -354,7 +401,7 @@ static HRESULT uia_raise_clientside_event(struct uia_queue_event *event)
return hr; return hr;
} }
static HRESULT uia_raise_serverside_event(struct uia_queue_event *event) static HRESULT uia_raise_serverside_event(struct uia_queue_uia_event *event)
{ {
HRESULT hr = S_OK; HRESULT hr = S_OK;
LRESULT lr, lr2; LRESULT lr, lr2;
@ -397,29 +444,70 @@ static void uia_event_thread_process_queue(struct list *event_queue)
while (1) while (1)
{ {
struct uia_queue_event *event; struct uia_queue_event *event;
HRESULT hr; HRESULT hr = S_OK;
if (!(event = uia_event_queue_pop(event_queue))) if (!(event = uia_event_queue_pop(event_queue)))
break; break;
if (event->queue_event_type == QUEUE_EVENT_TYPE_SERVERSIDE) switch (event->queue_event_type)
hr = uia_raise_serverside_event(event); {
else case QUEUE_EVENT_TYPE_SERVERSIDE:
hr = uia_raise_clientside_event(event); case QUEUE_EVENT_TYPE_CLIENTSIDE:
{
struct uia_queue_uia_event *uia_event = (struct uia_queue_uia_event *)event;
if (event->queue_event_type == QUEUE_EVENT_TYPE_SERVERSIDE)
hr = uia_raise_serverside_event(uia_event);
else
hr = uia_raise_clientside_event(uia_event);
uia_event_args_release(uia_event->args);
IWineUiaEvent_Release(&uia_event->event->IWineUiaEvent_iface);
break;
}
default:
break;
}
if (FAILED(hr)) if (FAILED(hr))
WARN("Failed to raise event type %d with hr %#lx\n", event->queue_event_type, hr); WARN("Failed to raise event type %d with hr %#lx\n", event->queue_event_type, hr);
uia_event_args_release(event->args);
IWineUiaEvent_Release(&event->event->IWineUiaEvent_iface);
free(event); free(event);
} }
} }
static void CALLBACK uia_event_thread_win_event_proc(HWINEVENTHOOK hook, DWORD event_id, HWND hwnd, LONG obj_id,
LONG child_id, DWORD thread_id, DWORD event_time)
{
struct uia_queue_win_event *win_event;
TRACE("%p, %ld, %p, %ld, %ld, %ld, %ld\n", hook, event_id, hwnd, obj_id, child_id, thread_id, event_time);
if (!win_event_to_uia_event_id(event_id))
return;
if (!(win_event = calloc(1, sizeof(*win_event))))
{
ERR("Failed to allocate uia_queue_win_event structure\n");
return;
}
win_event->hook = hook;
win_event->event_id = event_id;
win_event->hwnd = hwnd;
win_event->obj_id = obj_id;
win_event->child_id = child_id;
win_event->thread_id = thread_id;
win_event->event_time = event_time;
uia_event_queue_push(&win_event->queue_entry, QUEUE_EVENT_TYPE_WIN_EVENT);
}
static DWORD WINAPI uia_event_thread_proc(void *arg) static DWORD WINAPI uia_event_thread_proc(void *arg)
{ {
HANDLE initialized_event = arg; HANDLE initialized_event = arg;
struct list event_queue; struct list event_queue;
HWINEVENTHOOK hook;
HWND hwnd; HWND hwnd;
MSG msg; MSG msg;
@ -435,6 +523,8 @@ static DWORD WINAPI uia_event_thread_proc(void *arg)
event_thread.hwnd = hwnd; event_thread.hwnd = hwnd;
event_thread.event_queue = &event_queue; event_thread.event_queue = &event_queue;
event_thread.hook = hook = SetWinEventHook(EVENT_MIN, EVENT_MAX, 0, uia_event_thread_win_event_proc, 0, 0,
WINEVENT_OUTOFCONTEXT);
/* Initialization complete, thread can now process window messages. */ /* Initialization complete, thread can now process window messages. */
SetEvent(initialized_event); SetEvent(initialized_event);
@ -455,6 +545,7 @@ static DWORD WINAPI uia_event_thread_proc(void *arg)
TRACE("Shutting down UI Automation event thread.\n"); TRACE("Shutting down UI Automation event thread.\n");
UnhookWinEvent(hook);
DestroyWindow(hwnd); DestroyWindow(hwnd);
CoUninitialize(); CoUninitialize();
FreeLibraryAndExitThread(huia_module, 0); FreeLibraryAndExitThread(huia_module, 0);
@ -664,7 +755,7 @@ static HRESULT WINAPI uia_event_set_event_data(IWineUiaEvent *iface, const GUID
static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_node, VARIANT in_nav_start_node) static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_node, VARIANT in_nav_start_node)
{ {
struct uia_event *event = impl_from_IWineUiaEvent(iface); struct uia_event *event = impl_from_IWineUiaEvent(iface);
struct uia_queue_event *queue_event; struct uia_queue_uia_event *queue_event;
struct uia_event_args *args; struct uia_event_args *args;
TRACE("%p, %s, %s\n", iface, debugstr_variant(&in_node), debugstr_variant(&in_nav_start_node)); TRACE("%p, %s, %s\n", iface, debugstr_variant(&in_node), debugstr_variant(&in_nav_start_node));
@ -680,7 +771,6 @@ static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_nod
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
} }
queue_event->queue_event_type = QUEUE_EVENT_TYPE_CLIENTSIDE;
queue_event->args = args; queue_event->args = args;
queue_event->event = event; queue_event->event = event;
queue_event->u.clientside.node = V_I4(&in_node); queue_event->u.clientside.node = V_I4(&in_node);
@ -688,7 +778,7 @@ static HRESULT WINAPI uia_event_raise_event(IWineUiaEvent *iface, VARIANT in_nod
queue_event->u.clientside.nav_start_node = V_I4(&in_nav_start_node); queue_event->u.clientside.nav_start_node = V_I4(&in_nav_start_node);
IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface);
uia_event_queue_push(queue_event); uia_event_queue_push(&queue_event->queue_entry, QUEUE_EVENT_TYPE_CLIENTSIDE);
return S_OK; return S_OK;
} }
@ -1277,7 +1367,7 @@ static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct u
} }
else else
{ {
struct uia_queue_event *queue_event; struct uia_queue_uia_event *queue_event;
HUIANODE node2, nav_start_node2; HUIANODE node2, nav_start_node2;
if (!(queue_event = calloc(1, sizeof(*queue_event)))) if (!(queue_event = calloc(1, sizeof(*queue_event))))
@ -1302,7 +1392,6 @@ static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct u
} }
} }
queue_event->queue_event_type = QUEUE_EVENT_TYPE_SERVERSIDE;
queue_event->args = args; queue_event->args = args;
queue_event->event = event; queue_event->event = event;
queue_event->u.serverside.node = node2; queue_event->u.serverside.node = node2;
@ -1310,7 +1399,7 @@ static HRESULT uia_event_invoke(HUIANODE node, HUIANODE nav_start_node, struct u
InterlockedIncrement(&args->ref); InterlockedIncrement(&args->ref);
IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface); IWineUiaEvent_AddRef(&event->IWineUiaEvent_iface);
uia_event_queue_push(queue_event); uia_event_queue_push(&queue_event->queue_entry, QUEUE_EVENT_TYPE_SERVERSIDE);
} }
return hr; return hr;