From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/Makefile.in | 3 +- dlls/uiautomationcore/uia_client.c | 87 ---------------------- dlls/uiautomationcore/uia_private.h | 7 +- dlls/uiautomationcore/uia_utils.c | 110 ++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 90 deletions(-) create mode 100644 dlls/uiautomationcore/uia_utils.c
diff --git a/dlls/uiautomationcore/Makefile.in b/dlls/uiautomationcore/Makefile.in index 8156807c334..f0f2a853cfb 100644 --- a/dlls/uiautomationcore/Makefile.in +++ b/dlls/uiautomationcore/Makefile.in @@ -10,7 +10,8 @@ C_SRCS = \ uia_event.c \ uia_ids.c \ uia_main.c \ - uia_provider.c + uia_provider.c \ + uia_utils.c
IDL_SRCS = \ uia_classes.idl \ diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 4a14fe8e48a..9ea66e27d61 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -58,93 +58,6 @@ static HRESULT add_node_to_node_array(struct uia_node_array *out_nodes, HUIANODE return S_OK; }
-static HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) -{ - LONG ubound; - HRESULT hr; - - *lbound = *elems = 0; - hr = SafeArrayGetLBound(sa, dim, lbound); - if (FAILED(hr)) - return hr; - - hr = SafeArrayGetUBound(sa, dim, &ubound); - if (FAILED(hr)) - return hr; - - *elems = (ubound - (*lbound)) + 1; - return S_OK; -} - -HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) -{ - UINT dims; - - *lbound = *elems = 0; - dims = SafeArrayGetDim(sa); - if (dims != 1) - { - WARN("Invalid dimensions %d for safearray.\n", dims); - return E_FAIL; - } - - return get_safearray_dim_bounds(sa, 1, lbound, elems); -} - -int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) -{ - LONG i, idx, lbound[2], elems[2]; - int val[2]; - HRESULT hr; - - hr = get_safearray_bounds(sa1, &lbound[0], &elems[0]); - if (FAILED(hr)) - { - ERR("Failed to get safearray bounds from sa1 with hr %#lx\n", hr); - return -1; - } - - hr = get_safearray_bounds(sa2, &lbound[1], &elems[1]); - if (FAILED(hr)) - { - ERR("Failed to get safearray bounds from sa2 with hr %#lx\n", hr); - return -1; - } - - if (elems[0] != elems[1]) - return (elems[0] > elems[1]) - (elems[0] < elems[1]); - - if (prop_type != UIAutomationType_IntArray) - { - FIXME("Array type %#x value comparison currently unimplemented.\n", prop_type); - return -1; - } - - for (i = 0; i < elems[0]; i++) - { - idx = lbound[0] + i; - hr = SafeArrayGetElement(sa1, &idx, &val[0]); - if (FAILED(hr)) - { - ERR("Failed to get element from sa1 with hr %#lx\n", hr); - return -1; - } - - idx = lbound[1] + i; - hr = SafeArrayGetElement(sa2, &idx, &val[1]); - if (FAILED(hr)) - { - ERR("Failed to get element from sa2 with hr %#lx\n", hr); - return -1; - } - - if (val[0] != val[1]) - return (val[0] > val[1]) - (val[0] < val[1]); - } - - return 0; -} - static void clear_uia_node_ptr_safearray(SAFEARRAY *sa, LONG elems) { HUIANODE node; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 9ee5c30aeef..5d3a64cb776 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -152,8 +152,6 @@ static inline BOOL uia_array_reserve(void **elements, SIZE_T *capacity, SIZE_T c }
/* uia_client.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, @@ -179,3 +177,8 @@ void uia_provider_thread_remove_node(HUIANODE node) DECLSPEC_HIDDEN; LRESULT uia_lresult_from_node(HUIANODE huianode) DECLSPEC_HIDDEN; HRESULT create_msaa_provider(IAccessible *acc, long child_id, HWND hwnd, BOOL known_root_acc, IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN; + +/* uia_utils.c */ +HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; +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; diff --git a/dlls/uiautomationcore/uia_utils.c b/dlls/uiautomationcore/uia_utils.c new file mode 100644 index 00000000000..67c01eed8c3 --- /dev/null +++ b/dlls/uiautomationcore/uia_utils.c @@ -0,0 +1,110 @@ +/* + * 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); + +HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) +{ + LONG ubound; + HRESULT hr; + + *lbound = *elems = 0; + hr = SafeArrayGetLBound(sa, dim, lbound); + if (FAILED(hr)) + return hr; + + hr = SafeArrayGetUBound(sa, dim, &ubound); + if (FAILED(hr)) + return hr; + + *elems = (ubound - (*lbound)) + 1; + return S_OK; +} + +HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) +{ + UINT dims; + + *lbound = *elems = 0; + dims = SafeArrayGetDim(sa); + if (dims != 1) + { + WARN("Invalid dimensions %d for safearray.\n", dims); + return E_FAIL; + } + + return get_safearray_dim_bounds(sa, 1, lbound, elems); +} + +int uia_compare_safearrays(SAFEARRAY *sa1, SAFEARRAY *sa2, int prop_type) +{ + LONG i, idx, lbound[2], elems[2]; + int val[2]; + HRESULT hr; + + hr = get_safearray_bounds(sa1, &lbound[0], &elems[0]); + if (FAILED(hr)) + { + ERR("Failed to get safearray bounds from sa1 with hr %#lx\n", hr); + return -1; + } + + hr = get_safearray_bounds(sa2, &lbound[1], &elems[1]); + if (FAILED(hr)) + { + ERR("Failed to get safearray bounds from sa2 with hr %#lx\n", hr); + return -1; + } + + if (elems[0] != elems[1]) + return (elems[0] > elems[1]) - (elems[0] < elems[1]); + + if (prop_type != UIAutomationType_IntArray) + { + FIXME("Array type %#x value comparison currently unimplemented.\n", prop_type); + return -1; + } + + for (i = 0; i < elems[0]; i++) + { + idx = lbound[0] + i; + hr = SafeArrayGetElement(sa1, &idx, &val[0]); + if (FAILED(hr)) + { + ERR("Failed to get element from sa1 with hr %#lx\n", hr); + return -1; + } + + idx = lbound[1] + i; + hr = SafeArrayGetElement(sa2, &idx, &val[1]); + if (FAILED(hr)) + { + ERR("Failed to get element from sa2 with hr %#lx\n", hr); + return -1; + } + + if (val[0] != val[1]) + return (val[0] > val[1]) - (val[0] < val[1]); + } + + return 0; +}
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 65 +++--------------------- dlls/uiautomationcore/uia_private.h | 3 ++ dlls/uiautomationcore/uia_utils.c | 76 +++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 57 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 9ea66e27d61..32f16c0731d 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -169,18 +169,6 @@ exit: } }
-static HRESULT get_global_interface_table(IGlobalInterfaceTable **git) -{ - HRESULT hr; - - hr = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, NULL, - CLSCTX_INPROC_SERVER, &IID_IGlobalInterfaceTable, (void **)git); - if (FAILED(hr)) - WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr); - - return hr; -} - static HWND get_hwnd_from_provider(IRawElementProviderSimple *elprov) { IRawElementProviderSimple *host_prov; @@ -428,16 +416,8 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface) { if (node->git_cookie[i]) { - IGlobalInterfaceTable *git; - HRESULT hr; - - hr = get_global_interface_table(&git); - if (SUCCEEDED(hr)) - { - hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[i]); - if (FAILED(hr)) - WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr); - } + if (FAILED(unregister_interface_in_git(node->git_cookie[i]))) + WARN("Failed to get revoke provider interface from GIT\n"); }
if (node->prov[i]) @@ -472,21 +452,13 @@ static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, int idx, IWineU prov_type = get_node_provider_type_at_idx(node, idx); if (node->git_cookie[prov_type]) { - IGlobalInterfaceTable *git; IWineUiaProvider *prov; HRESULT hr;
- hr = get_global_interface_table(&git); + hr = get_interface_in_git(&IID_IWineUiaProvider, node->git_cookie[prov_type], (IUnknown **)&prov); if (FAILED(hr)) return hr;
- hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, node->git_cookie[prov_type], - &IID_IWineUiaProvider, (void **)&prov); - if (FAILED(hr)) - { - ERR("Failed to get provider interface from GlobalInterfaceTable, hr %#lx\n", hr); - return hr; - } *out_prov = prov; } else @@ -542,16 +514,8 @@ static HRESULT WINAPI uia_node_disconnect(IWineUiaNode *iface) prov_type = get_node_provider_type_at_idx(node, 0); if (node->git_cookie[prov_type]) { - IGlobalInterfaceTable *git; - HRESULT hr; - - hr = get_global_interface_table(&git); - if (SUCCEEDED(hr)) - { - hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie[prov_type]); - if (FAILED(hr)) - WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr); - } + if (FAILED(unregister_interface_in_git(node->git_cookie[prov_type]))) + WARN("Failed to get revoke provider interface from GIT\n"); node->git_cookie[prov_type] = 0; }
@@ -639,7 +603,6 @@ static HRESULT prepare_uia_node(struct uia_node *node) for (i = 0; i < PROV_TYPE_COUNT; i++) { enum ProviderOptions prov_opts; - IGlobalInterfaceTable *git; struct uia_provider *prov; HRESULT hr;
@@ -660,12 +623,8 @@ static HRESULT prepare_uia_node(struct uia_node *node) */ if (prov_opts & ProviderOptions_UseComThreading) { - hr = get_global_interface_table(&git); - if (FAILED(hr)) - return hr; - - hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface, - &IID_IWineUiaProvider, &node->git_cookie[i]); + hr = register_interface_in_git((IUnknown *)&prov->IWineUiaProvider_iface, &IID_IWineUiaProvider, + &node->git_cookie[i]); if (FAILED(hr)) return hr; } @@ -2260,7 +2219,6 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU { IWineUiaProvider *provider_iface = NULL; struct uia_nested_node_provider *prov; - IGlobalInterfaceTable *git; IWineUiaNode *nested_node; int prov_opts, prov_type; DWORD git_cookie; @@ -2339,14 +2297,7 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU * We need to use the GIT on all nested node providers so that our * IWineUiaNode proxy is used in the correct apartment. */ - hr = get_global_interface_table(&git); - if (FAILED(hr)) - { - IWineUiaProvider_Release(&prov->IWineUiaProvider_iface); - return hr; - } - - hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface, + hr = register_interface_in_git((IUnknown *)(IUnknown *)&prov->IWineUiaProvider_iface, &IID_IWineUiaProvider, &git_cookie); if (FAILED(hr)) { diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 5d3a64cb776..faec42f5d0e 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -179,6 +179,9 @@ HRESULT create_msaa_provider(IAccessible *acc, long child_id, HWND hwnd, BOOL kn IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN;
/* uia_utils.c */ +HRESULT register_interface_in_git(IUnknown *iface, REFIID riid, DWORD *ret_cookie) DECLSPEC_HIDDEN; +HRESULT unregister_interface_in_git(DWORD git_cookie) DECLSPEC_HIDDEN; +HRESULT get_interface_in_git(REFIID riid, DWORD git_cookie, IUnknown **ret_iface) DECLSPEC_HIDDEN; HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) DECLSPEC_HIDDEN; 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; diff --git a/dlls/uiautomationcore/uia_utils.c b/dlls/uiautomationcore/uia_utils.c index 67c01eed8c3..ef7cf5ff9d2 100644 --- a/dlls/uiautomationcore/uia_utils.c +++ b/dlls/uiautomationcore/uia_utils.c @@ -22,6 +22,82 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+/* + * Global interface table helper functions. + */ +static HRESULT get_global_interface_table(IGlobalInterfaceTable **git) +{ + HRESULT hr; + + hr = CoCreateInstance(&CLSID_StdGlobalInterfaceTable, NULL, + CLSCTX_INPROC_SERVER, &IID_IGlobalInterfaceTable, (void **)git); + if (FAILED(hr)) + WARN("Failed to get GlobalInterfaceTable, hr %#lx\n", hr); + + return hr; +} + +HRESULT register_interface_in_git(IUnknown *iface, REFIID riid, DWORD *ret_cookie) +{ + IGlobalInterfaceTable *git; + DWORD git_cookie; + HRESULT hr; + + *ret_cookie = 0; + hr = get_global_interface_table(&git); + if (FAILED(hr)) + return hr; + + hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, iface, riid, &git_cookie); + if (FAILED(hr)) + { + WARN("Failed to register interface in GlobalInterfaceTable, hr %#lx\n", hr); + return hr; + } + + *ret_cookie = git_cookie; + + return S_OK; +} + +HRESULT unregister_interface_in_git(DWORD git_cookie) +{ + IGlobalInterfaceTable *git; + HRESULT hr; + + hr = get_global_interface_table(&git); + if (FAILED(hr)) + return hr; + + hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, git_cookie); + if (FAILED(hr)) + WARN("Failed to revoke interface from GlobalInterfaceTable, hr %#lx\n", hr); + + return hr; +} + +HRESULT get_interface_in_git(REFIID riid, DWORD git_cookie, IUnknown **ret_iface) +{ + IGlobalInterfaceTable *git; + IUnknown *iface; + HRESULT hr; + + hr = get_global_interface_table(&git); + if (FAILED(hr)) + return hr; + + hr = IGlobalInterfaceTable_GetInterfaceFromGlobal(git, git_cookie, riid, (void **)&iface); + if (FAILED(hr)) + { + ERR("Failed to get interface from Global Interface Table, hr %#lx\n", hr); + return hr; + } + + *ret_iface = iface; + + return S_OK; +} + HRESULT get_safearray_dim_bounds(SAFEARRAY *sa, UINT dim, LONG *lbound, LONG *elems) { LONG ubound;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 2 +- dlls/uiautomationcore/uia_classes.idl | 10 ++ dlls/uiautomationcore/uia_event.c | 149 +++++++++++++++++++-- dlls/uiautomationcore/uia_private.h | 2 +- 4 files changed, 151 insertions(+), 12 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 2dd35199186..3ff530057f6 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13810,7 +13810,7 @@ static DWORD WINAPI uia_add_event_test_thread(LPVOID param) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); ok(Provider2.ref == 1, "Unexpected refcnt %ld\n", Provider2.ref); - todo_wine ok(Provider2.last_call_tid == data->exp_thread_id || + ok(Provider2.last_call_tid == data->exp_thread_id || broken(Provider2.last_call_tid == GetCurrentThreadId()), "Expected method call on separate thread\n"); ok(Provider2.advise_events_removed_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event removed, event ID %d\n", Provider.advise_events_removed_event_id); diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index 3697a4024f5..79bf8556326 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -54,6 +54,16 @@ library UIA_wine_private { importlib("stdole2.tlb");
+ [ + object, + uuid(9a754e12-e570-49ab-b223-6f6871007d28), + pointer_default(unique), + ] + interface IWineUiaEventAdviser : IUnknown + { + HRESULT advise([in]BOOL advise_added, [in]LONG_PTR huiaevent); + } + [ object, uuid(5e60162c-ab0e-4e22-a61d-3a3acd442aba), diff --git a/dlls/uiautomationcore/uia_event.c b/dlls/uiautomationcore/uia_event.c index f627e1813cd..7fd4eda8ead 100644 --- a/dlls/uiautomationcore/uia_event.c +++ b/dlls/uiautomationcore/uia_event.c @@ -63,7 +63,7 @@ static ULONG WINAPI uia_event_Release(IWineUiaEvent *iface)
SafeArrayDestroy(event->runtime_id); for (i = 0; i < event->event_advisers_count; i++) - IRawElementProviderAdviseEvents_Release(event->event_advisers[i]); + IWineUiaEventAdviser_Release(event->event_advisers[i]); heap_free(event->event_advisers); heap_free(event); } @@ -81,12 +81,7 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_
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); + hr = IWineUiaEventAdviser_advise(event->event_advisers[i], advise_added, (UINT_PTR)event); if (FAILED(hr)) return hr; } @@ -95,7 +90,7 @@ static HRESULT WINAPI uia_event_advise_events(IWineUiaEvent *iface, BOOL advise_ if (!advise_added) { for (i = 0; i < event->event_advisers_count; i++) - IRawElementProviderAdviseEvents_Release(event->event_advisers[i]); + IWineUiaEventAdviser_Release(event->event_advisers[i]); heap_free(event->event_advisers); event->event_advisers_count = event->event_advisers_arr_size = 0; } @@ -138,14 +133,148 @@ static HRESULT create_uia_event(struct uia_event **out_event, int event_id, int return S_OK; }
+/* + * IWineUiaEventAdviser interface. + */ +struct uia_event_adviser { + IWineUiaEventAdviser IWineUiaEventAdviser_iface; + LONG ref; + + IRawElementProviderAdviseEvents *advise_events; + DWORD git_cookie; +}; + +static inline struct uia_event_adviser *impl_from_IWineUiaEventAdviser(IWineUiaEventAdviser *iface) +{ + return CONTAINING_RECORD(iface, struct uia_event_adviser, IWineUiaEventAdviser_iface); +} + +static HRESULT WINAPI uia_event_adviser_QueryInterface(IWineUiaEventAdviser *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IWineUiaEventAdviser) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IWineUiaEventAdviser_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI uia_event_adviser_AddRef(IWineUiaEventAdviser *iface) +{ + struct uia_event_adviser *adv_events = impl_from_IWineUiaEventAdviser(iface); + ULONG ref = InterlockedIncrement(&adv_events->ref); + + TRACE("%p, refcount %ld\n", adv_events, ref); + return ref; +} + +static ULONG WINAPI uia_event_adviser_Release(IWineUiaEventAdviser *iface) +{ + struct uia_event_adviser *adv_events = impl_from_IWineUiaEventAdviser(iface); + ULONG ref = InterlockedDecrement(&adv_events->ref); + + TRACE("%p, refcount %ld\n", adv_events, ref); + if (!ref) + { + if (adv_events->git_cookie) + { + if (FAILED(unregister_interface_in_git(adv_events->git_cookie))) + WARN("Failed to revoke advise events interface from GIT\n"); + } + IRawElementProviderAdviseEvents_Release(adv_events->advise_events); + heap_free(adv_events); + } + + return ref; +} + +static HRESULT WINAPI uia_event_adviser_advise(IWineUiaEventAdviser *iface, BOOL advise_added, LONG_PTR huiaevent) +{ + struct uia_event_adviser *adv_events = impl_from_IWineUiaEventAdviser(iface); + struct uia_event *event_data = (struct uia_event *)huiaevent; + IRawElementProviderAdviseEvents *advise_events; + HRESULT hr; + + TRACE("%p, %d, %#Ix\n", adv_events, advise_added, huiaevent); + + if (adv_events->git_cookie) + { + hr = get_interface_in_git(&IID_IRawElementProviderAdviseEvents, adv_events->git_cookie, + (IUnknown **)&advise_events); + if (FAILED(hr)) + return hr; + } + else + { + advise_events = adv_events->advise_events; + IRawElementProviderAdviseEvents_AddRef(advise_events); + } + + if (advise_added) + hr = IRawElementProviderAdviseEvents_AdviseEventAdded(advise_events, event_data->event_id, NULL); + else + hr = IRawElementProviderAdviseEvents_AdviseEventRemoved(advise_events, event_data->event_id, NULL); + + IRawElementProviderAdviseEvents_Release(advise_events); + return hr; +} + +static const IWineUiaEventAdviserVtbl uia_event_adviser_vtbl = { + uia_event_adviser_QueryInterface, + uia_event_adviser_AddRef, + uia_event_adviser_Release, + uia_event_adviser_advise, +}; + HRESULT uia_event_add_provider_event_adviser(IRawElementProviderAdviseEvents *advise_events, struct uia_event *event) { + struct uia_event_adviser *adv_events; + IRawElementProviderSimple *elprov; + enum ProviderOptions prov_opts; + HRESULT hr; + + hr = IRawElementProviderAdviseEvents_QueryInterface(advise_events, &IID_IRawElementProviderSimple, + (void **)&elprov); + if (FAILED(hr)) + { + ERR("Failed to get IRawElementProviderSimple from advise events\n"); + return E_FAIL; + } + + hr = IRawElementProviderSimple_get_ProviderOptions(elprov, &prov_opts); + IRawElementProviderSimple_Release(elprov); + if (FAILED(hr)) + return hr; + + if (!(adv_events = heap_alloc_zero(sizeof(*adv_events)))) + return E_OUTOFMEMORY; + + if (prov_opts & ProviderOptions_UseComThreading) + { + hr = register_interface_in_git((IUnknown *)advise_events, &IID_IRawElementProviderAdviseEvents, + &adv_events->git_cookie); + if (FAILED(hr)) + { + heap_free(adv_events); + return hr; + } + } + + adv_events->IWineUiaEventAdviser_iface.lpVtbl = &uia_event_adviser_vtbl; + adv_events->ref = 1; + adv_events->advise_events = advise_events; + IRawElementProviderAdviseEvents_AddRef(advise_events); + if (!uia_array_reserve((void **)&event->event_advisers, &event->event_advisers_arr_size, event->event_advisers_count + 1, sizeof(*event->event_advisers))) + { + IWineUiaEventAdviser_Release(&adv_events->IWineUiaEventAdviser_iface); return E_OUTOFMEMORY; + }
- event->event_advisers[event->event_advisers_count] = advise_events; - IRawElementProviderAdviseEvents_AddRef(advise_events); + event->event_advisers[event->event_advisers_count] = &adv_events->IWineUiaEventAdviser_iface; event->event_advisers_count++;
return S_OK; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index faec42f5d0e..bd5ca714b94 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -102,7 +102,7 @@ struct uia_event int event_id; int scope;
- IRawElementProviderAdviseEvents **event_advisers; + IWineUiaEventAdviser **event_advisers; int event_advisers_count; SIZE_T event_advisers_arr_size;
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 14 ++--- dlls/uiautomationcore/uia_client.c | 63 ++++++++++++++++++++-- 2 files changed, 66 insertions(+), 11 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 3ff530057f6..838ff3fbe11 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -13746,7 +13746,7 @@ static const struct prov_method_sequence event_seq5[] = { { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win10v1809+. */ { &Provider, FRAG_GET_FRAGMENT_ROOT }, { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, /* Called twice on Win11. */ - { &Provider, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS, METHOD_TODO }, + { &Provider, FRAG_GET_EMBEDDED_FRAGMENT_ROOTS }, 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+. */ @@ -13756,8 +13756,8 @@ static const struct prov_method_sequence event_seq5[] = {
static const struct prov_method_sequence event_seq6[] = { { &Provider, ADVISE_EVENTS_EVENT_REMOVED }, - { &Provider_child, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, - { &Provider_child2, ADVISE_EVENTS_EVENT_REMOVED, METHOD_TODO }, + { &Provider_child, ADVISE_EVENTS_EVENT_REMOVED }, + { &Provider_child2, ADVISE_EVENTS_EVENT_REMOVED }, { 0 }, };
@@ -14121,13 +14121,13 @@ static void test_UiaAddEvent(void) ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); ok(!!event, "event == NULL\n"); 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); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child2.ref == 2, "Unexpected refcnt %ld\n", Provider_child2.ref); 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, + 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); - todo_wine ok(Provider_child2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, + ok(Provider_child2.advise_events_added_event_id == UIA_AutomationFocusChangedEventId, "Unexpected advise event added, event ID %d\n", Provider_child2.advise_events_added_event_id);
method_sequences_enabled = TRUE; diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 32f16c0731d..9c5595eaf4b 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -1722,6 +1722,7 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT struct uia_event *event = (struct uia_event *)huiaevent; IRawElementProviderFragmentRoot *elroot; IRawElementProviderFragment *elfrag; + SAFEARRAY *embedded_roots = NULL; HRESULT hr;
TRACE("%p, %#Ix\n", iface, huiaevent); @@ -1731,9 +1732,19 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT return S_OK;
hr = IRawElementProviderFragment_get_FragmentRoot(elfrag, &elroot); - IRawElementProviderFragment_Release(elfrag); if (FAILED(hr)) - return hr; + goto exit; + + /* + * For now, we only support embedded fragment roots on providers that + * don't represent a nested node. + */ + if (event->scope & (TreeScope_Descendants | TreeScope_Children) && !prov->return_nested_node) + { + hr = IRawElementProviderFragment_GetEmbeddedFragmentRoots(elfrag, &embedded_roots); + if (FAILED(hr)) + WARN("GetEmbeddedFragmentRoots failed with hr %#lx\n", hr); + }
if (elroot) { @@ -1747,11 +1758,55 @@ static HRESULT WINAPI uia_provider_attach_event(IWineUiaProvider *iface, LONG_PT hr = uia_event_add_provider_event_adviser(advise_events, event); IRawElementProviderAdviseEvents_Release(advise_events); if (FAILED(hr)) - return hr; + goto exit; } }
- return S_OK; + if (embedded_roots) + { + LONG lbound, elems, i; + HUIANODE node; + + hr = get_safearray_bounds(embedded_roots, &lbound, &elems); + if (FAILED(hr)) + goto exit; + + for (i = 0; i < elems; i++) + { + IRawElementProviderSimple *elprov; + IUnknown *unk; + LONG idx; + + idx = lbound + i; + hr = SafeArrayGetElement(embedded_roots, &idx, &unk); + if (FAILED(hr)) + goto exit; + + hr = IUnknown_QueryInterface(unk, &IID_IRawElementProviderSimple, (void **)&elprov); + IUnknown_Release(unk); + if (FAILED(hr)) + goto exit; + + hr = create_uia_node_from_elprov(elprov, &node, !prov->return_nested_node); + IRawElementProviderSimple_Release(elprov); + if (SUCCEEDED(hr)) + { + hr = attach_event_to_uia_node(node, event); + UiaNodeRelease(node); + if (FAILED(hr)) + { + WARN("attach_event_to_uia_node failed with hr %#lx\n", hr); + goto exit; + } + } + } + } + +exit: + IRawElementProviderFragment_Release(elfrag); + SafeArrayDestroy(embedded_roots); + + return hr; }
static const IWineUiaProviderVtbl uia_provider_vtbl = {
This merge request was approved by Esme Povirk.