diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 596104484ac..2dd35199186 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13714,12 +13714,12 @@ static const struct prov_method_sequence event_seq1[] = { static const struct prov_method_sequence event_seq2[] = { { &Provider, FRAG_GET_RUNTIME_ID }, { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO }, + { &Provider, FRAG_GET_FRAGMENT_ROOT }, { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */ NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */ { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */ { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_ADDED }, { 0 }, }; @@ -13737,25 +13737,25 @@ static const struct prov_method_sequence event_seq3[] = { }; static const struct prov_method_sequence event_seq4[] = { - { &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_REMOVED }, { 0 }, }; static const struct prov_method_sequence event_seq5[] = { { &Provider, FRAG_GET_RUNTIME_ID }, { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_TODO }, + { &Provider, FRAG_GET_FRAGMENT_ROOT }, { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */ { &Provider, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS, METHOD_TODO }, NODE_CREATE_SEQ_OPTIONAL(&Provider), /* Only done in Win11. */ { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win8+. */ { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ - { &Provider, ADVISE_EVENTS_EVENT_ADDED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_ADDED }, { 0 }, }; static const struct prov_method_sequence event_seq6[] = { - { &Provider, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { &Provider, ADVISE_EVENTS_EVENT_REMOVED }, { &Provider_child, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, { &Provider_child2, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, { 0 }, @@ -13812,7 +13812,7 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param) ok(Provider2.ref == 1, "Unexpected refcnt %ld\n", Provider2.ref); todo_wine ok(Provider2.last_call_tid == data->exp_thread_id || broken(Provider2.last_call_tid == GetCurrentThreadId()), "Expected method call on separate thread\n"); - todo_wine ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId, + ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event removed, event ID %d\n", Provider.advise_events_removed_event_id); CoUninitialize(); @@ -13931,7 +13931,7 @@ static void test_UiaAddEvent(void) &event); 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); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); ok_method_sequence(event_seq2, "event_seq2"); /* @@ -13965,7 +13965,7 @@ static void test_UiaAddEvent(void) &event); 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); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); ok_method_sequence(event_seq2, "event_seq2"); /* Event callback is now invoked since we can match by runtime ID. */ @@ -14005,7 +14005,7 @@ static void test_UiaAddEvent(void) &event); 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); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); ok_method_sequence(event_seq5, "event_seq5"); /* @@ -14052,7 +14052,7 @@ static void test_UiaAddEvent(void) &cache_req, &event); 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); + ok(Provider.ref == 3, "Unexpected refcnt %ld\n", Provider.ref); /* Raised an event on Provider_child_child. */ init_node_provider_desc(&exp_node_desc, GetCurrentProcessId(), NULL); @@ -14086,9 +14086,9 @@ static void test_UiaAddEvent(void) 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(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); - todo_wine ok(Provider2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + ok(Provider2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); thread_data.exp_thread_id = GetCurrentThreadId(); @@ -14120,10 +14120,10 @@ static void test_UiaAddEvent(void) &cache_req, &event); 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); + 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); - todo_wine ok(Provider.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + ok(Provider.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event added, event ID %d\n", Provider.advise_events_added_event_id); todo_wine ok(Provider_child.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event added, event ID %d\n", Provider_child.advise_events_added_event_id); diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 83ff63fdd70..3697a4024f5 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -61,6 +61,7 @@ library UIA_wine_private ] interface IWineUiaEvent : IUnknown { + HRESULT advise_events([in]BOOL advise_added); } [ @@ -76,6 +77,7 @@ library UIA_wine_private HRESULT has_parent([out, retval]BOOL *out_val); 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); } [ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index ced728428a3..4a14fe8e48a 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -462,6 +462,21 @@ static HRESULT get_focus_from_node_provider(IWineUiaNode *node, int idx, VARIANT return hr; } +static HRESULT attach_event_to_node_provider(IWineUiaNode *node, int idx, HUIAEVENT huiaevent) +{ + IWineUiaProvider *prov; + HRESULT hr; + + hr = IWineUiaNode_get_provider(node, idx, &prov); + if (FAILED(hr)) + return hr; + + hr = IWineUiaProvider_attach_event(prov, (LONG_PTR)huiaevent); + IWineUiaProvider_Release(prov); + + return hr; +} + /* * IWineUiaNode interface. */ @@ -1285,6 +1300,22 @@ exit: return hr; } +HRESULT attach_event_to_uia_node(HUIANODE node, struct uia_event *event) +{ + struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + HRESULT hr = S_OK; + int i; + + for (i = 0; i < node_data->prov_count; i++) + { + hr = attach_event_to_node_provider(&node_data->IWineUiaNode_iface, i, (HUIAEVENT)event); + if (FAILED(hr)) + return hr; + } + + return hr; +} + /* * IWineUiaProvider interface. */ @@ -1813,6 +1844,44 @@ static HRESULT WINAPI uia_provider_get_focus(IWineUiaProvider *iface, VARIANT *o return hr; } +static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent) +{ + struct uia_provider *prov = impl_from_IWineUiaProvider(iface); + struct uia_event *event = (struct uia_event *)huiaevent; + IRawElementProviderFragmentRoot *elroot; + IRawElementProviderFragment *elfrag; + HRESULT hr; + + TRACE("%p, %#Ix\n", iface, huiaevent); + + hr = IRawElementProviderSimple_QueryInterface(prov->elprov, &IID_IRawElementProviderFragment, (void **)&elfrag); + if (FAILED(hr)) + return S_OK; + + hr = IRawElementProviderFragment_get_FragmentRoot(elfrag, &elroot); + IRawElementProviderFragment_Release(elfrag); + if (FAILED(hr)) + return hr; + + if (elroot) + { + IRawElementProviderAdviseEvents *advise_events; + + hr = IRawElementProviderFragmentRoot_QueryInterface(elroot, &IID_IRawElementProviderAdviseEvents, + (void **)&advise_events); + IRawElementProviderFragmentRoot_Release(elroot); + if (SUCCEEDED(hr)) + { + hr = uia_event_add_provider_event_adviser(advise_events, event); + IRawElementProviderAdviseEvents_Release(advise_events); + if (FAILED(hr)) + return hr; + } + } + + return S_OK; +} + static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_QueryInterface, uia_provider_AddRef, @@ -1822,6 +1891,7 @@ static const IWineUiaProviderVtbl uia_provider_vtbl = { uia_provider_has_parent, uia_provider_navigate, uia_provider_get_focus, + uia_provider_attach_event, }; static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProviderSimple *elprov, @@ -2246,6 +2316,12 @@ static HRESULT WINAPI uia_nested_node_provider_get_focus(IWineUiaProvider *iface return S_OK; } +static HRESULT WINAPI uia_nested_node_provider_attach_event(IWineUiaProvider *iface, LONG_PTR huiaevent) +{ + FIXME("%p, %#Ix: stub\n", iface, huiaevent); + return E_NOTIMPL; +} + static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_QueryInterface, uia_nested_node_provider_AddRef, @@ -2255,6 +2331,7 @@ static const IWineUiaProviderVtbl uia_nested_node_provider_vtbl = { uia_nested_node_provider_has_parent, uia_nested_node_provider_navigate, uia_nested_node_provider_get_focus, + uia_nested_node_provider_attach_event, }; static BOOL is_nested_node_provider(IWineUiaProvider *iface) diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index faeb689a3b6..f627e1813cd 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -22,18 +22,6 @@ 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. */ @@ -71,17 +59,55 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface) TRACE("%p, refcount %ld\n", event, ref); if (!ref) { + int i; + SafeArrayDestroy(event->runtime_id); + for (i = 0; i < event->event_advisers_count; i++) + IRawElementProviderAdviseEvents_Release(event->event_advisers[i]); + heap_free(event->event_advisers); heap_free(event); } return ref; } +static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_added) +{ + struct uia_event *event = impl_from_IWineUiaEvent(iface); + HRESULT hr; + int i; + + TRACE("%p, %d\n", event, advise_added); + + for (i = 0; i < event->event_advisers_count; i++) + { + IRawElementProviderAdviseEvents *adviser = event->event_advisers[i]; + + if (advise_added) + hr = IRawElementProviderAdviseEvents_AdviseEventAdded(adviser, event->event_id, NULL); + else + hr = IRawElementProviderAdviseEvents_AdviseEventRemoved(adviser, event->event_id, NULL); + if (FAILED(hr)) + return hr; + } + + /* Once we've advised of removal, no need to keep the advisers around. */ + if (!advise_added) + { + for (i = 0; i < event->event_advisers_count; i++) + IRawElementProviderAdviseEvents_Release(event->event_advisers[i]); + heap_free(event->event_advisers); + event->event_advisers_count = event->event_advisers_arr_size = 0; + } + + return S_OK; +} + static const IWineUiaEventVtbl uia_event_vtbl = { uia_event_QueryInterface, uia_event_AddRef, uia_event_Release, + uia_event_advise_events, }; static struct uia_event *unsafe_impl_from_IWineUiaEvent(IWineUiaEvent *iface) @@ -112,6 +138,19 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int return S_OK; } +HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) +{ + if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size, + event->event_advisers_count + 1, sizeof(*event->event_advisers))) + return E_OUTOFMEMORY; + + event->event_advisers[event->event_advisers_count] = advise_events; + IRawElementProviderAdviseEvents_AddRef(advise_events); + event->event_advisers_count++; + + return S_OK; +} + /*********************************************************************** * UiaAddEvent (uiautomationcore.@) */ @@ -150,9 +189,21 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback return hr; } + hr = attach_event_to_uia_node(huianode, event); + if (FAILED(hr)) + goto exit; + + hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, TRUE); + if (FAILED(hr)) + goto exit; + *huiaevent = (HUIAEVENT)event; - return S_OK; +exit: + if (FAILED(hr)) + IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + + return hr; } /*********************************************************************** @@ -161,12 +212,17 @@ HRESULT WINAPI UiaAddEvent(HUIANODE huianode, EVENTID event_id, UiaEventCallback HRESULT WINAPI UiaRemoveEvent(HUIAEVENT huiaevent) { struct uia_event *event = unsafe_impl_from_IWineUiaEvent((IWineUiaEvent *)huiaevent); + HRESULT hr; TRACE("(%p)\n", event); if (!event) return E_INVALIDARG; + hr = IWineUiaEvent_advise_events(&event->IWineUiaEvent_iface, FALSE); IWineUiaEvent_Release(&event->IWineUiaEvent_iface); + if (FAILED(hr)) + WARN("advise_events failed with hr %#lx\n", hr); + return S_OK; } diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index d3f46874f01..9ee5c30aeef 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -93,6 +93,22 @@ static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider * return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); } +struct uia_event +{ + IWineUiaEvent IWineUiaEvent_iface; + LONG ref; + + SAFEARRAY *runtime_id; + int event_id; + int scope; + + IRawElementProviderAdviseEvents **event_advisers; + int event_advisers_count; + SIZE_T event_advisers_arr_size; + + UiaEventCallback *cback; +}; + static inline void variant_init_bool(VARIANT *v, BOOL val) { V_VT(v) = VT_BOOL; @@ -139,12 +155,17 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) DECLSPEC_HIDDEN; 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 create_uia_node_from_elprov(IRawElementProviderSimple *elprov, HUIANODE *out_node, BOOL get_hwnd_providers) DECLSPEC_HIDDEN; /* uia_com_client.c */ HRESULT create_uia_iface(IUnknown **iface, BOOL is_cui8) DECLSPEC_HIDDEN; +/* uia_event.c */ +HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, + struct uia_event *event) 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;