From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/Makefile.in | 1 + dlls/uiautomationcore/tests/uiautomation.c | 79 ++++++++--- dlls/uiautomationcore/uia_classes.idl | 9 ++ dlls/uiautomationcore/uia_client.c | 11 -- dlls/uiautomationcore/uia_event.c | 148 +++++++++++++++++++++ dlls/uiautomationcore/uia_ids.c | 19 +++ dlls/uiautomationcore/uia_private.h | 1 + 7 files changed, 237 insertions(+), 31 deletions(-) create mode 100644 dlls/uiautomationcore/uia_event.c
diff --git a/dlls/uiautomationcore/Makefile.in b/dlls/uiautomationcore/Makefile.in index 28d814e6ee6..8156807c334 100644 --- a/dlls/uiautomationcore/Makefile.in +++ b/dlls/uiautomationcore/Makefile.in @@ -7,6 +7,7 @@ EXTRADLLFLAGS = -Wb,--prefer-native C_SRCS = \ uia_client.c \ uia_com_client.c \ + uia_event.c \ uia_ids.c \ uia_main.c \ uia_provider.c diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 6b5d739ca47..475955e251e 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13819,6 +13819,45 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param) return 0; }
+static void test_UiaAddEvent_args(HUIANODE node) +{ + struct UiaCacheRequest cache_req; + HUIAEVENT event; + HRESULT hr; + + set_cache_request(&cache_req, (struct UiaCondition *)&UiaTrueCondition, TreeScope_Element, NULL, 0, NULL, 0, + AutomationElementMode_Full); + + /* NULL node. */ + hr = UiaAddEvent(NULL, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + &event); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* NULL event callback. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, NULL, TreeScope_Element, NULL, 0, &cache_req, + &event); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* NULL cache request. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, NULL, + &event); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* NULL event handle. */ + hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, + NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* Event IDs aren't checked for validity, 1 is not a valid UIA event ID. */ + event = NULL; + hr = UiaAddEvent(node, 1, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); + + hr = UiaRemoveEvent(event); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +} + static void test_UiaAddEvent(void) { IRawElementProviderFragmentRoot *embedded_roots[2] = { &Provider_child.IRawElementProviderFragmentRoot_iface, @@ -13855,6 +13894,9 @@ static void test_UiaAddEvent(void) check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", TRUE); VariantClear(&v);
+ /* Test valid function input arguments. */ + test_UiaAddEvent_args(node); + /* * Raise event without any registered event handlers. */ @@ -13875,11 +13917,10 @@ static void test_UiaAddEvent(void) AutomationElementMode_Full); hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq2, "event_seq2"); + ok_method_sequence(event_seq2, "event_seq2");
/* * Even though we raise an event on the same provider as the one our node @@ -13911,11 +13952,10 @@ static void test_UiaAddEvent(void) Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq2, "event_seq2"); + ok_method_sequence(event_seq2, "event_seq2");
/* Event callback is now invoked since we can match by runtime ID. */ method_sequences_enabled = FALSE; @@ -13953,11 +13993,10 @@ static void test_UiaAddEvent(void) /* Create an event with TreeScope_Children. */ hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Children, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); - if (SUCCEEDED(hr)) - ok_method_sequence(event_seq5, "event_seq5"); + ok_method_sequence(event_seq5, "event_seq5");
/* * Only TreeScope_Children and not TreeScope_Element, handler won't be @@ -14001,8 +14040,8 @@ static void test_UiaAddEvent(void) /* Create an event with TreeScope_Descendants. */ hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref);
/* Raised an event on Provider_child_child. */ @@ -14034,8 +14073,8 @@ static void test_UiaAddEvent(void) Provider.frag_root = &Provider2.IRawElementProviderFragmentRoot_iface; hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); todo_wine ok(Provider2.ref > 1, "Unexpected refcnt %ld\n", Provider2.ref); ok(!Provider.advise_events_added_event_id, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); @@ -14069,8 +14108,8 @@ static void test_UiaAddEvent(void)
hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Descendants, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n"); todo_wine ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); todo_wine ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); @@ -14132,8 +14171,8 @@ static void test_UiaAddEvent(void) AutomationElementMode_Full); hr = UiaAddEvent(node, UIA_AutomationFocusChangedEventId, uia_event_callback, TreeScope_Element | TreeScope_Children, NULL, 0, &cache_req, &event); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - todo_wine ok(!!event, "event == NULL\n"); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!!event, "event == NULL\n");
/* * Raising an event on a serverside provider results in no clientside diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index b727184ff37..83ff63fdd70 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -54,6 +54,15 @@ library UIA_wine_private { importlib("stdole2.tlb");
+ [ + object, + uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba), + pointer_default(unique), + ] + interface IWineUiaEvent : IUnknown + { + } + [ object, uuid(57865755-6c05-4522-98df-4ca658b768ef), diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index a2f464ae4df..c8b369fa65b 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -3513,17 +3513,6 @@ exit: return hr; }
-/*********************************************************************** - * UiaAddEvent (uiautomationcore.@) - */ -HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope, - PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent) -{ - FIXME("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count, - cache_req, huiaevent); - return E_NOTIMPL; -} - /*********************************************************************** * UiaRemoveEvent (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c new file mode 100644 index 00000000000..e1d2585c056 --- /dev/null +++ b/dlls/uiautomationcore/uia_event.c @@ -0,0 +1,148 @@ +/* + * Copyright 2023 Connor McAdams for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "uia_private.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(uiautomation); + +struct uia_event +{ + IWineUiaEvent IWineUiaEvent_iface; + LONG ref; + + SAFEARRAY *runtime_id; + int event_id; + int scope; + + UiaEventCallback *cback; +}; + +/* + * IWineUiaEvent interface. + */ +static inline struct uia_event *impl_from_IWineUiaEvent(IWineUiaEvent *iface) +{ + return CONTAINING_RECORD(iface, struct uia_event, IWineUiaEvent_iface); +} + +static HRESULT WINAPI uia_event_QueryInterface(IWineUiaEvent *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IWineUiaEvent) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IWineUiaEvent_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_event_AddRef(IWineUiaEvent *iface) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + ULONG ref = InterlockedIncrement(&event->ref); + + TRACE("%p, refcount %ld\n", event, ref); + return ref; +} + +static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + ULONG ref = InterlockedDecrement(&event->ref); + + TRACE("%p, refcount %ld\n", event, ref); + if (!ref) + { + SafeArrayDestroy(event->runtime_id); + heap_free(event); + } + + return ref; +} + +static const IWineUiaEventVtbl uia_event_vtbl = { + uia_event_QueryInterface, + uia_event_AddRef, + uia_event_Release, +}; + +static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int scope, UiaEventCallback *cback, + SAFEARRAY *runtime_id) +{ + struct uia_event *event = heap_alloc_zero(sizeof(*event)); + + *out_event = NULL; + if (!event) + return E_OUTOFMEMORY; + + event->IWineUiaEvent_iface.lpVtbl = &uia_event_vtbl; + event->ref = 1; + event->runtime_id = runtime_id; + event->event_id = event_id; + event->scope = scope; + event->cback = cback; + + *out_event = event; + return S_OK; +} + +/*********************************************************************** + * UiaAddEvent (uiautomationcore.@) + */ +HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback *callback, enum TreeScope scope, + PROPERTYID *prop_ids, int prop_ids_count, struct UiaCacheRequest *cache_req, HUIAEVENT *huiaevent) +{ + const struct uia_event_info *event_info = uia_event_info_from_id(event_id); + struct uia_event *event; + SAFEARRAY *sa; + HRESULT hr; + + TRACE("(%p, %d, %p, %#x, %p, %d, %p, %p)\n", huianode, event_id, callback, scope, prop_ids, prop_ids_count, + cache_req, huiaevent); + + if (!huianode || !callback || !cache_req || !huiaevent) + return E_INVALIDARG; + + if (!event_info) + WARN("No event information for event ID %d\n", event_id); + + *huiaevent = NULL; + if (event_info && (event_info->event_arg_type == EventArgsType_PropertyChanged)) + { + FIXME("Property changed event registration currently unimplemented\n"); + return E_NOTIMPL; + } + + hr = UiaGetRuntimeId(huianode, &sa); + if (FAILED(hr)) + return hr; + + hr = create_uia_event(&event, event_id, scope, callback, sa); + if (FAILED(hr)) + { + SafeArrayDestroy(sa); + return hr; + } + + *huiaevent = (HUIAEVENT)event; + + return S_OK; +} diff --git a/dlls/uiautomationcore/uia_ids.c b/dlls/uiautomationcore/uia_ids.c index d23ba14c333..01e52f6f45a 100644 --- a/dlls/uiautomationcore/uia_ids.c +++ b/dlls/uiautomationcore/uia_ids.c @@ -413,6 +413,17 @@ static const struct uia_event_info default_uia_events[] = { EventArgsType_Simple, }, };
+static const int event_id_idx[] = { + 0x23, 0x1d, 0x0d, 0x0b, 0x19, 0x06, 0x07, 0x0f, + 0x0a, 0x1e, 0x1b, 0x1a, 0x22, 0x00, 0x18, 0x10, + 0x01, 0x20, 0x08, 0x17, 0x15, 0x13, 0x0e, 0x0c, + 0x14, 0x09, 0x03, 0x21, 0x12, 0x16, 0x05, 0x1c, + 0x02, 0x11, 0x04, 0x1f, +}; + +#define EVENT_ID_MIN 20000 +#define EVENT_ID_MAX (EVENT_ID_MIN + ARRAY_SIZE(default_uia_events)) + static const struct uia_event_info *uia_event_info_from_guid(const GUID *guid) { struct uia_event_info *event; @@ -424,6 +435,14 @@ static const struct uia_event_info *uia_event_info_from_guid(const GUID *guid) return NULL; }
+const struct uia_event_info *uia_event_info_from_id(EVENTID event_id) +{ + if ((event_id < EVENT_ID_MIN) || (event_id > EVENT_ID_MAX)) + return NULL; + + return &default_uia_events[event_id_idx[event_id - EVENT_ID_MIN]]; +} + /* Sorted by GUID. */ static const struct uia_pattern_info default_uia_patterns[] = { { &ScrollItem_Pattern_GUID, UIA_ScrollItemPatternId, diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 8bc0436697d..d3f46874f01 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -147,6 +147,7 @@ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN;
/* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN; +const struct uia_event_info *uia_event_info_from_id(EVENTID event_id) DECLSPEC_HIDDEN; const struct uia_pattern_info *uia_pattern_info_from_id(PATTERNID pattern_id) DECLSPEC_HIDDEN; const struct uia_control_type_info *uia_control_type_info_from_id(CONTROLTYPEID control_type_id) DECLSPEC_HIDDEN;