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 | 3 + dlls/uiautomationcore/uia_client.c | 44 +++++++ dlls/uiautomationcore/uia_event.c | 133 ++++++++++++++++++++- dlls/uiautomationcore/uia_private.h | 3 + 5 files changed, 192 insertions(+), 7 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index a178487e420..d1e276b0b00 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -449,6 +449,11 @@ static void test_implements_interface_(IUnknown *unk, const GUID *iid, BOOL exp_ IUnknown_Release(unk2); }
+#define check_interface_marshal_proxy_creation( iface, iid, expect_proxy ) \ + check_interface_marshal_proxy_creation_( (iface), (iid), (expect_proxy), __FILE__, __LINE__) +static void check_interface_marshal_proxy_creation_(IUnknown *iface, REFIID iid, BOOL expect_proxy, const char *file, + int line); + #define DEFINE_ACC_METHOD_EXPECT(method) \ int expect_ ## method , called_ ## method
@@ -2824,6 +2829,11 @@ static HRESULT WINAPI ProviderWinEventHandler_RespondToWinEvent(IProxyProviderWi
if (data->responder_prov) { + /* + * The IProxyProviderWinEventSink interface uses the free threaded + * marshaler, so no proxy will be created in-process. + */ + check_interface_marshal_proxy_creation((IUnknown *)event_sink, &IID_IProxyProviderWinEventSink, FALSE); hr = IProxyProviderWinEventSink_AddAutomationEvent(event_sink, data->responder_prov, data->responder_event); ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); } @@ -11976,8 +11986,6 @@ static DWORD WINAPI interface_marshal_proxy_thread(LPVOID param) return 0; }
-#define check_interface_marshal_proxy_creation( iface, iid, expect_proxy ) \ - check_interface_marshal_proxy_creation_( (iface), (iid), (expect_proxy), __FILE__, __LINE__) static void check_interface_marshal_proxy_creation_(IUnknown *iface, REFIID iid, BOOL expect_proxy, const char *file, int line) { @@ -16717,8 +16725,8 @@ static const struct prov_method_sequence win_event_handler_seq[] = { { &Provider_hwnd2, PROV_GET_HOST_RAW_ELEMENT_PROVIDER, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider_nc2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ { &Provider_hwnd2, FRAG_NAVIGATE }, /* NavigateDirection_Parent */ - { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, - { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT, METHOD_TODO }, + { &Provider_nc2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, + { &Provider_hwnd2, WINEVENT_HANDLER_RESPOND_TO_WINEVENT }, NODE_CREATE_SEQ_TODO(&Provider_child), { &Provider_child, FRAG_GET_RUNTIME_ID, METHOD_TODO }, { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_TODO }, /* UIA_ProviderDescriptionPropertyId */ diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index b1dea2d414d..22d9d650502 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -19,6 +19,7 @@ #pragma makedep regtypelib
import "oaidl.idl"; +import "uiautomationcore.idl";
struct uia_prop_info { const GUID *guid; @@ -92,6 +93,8 @@ library UIA_wine_private HRESULT navigate([in]int nav_dir, [out, retval]VARIANT *ret_val); HRESULT get_focus([out, retval]VARIANT *ret_val); HRESULT attach_event([in]LONG_PTR huiaevent); + HRESULT respond_to_win_event([in]DWORD win_event, [in]ULONG hwnd, [in]LONG obj_id, [in]LONG child_id, + [in]IProxyProviderWinEventSink *sink); }
[ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 1ff3e1a9a46..f7b602ab2ed 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -378,6 +378,22 @@ static HRESULT attach_event_to_node_provider(IWineUiaNode *node, int idx, HUIAEV return hr; }
+HRESULT respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD win_event, HWND hwnd, LONG obj_id, + LONG child_id, IProxyProviderWinEventSink *sink) +{ + IWineUiaProvider *prov; + HRESULT hr; + + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_respond_to_win_event(prov, win_event, HandleToUlong(hwnd), obj_id, child_id, sink); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1939,6 +1955,23 @@ exit: return hr; }
+static HRESULT WINAPI uia_provider_respond_to_win_event(IWineUiaProvider *iface, DWORD win_event, ULONG hwnd, LONG obj_id, + LONG child_id, IProxyProviderWinEventSink *sink) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + IProxyProviderWinEventHandler *handler; + HRESULT hr; + + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IProxyProviderWinEventHandler, (void **)&handler); + if (FAILED(hr)) + return S_OK; + + hr = IProxyProviderWinEventHandler_RespondToWinEvent(handler, win_event, UlongToHandle(hwnd), obj_id, child_id, sink); + IProxyProviderWinEventHandler_Release(handler); + + return hr; +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1949,6 +1982,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_navigate, uia_provider_get_focus, uia_provider_attach_event, + uia_provider_respond_to_win_event, };
static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2388,6 +2422,15 @@ static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *if return hr; }
+static HRESULT WINAPI uia_nested_node_provider_respond_to_win_event(IWineUiaProvider *iface, DWORD win_event, ULONG hwnd, + LONG obj_id, LONG child_id, IProxyProviderWinEventSink *sink) +{ + FIXME("%p, %#lx, #%lx, %#lx, %#lx, %p: stub\n", iface, win_event, hwnd, obj_id, child_id, sink); + /* This should not be called. */ + assert(0); + return E_FAIL; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2398,6 +2441,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_navigate, uia_nested_node_provider_get_focus, uia_nested_node_provider_attach_event, + uia_nested_node_provider_respond_to_win_event, };
static BOOL is_nested_node_provider(IWineUiaProvider *iface) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index 08f89899c0d..4e28d99ac49 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -20,7 +20,6 @@
#include "wine/debug.h" #include "wine/rbtree.h" -#include "assert.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
@@ -323,6 +322,118 @@ static void uia_event_args_release(struct uia_event_args *args) free(args); }
+/* + * IProxyProviderWinEventSink interface implementation. + */ +struct uia_proxy_win_event_sink { + IProxyProviderWinEventSink IProxyProviderWinEventSink_iface; + LONG ref; + + int event_id; + IUnknown *marshal; +}; + +static inline struct uia_proxy_win_event_sink *impl_from_IProxyProviderWinEventSink(IProxyProviderWinEventSink *iface) +{ + return CONTAINING_RECORD(iface, struct uia_proxy_win_event_sink, IProxyProviderWinEventSink_iface); +} + +static HRESULT WINAPI uia_proxy_win_event_sink_QueryInterface(IProxyProviderWinEventSink *iface, REFIID riid, void **obj) +{ + struct uia_proxy_win_event_sink *sink = impl_from_IProxyProviderWinEventSink(iface); + + *obj = NULL; + if (IsEqualIID(riid, &IID_IProxyProviderWinEventSink) || IsEqualIID(riid, &IID_IUnknown)) + *obj = iface; + else if (IsEqualIID(riid, &IID_IMarshal)) + return IUnknown_QueryInterface(sink->marshal, riid, obj); + else + return E_NOINTERFACE; + + IProxyProviderWinEventSink_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_proxy_win_event_sink_AddRef(IProxyProviderWinEventSink *iface) +{ + struct uia_proxy_win_event_sink *sink = impl_from_IProxyProviderWinEventSink(iface); + ULONG ref = InterlockedIncrement(&sink->ref); + + TRACE("%p, refcount %ld\n", sink, ref); + + return ref; +} + +static ULONG WINAPI uia_proxy_win_event_sink_Release(IProxyProviderWinEventSink *iface) +{ + struct uia_proxy_win_event_sink *sink = impl_from_IProxyProviderWinEventSink(iface); + ULONG ref = InterlockedDecrement(&sink->ref); + + TRACE("%p, refcount %ld\n", sink, ref); + + if (!ref) + { + IUnknown_Release(sink->marshal); + free(sink); + } + + return ref; +} + +static HRESULT WINAPI uia_proxy_win_event_sink_AddAutomationPropertyChangedEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, PROPERTYID prop_id, VARIANT new_value) +{ + FIXME("%p, %p, %d, %s: stub\n", iface, elprov, prop_id, debugstr_variant(&new_value)); + return E_NOTIMPL; +} + +static HRESULT WINAPI uia_proxy_win_event_sink_AddAutomationEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, EVENTID event_id) +{ + FIXME("%p, %p, %d: stub\n", iface, elprov, event_id); + return S_OK; +} + +static HRESULT WINAPI uia_proxy_win_event_sink_AddStructureChangedEvent(IProxyProviderWinEventSink *iface, + IRawElementProviderSimple *elprov, enum StructureChangeType structure_change_type, SAFEARRAY *runtime_id) +{ + FIXME("%p, %p, %d, %p: stub\n", iface, elprov, structure_change_type, runtime_id); + return E_NOTIMPL; +} + +static const IProxyProviderWinEventSinkVtbl uia_proxy_event_sink_vtbl = { + uia_proxy_win_event_sink_QueryInterface, + uia_proxy_win_event_sink_AddRef, + uia_proxy_win_event_sink_Release, + uia_proxy_win_event_sink_AddAutomationPropertyChangedEvent, + uia_proxy_win_event_sink_AddAutomationEvent, + uia_proxy_win_event_sink_AddStructureChangedEvent, +}; + +static HRESULT create_proxy_win_event_sink(struct uia_proxy_win_event_sink **out_sink, int event_id) +{ + struct uia_proxy_win_event_sink *sink = calloc(1, sizeof(*sink)); + HRESULT hr; + + *out_sink = NULL; + if (!sink) + return E_OUTOFMEMORY; + + sink->IProxyProviderWinEventSink_iface.lpVtbl = &uia_proxy_event_sink_vtbl; + sink->ref = 1; + sink->event_id = event_id; + + hr = CoCreateFreeThreadedMarshaler((IUnknown *)&sink->IProxyProviderWinEventSink_iface, &sink->marshal); + if (FAILED(hr)) + { + free(sink); + return hr; + } + + *out_sink = sink; + return S_OK; +} + /* * UI Automation event thread. */ @@ -559,9 +670,12 @@ static HRESULT create_msaa_provider_from_hwnd(HWND hwnd, int in_child_id, IRawEl static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *data) { struct uia_queue_win_event *win_event = (struct uia_queue_win_event *)data; + struct uia_proxy_win_event_sink *sink; IRawElementProviderSimple *elprov; + struct uia_node *node_data; HUIANODE node; HRESULT hr; + int i;
if (!uia_hwnd_map_check_hwnd(&event->u.clientside.win_event_hwnd_map, win_event->hwnd)) { @@ -592,10 +706,23 @@ static HRESULT uia_win_event_for_each_callback(struct uia_event *event, void *da if (FAILED(hr)) return hr;
- FIXME("IProxyProviderWinEventHandler usage is currently unimplemented.\n"); + hr = create_proxy_win_event_sink(&sink, event->event_id); + if (SUCCEEDED(hr)) + { + node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + for (i = 0; i < node_data->prov_count; i++) + { + hr = respond_to_win_event_on_node_provider((IWineUiaNode *)node, i, win_event->event_id, win_event->hwnd, win_event->obj_id, + win_event->child_id, &sink->IProxyProviderWinEventSink_iface); + if (FAILED(hr)) + break; + } + + IProxyProviderWinEventSink_Release(&sink->IProxyProviderWinEventSink_iface); + }
UiaNodeRelease(node); - return S_OK; + return hr; }
static void uia_event_thread_process_queue(struct list *event_queue) diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index abd03ce308e..8a750bf3ae1 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -22,6 +22,7 @@ #include "uia_classes.h" #include "wine/list.h" #include "wine/rbtree.h" +#include "assert.h"
extern HMODULE huia_module DECLSPEC_HIDDEN;
@@ -210,6 +211,8 @@ 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 respond_to_win_event_on_node_provider(IWineUiaNode *node, int idx, DWORD win_event, HWND hwnd, LONG obj_id, + LONG child_id, IProxyProviderWinEventSink *sink) 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;