Add the ability to disconnect nested node providers, and return element properties for nested nodes.
-- v5: uiautomationcore: Add UIAutomationType_Element property support for nested node providers. uiautomationcore: Implement UiaDisconnectProvider. uiautomationcore: Track all HUIANODEs returned from the provider thread. uiautomationcore: Don't use nested node providers for same-thread HWNDs.
From: Connor McAdams cmcadams@codeweavers.com
When a client requests an HUIANODE for an HWND that belongs to the same thread, use an IRawElementProvider based provider rather than an IWineUiaNode based provider.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 35 ++++---- dlls/uiautomationcore/uia_client.c | 99 ++++++++++++++++------ 2 files changed, 88 insertions(+), 46 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index ff711ccb325..aa5a9afab7f 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -5484,30 +5484,27 @@ static void test_UiaNodeFromHandle(const char *name) Provider_child.prov_opts = ProviderOptions_ServerSideProvider; Provider_child.hwnd = NULL; hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
+ hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = UiaHUiaNodeFromVariant(&v, &node2); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); - VariantClear(&v); - } + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); + }
- Provider_child.expected_tid = GetCurrentThreadId(); - hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); - ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); - ok_method_sequence(node_from_hwnd4, "node_from_hwnd4"); + Provider_child.expected_tid = GetCurrentThreadId(); + hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); + ok_method_sequence(node_from_hwnd4, "node_from_hwnd4");
- ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); - } + ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n");
Provider.expected_tid = Provider_child.expected_tid = 0; ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 1d6360682c2..c2946b03500 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -787,6 +787,11 @@ struct uia_client_thread LONG ref; };
+struct uia_get_node_prov_args { + LRESULT lr; + BOOL unwrap; +}; + static struct uia_client_thread client_thread; static CRITICAL_SECTION client_thread_cs; static CRITICAL_SECTION_DEBUG client_thread_cs_debug = @@ -799,14 +804,17 @@ static CRITICAL_SECTION client_thread_cs = { &client_thread_cs_debug, -1, 0, 0,
#define WM_UIA_CLIENT_GET_NODE_PROV (WM_USER + 1) #define WM_UIA_CLIENT_THREAD_STOP (WM_USER + 2) -static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr); +static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr, BOOL unwrap); static LRESULT CALLBACK uia_client_thread_msg_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_UIA_CLIENT_GET_NODE_PROV: - return create_wine_uia_nested_node_provider((struct uia_node *)lparam, (LRESULT)wparam); + { + struct uia_get_node_prov_args *args = (struct uia_get_node_prov_args *)wparam; + return create_wine_uia_nested_node_provider((struct uia_node *)lparam, args->lr, args->unwrap); + }
default: break; @@ -924,11 +932,13 @@ static void uia_stop_client_thread(void) LeaveCriticalSection(&client_thread_cs); }
-static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr) +static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESULT lr, + BOOL unwrap) { IGlobalInterfaceTable *git; struct uia_provider *prov; IWineUiaNode *nested_node; + DWORD git_cookie; HRESULT hr;
hr = ObjectFromLresult(lr, &IID_IWineUiaNode, 0, (void **)&nested_node); @@ -938,30 +948,64 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU return hr; }
- prov = heap_alloc_zero(sizeof(*prov)); - if (!prov) - return E_OUTOFMEMORY; - - prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl; - prov->node = nested_node; - prov->ref = 1; - node->prov = &prov->IWineUiaProvider_iface; - /* - * We need to use the GIT on all nested node providers so that our - * IWineUiaNode proxy is used in the correct apartment. + * If we're retrieving a node from an HWND that belongs to the same thread + * as the client making the request, return a normal provider instead of a + * nested node provider. */ - hr = get_global_interface_table(&git); - if (FAILED(hr)) - goto exit; + if (unwrap) + { + struct uia_node *node_data = unsafe_impl_from_IWineUiaNode(nested_node);
- hr = IGlobalInterfaceTable_RegisterInterfaceInGlobal(git, (IUnknown *)&prov->IWineUiaProvider_iface, - &IID_IWineUiaProvider, &node->git_cookie); -exit: - if (FAILED(hr)) - IWineUiaProvider_Release(&prov->IWineUiaProvider_iface); + if (!node_data) + { + ERR("Failed to get uia_node structure from nested node\n"); + uia_stop_client_thread(); + return E_FAIL; + }
- return hr; + prov = impl_from_IWineUiaProvider(node_data->prov); + IWineUiaProvider_AddRef(node_data->prov); + git_cookie = node_data->git_cookie; + + node_data->git_cookie = 0; + IWineUiaNode_Release(&node_data->IWineUiaNode_iface); + uia_stop_client_thread(); + } + else + { + prov = heap_alloc_zero(sizeof(*prov)); + if (!prov) + return E_OUTOFMEMORY; + + prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl; + prov->node = nested_node; + prov->ref = 1; + + /* + * 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, + &IID_IWineUiaProvider, &git_cookie); + if (FAILED(hr)) + { + IWineUiaProvider_Release(&prov->IWineUiaProvider_iface); + return hr; + } + } + + node->prov = &prov->IWineUiaProvider_iface; + node->git_cookie = git_cookie; + + return S_OK; }
/* @@ -971,27 +1015,28 @@ exit: */ static HRESULT uia_get_provider_from_hwnd(struct uia_node *node) { - LRESULT lr; + struct uia_get_node_prov_args args;
if (!uia_start_client_thread()) return E_FAIL;
SetLastError(NOERROR); - lr = SendMessageW(node->hwnd, WM_GETOBJECT, 0, UiaRootObjectId); + args.lr = SendMessageW(node->hwnd, WM_GETOBJECT, 0, UiaRootObjectId); if (GetLastError() == ERROR_INVALID_WINDOW_HANDLE) { uia_stop_client_thread(); return UIA_E_ELEMENTNOTAVAILABLE; }
- if (!lr) + if (!args.lr) { FIXME("No native UIA provider for hwnd %p, MSAA proxy currently unimplemented.\n", node->hwnd); uia_stop_client_thread(); return E_NOTIMPL; }
- return SendMessageW(client_thread.hwnd, WM_UIA_CLIENT_GET_NODE_PROV, (WPARAM)lr, (LPARAM)node); + args.unwrap = GetCurrentThreadId() == GetWindowThreadProcessId(node->hwnd, NULL); + return SendMessageW(client_thread.hwnd, WM_UIA_CLIENT_GET_NODE_PROV, (WPARAM)&args, (LPARAM)node); }
/***********************************************************************
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_client.c | 4 +++ dlls/uiautomationcore/uia_private.h | 3 +++ dlls/uiautomationcore/uia_provider.c | 40 +++++++++++++++++++++++++--- 3 files changed, 44 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index c2946b03500..c0fa414accc 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -313,6 +313,8 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface) }
IWineUiaProvider_Release(node->prov); + if (!list_empty(&node->prov_thread_list_entry)) + uia_provider_thread_remove_node((HUIANODE)iface); if (node->nested_node) uia_stop_provider_thread();
@@ -769,6 +771,7 @@ HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE * }
node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl; + list_init(&node->prov_thread_list_entry); node->ref = 1;
*huianode = (void *)&node->IWineUiaNode_iface; @@ -1063,6 +1066,7 @@ HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode)
node->hwnd = hwnd; node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl; + list_init(&node->prov_thread_list_entry); node->ref = 1;
hr = uia_get_provider_from_hwnd(node); diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 1efc8fbd1e4..da7f54c8ce3 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -20,6 +20,7 @@
#include "uiautomation.h" #include "uia_classes.h" +#include "wine/list.h"
extern HMODULE huia_module DECLSPEC_HIDDEN;
@@ -38,6 +39,7 @@ struct uia_node {
HWND hwnd; BOOL nested_node; + struct list prov_thread_list_entry; };
static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface) @@ -50,3 +52,4 @@ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_H
/* uia_provider.c */ void uia_stop_provider_thread(void) DECLSPEC_HIDDEN; +void uia_provider_thread_remove_node(HUIANODE node) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 7b4a6be4042..fe7dff9112e 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1130,6 +1130,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD */ struct uia_provider_thread { + struct list nodes_list; HANDLE hthread; HWND hwnd; LONG ref; @@ -1145,6 +1146,33 @@ static CRITICAL_SECTION_DEBUG provider_thread_cs_debug = }; static CRITICAL_SECTION provider_thread_cs = { &provider_thread_cs_debug, -1, 0, 0, 0, 0 };
+void uia_provider_thread_remove_node(HUIANODE node) +{ + struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + + TRACE("Removing node %p\n", node); + + EnterCriticalSection(&provider_thread_cs); + list_remove(&node_data->prov_thread_list_entry); + list_init(&node_data->prov_thread_list_entry); + LeaveCriticalSection(&provider_thread_cs); +} + +static HRESULT uia_provider_thread_add_node(HUIANODE node) +{ + struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + + TRACE("Adding node %p\n", node); + + node_data->nested_node = TRUE; + + EnterCriticalSection(&provider_thread_cs); + list_add_tail(&provider_thread.nodes_list, &node_data->prov_thread_list_entry); + LeaveCriticalSection(&provider_thread_cs); + + return S_OK; +} + #define WM_GET_OBJECT_UIA_NODE (WM_USER + 1) #define WM_UIA_PROVIDER_THREAD_STOP (WM_USER + 2) static LRESULT CALLBACK uia_provider_thread_msg_proc(HWND hwnd, UINT msg, WPARAM wparam, @@ -1155,11 +1183,14 @@ static LRESULT CALLBACK uia_provider_thread_msg_proc(HWND hwnd, UINT msg, WPARAM case WM_GET_OBJECT_UIA_NODE: { HUIANODE node = (HUIANODE)lparam; - struct uia_node *node_data; LRESULT lr;
- node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); - node_data->nested_node = TRUE; + if (FAILED(uia_provider_thread_add_node(node))) + { + WARN("Failed to add node %p to provider thread list.\n", node); + UiaNodeRelease(node); + return 0; + }
/* * LresultFromObject returns an index into the global atom string table, @@ -1239,6 +1270,7 @@ static BOOL uia_start_provider_thread(void) GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const WCHAR *)uia_start_provider_thread, &hmodule);
+ list_init(&provider_thread.nodes_list); events[0] = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); if (!(provider_thread.hthread = CreateThread(NULL, 0, uia_provider_thread_proc, ready_event, 0, NULL))) @@ -1276,6 +1308,8 @@ void uia_stop_provider_thread(void) { PostMessageW(provider_thread.hwnd, WM_UIA_PROVIDER_THREAD_STOP, 0, 0); CloseHandle(provider_thread.hthread); + if (!list_empty(&provider_thread.nodes_list)) + ERR("Provider thread shutdown with nodes still in the list\n"); memset(&provider_thread, 0, sizeof(provider_thread)); } LeaveCriticalSection(&provider_thread_cs);
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 277 ++++++++++++++++++++- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 101 +++++++- dlls/uiautomationcore/uia_main.c | 6 - dlls/uiautomationcore/uia_private.h | 6 + dlls/uiautomationcore/uia_provider.c | 148 ++++++++++- include/uiautomationcoreapi.h | 1 + 7 files changed, 529 insertions(+), 11 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index aa5a9afab7f..7982bd5fa8b 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -29,6 +29,7 @@ #include "wine/test.h"
static HRESULT (WINAPI *pUiaProviderFromIAccessible)(IAccessible *, long, DWORD, IRawElementProviderSimple **); +static HRESULT (WINAPI *pUiaDisconnectProvider)(IRawElementProviderSimple *);
#define DEFINE_EXPECT(func) \ static int expect_ ## func = 0, called_ ## func = 0 @@ -5083,6 +5084,74 @@ static const struct prov_method_sequence node_from_hwnd8[] = { { 0 } };
+static const struct prov_method_sequence node_from_hwnd9[] = { + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + /* Win10v1507 and below call this. */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, + /* Only done in Windows 8+. */ + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, + { &Provider, FRAG_GET_FRAGMENT_ROOT, METHOD_OPTIONAL }, + /* These three are only done on Windows 7. */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, + { &Provider, FRAG_NAVIGATE, METHOD_OPTIONAL }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_ProviderDescriptionPropertyId */ + { 0 } +}; + +static const struct prov_method_sequence disconnect_prov1[] = { + { &Provider_child, PROV_GET_PROVIDER_OPTIONS }, + /* Win10v1507 and below call this. */ + { &Provider_child, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_child, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + { &Provider_child, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_child, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ + { &Provider_child, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, + { &Provider_child, FRAG_GET_RUNTIME_ID }, + { &Provider_child, FRAG_GET_FRAGMENT_ROOT }, + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { 0 } +}; + +static const struct prov_method_sequence disconnect_prov2[] = { + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + /* Win10v1507 and below call this. */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, + { &Provider, FRAG_GET_RUNTIME_ID }, + { &Provider, FRAG_GET_FRAGMENT_ROOT }, + { 0 } +}; + +static const struct prov_method_sequence disconnect_prov3[] = { + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + /* Win10v1507 and below call this. */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, + { &Provider, FRAG_GET_RUNTIME_ID }, + { 0 } +}; + +static const struct prov_method_sequence disconnect_prov4[] = { + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + /* Win10v1507 and below call this. */ + { &Provider, PROV_GET_PROPERTY_VALUE, METHOD_OPTIONAL }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, PROV_GET_HOST_RAW_ELEMENT_PROVIDER }, + { &Provider, FRAG_NAVIGATE, METHOD_TODO }, /* NavigateDirection_Parent */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, + { 0 } +}; + static void test_UiaNodeFromHandle_client_proc(void) { APTTYPEQUALIFIER apt_qualifier; @@ -5157,8 +5226,8 @@ static void test_UiaNodeFromHandle_client_proc(void)
static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) { + HUIANODE node, node2, node3; HWND hwnd = (HWND)param; - HUIANODE node, node2; WCHAR buf[2048]; HRESULT hr; VARIANT v; @@ -5329,6 +5398,160 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) Sleep(50); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
+ if (!pUiaDisconnectProvider) + { + win_skip("UiaDisconnectProvider not exported by uiautomationcore.dll\n"); + goto exit; + } + + /* + * UiaDisconnectProvider tests. + */ + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + prov_root = &Provider.IRawElementProviderSimple_iface; + Provider.prov_opts = ProviderOptions_ServerSideProvider; + Provider.hwnd = hwnd; + Provider.runtime_id[0] = Provider.runtime_id[1] = 0; + Provider.frag_root = NULL; + Provider.prov_opts = ProviderOptions_ServerSideProvider; + hr = UiaNodeFromHandle(hwnd, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + memset(buf, 0, sizeof(buf)); + + ok(get_nested_provider_desc(V_BSTR(&v), L"Main", FALSE, buf), "Failed to get nested provider description\n"); + check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); + /* Win10v1507 and below have the nested provider as 'Hwnd'. */ + if (get_provider_desc(buf, L"Hwnd(parent link):", NULL)) + check_node_provider_desc(buf, L"Hwnd", L"Provider", TRUE); + else + check_node_provider_desc(buf, L"Main", L"Provider", TRUE); + + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE); + VariantClear(&v); + } + + ok_method_sequence(node_from_hwnd3, "node_from_hwnd3"); + + hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v); + + /* Nodes returned from a nested node will be tracked and disconnectable. */ + Provider_child.prov_opts = ProviderOptions_ServerSideProvider; + Provider_child.runtime_id[0] = UiaAppendRuntimeId; + Provider_child.runtime_id[1] = 2; + Provider_child.hwnd = NULL; + Provider_child.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; + hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + + if (SUCCEEDED(hr)) + { + hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); + } + + hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); + ok_method_sequence(node_from_hwnd6, "node_from_hwnd6"); + + /* Get a new node for the same provider. */ + hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(Provider_child.ref == 3, "Unexpected refcnt %ld\n", Provider_child.ref); + + hr = UiaHUiaNodeFromVariant(&v, &node3); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node3, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); + } + + hr = UiaGetPropertyValue(node3, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); + ok_method_sequence(node_from_hwnd6, "node_from_hwnd6"); + + /* + * Both node2 and node3 represent Provider_child, one call to + * UiaDisconnectProvider disconnects both. + */ + hr = pUiaDisconnectProvider(&Provider_child.IRawElementProviderSimple_iface); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + + hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); + ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx\n", hr); + + hr = UiaGetPropertyValue(node3, UIA_ControlTypePropertyId, &v); + ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx\n", hr); + ok_method_sequence(disconnect_prov1, "disconnect_prov1"); + + ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); + ok(UiaNodeRelease(node3), "UiaNodeRelease returned FALSE\n"); + } + + /* + * Returns same failure code as UiaGetRuntimeId when we fail to get a + * fragment root for AppendRuntimeId. + */ + Provider.hwnd = NULL; + Provider.runtime_id[0] = UiaAppendRuntimeId; + Provider.runtime_id[1] = 2; + Provider.frag_root = NULL; + hr = pUiaDisconnectProvider(&Provider.IRawElementProviderSimple_iface); + ok(hr == E_FAIL, "Unexpected hr %#lx\n", hr); + ok_method_sequence(disconnect_prov2, "disconnect_prov2"); + + /* + * Comparisons for disconnection are only based on RuntimeId comparisons, + * not interface pointer values. If an interface returns a NULL RuntimeId, + * no disconnection will occur. + */ + Provider.runtime_id[0] = Provider.runtime_id[1] = 0; + hr = pUiaDisconnectProvider(&Provider.IRawElementProviderSimple_iface); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx\n", hr); + ok_method_sequence(disconnect_prov3, "disconnect_prov3"); + + hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_uia_prop_val(UIA_ControlTypePropertyId, UIAutomationType_Int, &v); + + /* Finally, disconnect node. */ + Provider.hwnd = hwnd; + hr = pUiaDisconnectProvider(&Provider.IRawElementProviderSimple_iface); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + + hr = UiaGetPropertyValue(node, UIA_ControlTypePropertyId, &v); + ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx\n", hr); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok_method_sequence(disconnect_prov4, "disconnect_prov4"); + +exit: CoUninitialize();
return 0; @@ -5438,6 +5661,55 @@ static void test_UiaNodeFromHandle(const char *name) ok_method_sequence(node_from_hwnd1, "node_from_hwnd1");
todo_wine ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + + /* + * COM initialized, but provider passed into UiaReturnRawElementProvider + * fails to get a valid RuntimeId due to a lack of a fragment root. Win8+ + * will fall back to MSAA proxy. + */ + Provider.prov_opts = ProviderOptions_ServerSideProvider; + Provider.hwnd = NULL; + prov_root = &Provider.IRawElementProviderSimple_iface; + node = (void *)0xdeadbeef; + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + SET_EXPECT_MULTI(winproc_GETOBJECT_CLIENT, 2); + Provider.frag_root = NULL; + Provider.runtime_id[0] = UiaAppendRuntimeId; + Provider.runtime_id[1] = 1; + hr = UiaNodeFromHandle(hwnd, &node); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 1 || broken(Provider.ref == 2), "Unexpected refcnt %ld\n", Provider.ref); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + todo_wine CHECK_CALLED(winproc_GETOBJECT_CLIENT); + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + + if (get_provider_desc(V_BSTR(&v), L"Annotation:", NULL)) + { + check_node_provider_desc(V_BSTR(&v), L"Annotation", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Main", NULL, FALSE); + } + else + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", FALSE); + + check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE); + VariantClear(&v); + } + ok_method_sequence(node_from_hwnd9, "node_from_hwnd9"); + todo_wine ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + /* + * Bug on Windows 8 through Win10v1709 - if we have a RuntimeId failure, + * refcount doesn't get decremented. + */ + ok(Provider.ref == 1 || broken(Provider.ref == 2), "Unexpected refcnt %ld\n", Provider.ref); + if (Provider.ref == 2) + IRawElementProviderSimple_Release(&Provider.IRawElementProviderSimple_iface); + CoUninitialize();
/* @@ -5617,6 +5889,9 @@ START_TEST(uiautomation) pImmDisableIME = NULL; FreeLibrary(hModuleImm32);
+ if (uia_dll) + pUiaDisconnectProvider = (void *)GetProcAddress(uia_dll, "UiaDisconnectProvider"); + argc = winetest_get_mainargs(&argv); if (argc == 3) { diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index e2889901fbb..2fb0e88086b 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -56,5 +56,6 @@ library UIA_wine_private { HRESULT get_provider([out, retval]IWineUiaProvider **out_prov); HRESULT get_prop_val([in]const GUID *prop_guid, [out, retval]VARIANT *ret_val); + HRESULT disconnect(); } } diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index c0fa414accc..a3b04479b13 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -49,6 +49,54 @@ static HRESULT get_safearray_bounds(SAFEARRAY *sa, LONG *lbound, LONG *elems) return S_OK; }
+int uia_compare_runtime_ids(SAFEARRAY *sa1, SAFEARRAY *sa2) +{ + 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]; + + 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]; + } + + return 0; +} + static void clear_uia_node_ptr_safearray(SAFEARRAY *sa, LONG elems) { HUIANODE node; @@ -312,7 +360,8 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface) } }
- IWineUiaProvider_Release(node->prov); + if (node->prov) + IWineUiaProvider_Release(node->prov); if (!list_empty(&node->prov_thread_list_entry)) uia_provider_thread_remove_node((HUIANODE)iface); if (node->nested_node) @@ -328,6 +377,12 @@ static HRESULT WINAPI uia_node_get_provider(IWineUiaNode *iface, IWineUiaProvide { struct uia_node *node = impl_from_IWineUiaNode(iface);
+ if (node->disconnected) + { + *out_prov = NULL; + return UIA_E_ELEMENTNOTAVAILABLE; + } + if (node->git_cookie) { IGlobalInterfaceTable *git; @@ -360,11 +415,18 @@ static HRESULT WINAPI uia_node_get_prop_val(IWineUiaNode *iface, const GUID *pro VARIANT *ret_val) { int prop_id = UiaLookupId(AutomationIdentifierType_Property, prop_guid); + struct uia_node *node = impl_from_IWineUiaNode(iface); HRESULT hr; VARIANT v;
TRACE("%p, %s, %p\n", iface, debugstr_guid(prop_guid), ret_val);
+ if (node->disconnected) + { + VariantInit(ret_val); + return UIA_E_ELEMENTNOTAVAILABLE; + } + hr = UiaGetPropertyValue((HUIANODE)iface, prop_id, &v);
/* VT_UNKNOWN is UiaGetReservedNotSupported value, no need to marshal it. */ @@ -376,12 +438,47 @@ static HRESULT WINAPI uia_node_get_prop_val(IWineUiaNode *iface, const GUID *pro return hr; }
+static HRESULT WINAPI uia_node_disconnect(IWineUiaNode *iface) +{ + struct uia_node *node = impl_from_IWineUiaNode(iface); + + TRACE("%p\n", node); + + if (node->disconnected) + { + ERR("Attempted to disconnect node which was already disconnected.\n"); + return E_FAIL; + } + + if (node->git_cookie) + { + IGlobalInterfaceTable *git; + HRESULT hr; + + hr = get_global_interface_table(&git); + if (SUCCEEDED(hr)) + { + hr = IGlobalInterfaceTable_RevokeInterfaceFromGlobal(git, node->git_cookie); + if (FAILED(hr)) + WARN("Failed to get revoke provider interface from Global Interface Table, hr %#lx\n", hr); + } + node->git_cookie = 0; + } + + IWineUiaProvider_Release(node->prov); + node->prov = NULL; + node->disconnected = TRUE; + + return S_OK; +} + static const IWineUiaNodeVtbl uia_node_vtbl = { uia_node_QueryInterface, uia_node_AddRef, uia_node_Release, uia_node_get_provider, uia_node_get_prop_val, + uia_node_disconnect, };
static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface) @@ -772,6 +869,7 @@ HRESULT WINAPI UiaNodeFromProvider(IRawElementProviderSimple *elprov, HUIANODE *
node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl; list_init(&node->prov_thread_list_entry); + list_init(&node->node_map_list_entry); node->ref = 1;
*huianode = (void *)&node->IWineUiaNode_iface; @@ -1067,6 +1165,7 @@ HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode) node->hwnd = hwnd; node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl; list_init(&node->prov_thread_list_entry); + list_init(&node->node_map_list_entry); node->ref = 1;
hr = uia_get_provider_from_hwnd(node); diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c index f141b4ca716..c888b51a019 100644 --- a/dlls/uiautomationcore/uia_main.c +++ b/dlls/uiautomationcore/uia_main.c @@ -346,12 +346,6 @@ HRESULT WINAPI UiaHostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **pr return S_OK; }
-HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *provider) -{ - FIXME("(%p): stub\n", provider); - return E_NOTIMPL; -} - /*********************************************************************** * DllMain (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index da7f54c8ce3..c94f2b6c1eb 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -39,7 +39,10 @@ struct uia_node {
HWND hwnd; BOOL nested_node; + BOOL disconnected; struct list prov_thread_list_entry; + struct list node_map_list_entry; + struct uia_provider_thread_map_entry *map; };
static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface) @@ -47,6 +50,9 @@ static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface) return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface); }
+/* uia_client.c */ +int uia_compare_runtime_ids(SAFEARRAY *sa1, SAFEARRAY *sa2) DECLSPEC_HIDDEN; + /* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN;
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index fe7dff9112e..574fa3f08f2 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -21,6 +21,7 @@
#include "wine/debug.h" #include "wine/heap.h" +#include "wine/rbtree.h" #include "initguid.h" #include "wine/iaccessible2.h"
@@ -1131,6 +1132,7 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD struct uia_provider_thread { struct list nodes_list; + struct rb_tree node_map; HANDLE hthread; HWND hwnd; LONG ref; @@ -1146,6 +1148,20 @@ static CRITICAL_SECTION_DEBUG provider_thread_cs_debug = }; static CRITICAL_SECTION provider_thread_cs = { &provider_thread_cs_debug, -1, 0, 0, 0, 0 };
+struct uia_provider_thread_map_entry +{ + struct rb_entry entry; + + SAFEARRAY *runtime_id; + struct list nodes_list; +}; + +static int uia_runtime_id_compare(const void *key, const struct rb_entry *entry) +{ + struct uia_provider_thread_map_entry *prov_entry = RB_ENTRY_VALUE(entry, struct uia_provider_thread_map_entry, entry); + return uia_compare_runtime_ids(prov_entry->runtime_id, (SAFEARRAY *)key); +} + void uia_provider_thread_remove_node(HUIANODE node) { struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); @@ -1153,24 +1169,119 @@ void uia_provider_thread_remove_node(HUIANODE node) TRACE("Removing node %p\n", node);
EnterCriticalSection(&provider_thread_cs); + list_remove(&node_data->prov_thread_list_entry); list_init(&node_data->prov_thread_list_entry); + + if (!list_empty(&node_data->node_map_list_entry)) + { + list_remove(&node_data->node_map_list_entry); + list_init(&node_data->node_map_list_entry); + if (list_empty(&node_data->map->nodes_list)) + { + rb_remove(&provider_thread.node_map, &node_data->map->entry); + SafeArrayDestroy(node_data->map->runtime_id); + heap_free(node_data->map); + } + node_data->map = NULL; + } + + LeaveCriticalSection(&provider_thread_cs); +} + +void uia_provider_thread_disconnect_node(SAFEARRAY *sa) +{ + struct rb_entry *rb_entry; + + EnterCriticalSection(&provider_thread_cs); + + /* Provider thread not started, nothing to disconnect. */ + if (!provider_thread.ref) + { + LeaveCriticalSection(&provider_thread_cs); + return; + } + + rb_entry = rb_get(&provider_thread.node_map, sa); + if (rb_entry) + { + struct uia_provider_thread_map_entry *prov_map; + struct list *cursor, *cursor2; + struct uia_node *node_data; + + prov_map = RB_ENTRY_VALUE(rb_entry, struct uia_provider_thread_map_entry, entry); + LIST_FOR_EACH_SAFE(cursor, cursor2, &prov_map->nodes_list) + { + node_data = LIST_ENTRY(cursor, struct uia_node, node_map_list_entry); + + list_remove(cursor); + list_remove(&node_data->prov_thread_list_entry); + list_init(&node_data->prov_thread_list_entry); + list_init(&node_data->node_map_list_entry); + node_data->map = NULL; + + IWineUiaNode_disconnect(&node_data->IWineUiaNode_iface); + } + + rb_remove(&provider_thread.node_map, &prov_map->entry); + SafeArrayDestroy(prov_map->runtime_id); + heap_free(prov_map); + } + LeaveCriticalSection(&provider_thread_cs); }
static HRESULT uia_provider_thread_add_node(HUIANODE node) { struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); - - TRACE("Adding node %p\n", node); + SAFEARRAY *sa; + HRESULT hr;
node_data->nested_node = TRUE;
+ hr = UiaGetRuntimeId(node, &sa); + if (FAILED(hr)) + return hr; + + TRACE("Adding node %p\n", node); + EnterCriticalSection(&provider_thread_cs); + list_add_tail(&provider_thread.nodes_list, &node_data->prov_thread_list_entry); + + /* If we have a runtime ID, create an entry in the rb tree. */ + if (sa) + { + struct uia_provider_thread_map_entry *prov_map; + struct rb_entry *rb_entry; + + if ((rb_entry = rb_get(&provider_thread.node_map, sa))) + { + prov_map = RB_ENTRY_VALUE(rb_entry, struct uia_provider_thread_map_entry, entry); + SafeArrayDestroy(sa); + } + else + { + prov_map = heap_alloc_zero(sizeof(*prov_map)); + if (!prov_map) + { + SafeArrayDestroy(sa); + LeaveCriticalSection(&provider_thread_cs); + return E_OUTOFMEMORY; + } + + prov_map->runtime_id = sa; + list_init(&prov_map->nodes_list); + rb_put(&provider_thread.node_map, sa, &prov_map->entry); + } + + list_add_tail(&prov_map->nodes_list, &node_data->node_map_list_entry); + node_data->map = prov_map; + } + LeaveCriticalSection(&provider_thread_cs);
- return S_OK; + return hr; }
#define WM_GET_OBJECT_UIA_NODE (WM_USER + 1) @@ -1271,6 +1382,7 @@ static BOOL uia_start_provider_thread(void) (const WCHAR *)uia_start_provider_thread, &hmodule);
list_init(&provider_thread.nodes_list); + rb_init(&provider_thread.node_map, uia_runtime_id_compare); events[0] = ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); if (!(provider_thread.hthread = CreateThread(NULL, 0, uia_provider_thread_proc, ready_event, 0, NULL))) @@ -1363,3 +1475,33 @@ LRESULT WINAPI UiaReturnRawElementProvider(HWND hwnd, WPARAM wparam,
return uia_lresult_from_node(node); } + +/*********************************************************************** + * UiaDisconnectProvider (uiautomationcore.@) + */ +HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov) +{ + SAFEARRAY *sa; + HUIANODE node; + HRESULT hr; + + TRACE("(%p)\n", elprov); + + hr = UiaNodeFromProvider(elprov, &node); + if (FAILED(hr)) + return hr; + + hr = UiaGetRuntimeId(node, &sa); + UiaNodeRelease(node); + if (FAILED(hr)) + return hr; + + if (!sa) + return E_INVALIDARG; + + uia_provider_thread_disconnect_node(sa); + + SafeArrayDestroy(sa); + + return S_OK; +} diff --git a/include/uiautomationcoreapi.h b/include/uiautomationcoreapi.h index b4814b9e857..025b811ba5e 100644 --- a/include/uiautomationcoreapi.h +++ b/include/uiautomationcoreapi.h @@ -398,6 +398,7 @@ BOOL WINAPI UiaNodeRelease(HUIANODE huianode); HRESULT WINAPI UiaGetRuntimeId(HUIANODE huianode, SAFEARRAY **runtime_id); HRESULT WINAPI UiaHUiaNodeFromVariant(VARIANT *in_val, HUIANODE *huianode); HRESULT WINAPI UiaNodeFromHandle(HWND hwnd, HUIANODE *huianode); +HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *elprov);
#ifdef __cplusplus }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 214 ++++++++++----------- dlls/uiautomationcore/uia_client.c | 110 ++++++++--- dlls/uiautomationcore/uia_private.h | 16 ++ dlls/uiautomationcore/uia_provider.c | 5 +- 4 files changed, 206 insertions(+), 139 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 7982bd5fa8b..b500ca73b62 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -5294,57 +5294,53 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) Provider_child.runtime_id[1] = 2; Provider_child.frag_root = NULL; hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); - todo_wine ok(hr == E_FAIL || broken(hr == S_OK), "Unexpected hr %#lx\n", hr); + ok(hr == E_FAIL || broken(hr == S_OK), "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { hr = UiaHUiaNodeFromVariant(&v, &node2); ok(hr == S_OK, "Unexpected hr %#lx\n", hr); UiaNodeRelease(node2); } - if (hr != E_NOTIMPL) - ok_method_sequence(node_from_hwnd5, "node_from_hwnd5"); + + ok_method_sequence(node_from_hwnd5, "node_from_hwnd5");
/* RuntimeId check succeeds, we'll get a nested node. */ Provider_child.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
+ hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = UiaHUiaNodeFromVariant(&v, &node2); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - /* - * Even though this is a nested node, without any additional - * providers, it will not have the 'Nested' prefix. - */ - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); - VariantClear(&v); - } - - hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); - ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); - ok_method_sequence(node_from_hwnd6, "node_from_hwnd6"); - - UiaNodeRelease(node2); - /* - * There is a delay between nested nodes being released and the - * corresponding IRawElementProviderSimple release on newer Windows - * versions. + * Even though this is a nested node, without any additional + * providers, it will not have the 'Nested' prefix. */ - if (Provider_child.ref != 1) - Sleep(50); - ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); }
+ hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); + ok_method_sequence(node_from_hwnd6, "node_from_hwnd6"); + + UiaNodeRelease(node2); + + /* + * There is a delay between nested nodes being released and the + * corresponding IRawElementProviderSimple release on newer Windows + * versions. + */ + if (Provider_child.ref != 1) + Sleep(50); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
/* * Returned nested elements with an HWND will have client-side providers @@ -5355,42 +5351,39 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) /* Only sent on Win7. */ SET_EXPECT(winproc_GETOBJECT_CLIENT); hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); todo_wine CHECK_CALLED(winproc_GETOBJECT_UiaRoot); called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; - todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
+ hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = UiaHUiaNodeFromVariant(&v, &node2); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - memset(buf, 0, sizeof(buf)); + memset(buf, 0, sizeof(buf));
- ok(get_nested_provider_desc(V_BSTR(&v), L"Main", TRUE, buf), "Failed to get nested provider description\n"); - check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); - /* Win10v1507 and below have the nested provider as 'Hwnd'. */ - if (get_provider_desc(buf, L"Hwnd(parent link):", NULL)) - check_node_provider_desc(buf, L"Hwnd", L"Provider_child", TRUE); - else - check_node_provider_desc(buf, L"Main", L"Provider_child", TRUE); + ok(get_nested_provider_desc(V_BSTR(&v), L"Main", TRUE, buf), "Failed to get nested provider description\n"); + check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); + /* Win10v1507 and below have the nested provider as 'Hwnd'. */ + if (get_provider_desc(buf, L"Hwnd(parent link):", NULL)) + check_node_provider_desc(buf, L"Hwnd", L"Provider_child", TRUE); + else + check_node_provider_desc(buf, L"Main", L"Provider_child", TRUE);
- check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); - check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE); - check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, FALSE); - VariantClear(&v); - } + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, FALSE); + VariantClear(&v); + }
- ok_method_sequence(node_from_hwnd7, "node_from_hwnd7"); - UiaNodeRelease(node2); + ok_method_sequence(node_from_hwnd7, "node_from_hwnd7"); + UiaNodeRelease(node2);
- if (Provider_child.ref != 1) - Sleep(50); - ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); - } + if (Provider_child.ref != 1) + Sleep(50); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); /* Win10v1809 can be slow to call Release on Provider. */ @@ -5452,68 +5445,65 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) Provider_child.hwnd = NULL; Provider_child.frag_root = &Provider.IRawElementProviderFragmentRoot_iface; hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - todo_wine ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(Provider_child.ref == 2, "Unexpected refcnt %ld\n", Provider_child.ref);
+ hr = UiaHUiaNodeFromVariant(&v, &node2); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); if (SUCCEEDED(hr)) { - hr = UiaHUiaNodeFromVariant(&v, &node2); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - hr = UiaGetPropertyValue(node2, UIA_ProviderDescriptionPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); - VariantClear(&v); - } + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); + }
- hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); - ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); - ok_method_sequence(node_from_hwnd6, "node_from_hwnd6"); + hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); + ok_method_sequence(node_from_hwnd6, "node_from_hwnd6");
- /* Get a new node for the same provider. */ - hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - ok(Provider_child.ref == 3, "Unexpected refcnt %ld\n", Provider_child.ref); + /* Get a new node for the same provider. */ + hr = UiaGetPropertyValue(node, UIA_LabeledByPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(Provider_child.ref == 3, "Unexpected refcnt %ld\n", Provider_child.ref);
- hr = UiaHUiaNodeFromVariant(&v, &node3); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - hr = UiaGetPropertyValue(node3, UIA_ProviderDescriptionPropertyId, &v); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - if (SUCCEEDED(hr)) - { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); - check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); - VariantClear(&v); - } + hr = UiaHUiaNodeFromVariant(&v, &node3); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node3, UIA_ProviderDescriptionPropertyId, &v); + todo_wine ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + if (SUCCEEDED(hr)) + { + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), NULL); + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider_child", TRUE); + VariantClear(&v); + }
- hr = UiaGetPropertyValue(node3, UIA_ControlTypePropertyId, &v); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); - ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); - ok_method_sequence(node_from_hwnd6, "node_from_hwnd6"); + hr = UiaGetPropertyValue(node3, UIA_ControlTypePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok(V_I4(&v) == uia_i4_prop_val, "Unexpected I4 %#lx\n", V_I4(&v)); + ok_method_sequence(node_from_hwnd6, "node_from_hwnd6");
- /* - * Both node2 and node3 represent Provider_child, one call to - * UiaDisconnectProvider disconnects both. - */ - hr = pUiaDisconnectProvider(&Provider_child.IRawElementProviderSimple_iface); - ok(hr == S_OK, "Unexpected hr %#lx\n", hr); - ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref); + /* + * Both node2 and node3 represent Provider_child, one call to + * UiaDisconnectProvider disconnects both. + */ + hr = pUiaDisconnectProvider(&Provider_child.IRawElementProviderSimple_iface); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + ok(Provider_child.ref == 1, "Unexpected refcnt %ld\n", Provider_child.ref);
- hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); - ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx\n", hr); + hr = UiaGetPropertyValue(node2, UIA_ControlTypePropertyId, &v); + ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx\n", hr);
- hr = UiaGetPropertyValue(node3, UIA_ControlTypePropertyId, &v); - ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx\n", hr); - ok_method_sequence(disconnect_prov1, "disconnect_prov1"); + hr = UiaGetPropertyValue(node3, UIA_ControlTypePropertyId, &v); + ok(hr == UIA_E_ELEMENTNOTAVAILABLE, "Unexpected hr %#lx\n", hr); + ok_method_sequence(disconnect_prov1, "disconnect_prov1");
- ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); - ok(UiaNodeRelease(node3), "UiaNodeRelease returned FALSE\n"); - } + ok(UiaNodeRelease(node2), "UiaNodeRelease returned FALSE\n"); + ok(UiaNodeRelease(node3), "UiaNodeRelease returned FALSE\n");
/* * Returns same failure code as UiaGetRuntimeId when we fail to get a diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index a3b04479b13..715c3f061a3 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -492,19 +492,6 @@ static struct uia_node *unsafe_impl_from_IWineUiaNode(IWineUiaNode *iface) /* * IWineUiaProvider interface. */ -struct uia_provider { - IWineUiaProvider IWineUiaProvider_iface; - LONG ref; - - IRawElementProviderSimple *elprov; - IWineUiaNode *node; -}; - -static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *iface) -{ - return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); -} - static HRESULT WINAPI uia_provider_QueryInterface(IWineUiaProvider *iface, REFIID riid, void **ppv) { *ppv = NULL; @@ -639,15 +626,26 @@ static HRESULT uia_provider_get_elem_prop_val(struct uia_provider *prov,
hr = IUnknown_QueryInterface(V_UNKNOWN(&v), &IID_IRawElementProviderSimple, (void **)&elprov); + VariantClear(&v); if (FAILED(hr)) goto exit;
hr = UiaNodeFromProvider(elprov, &node); + IRawElementProviderSimple_Release(elprov); if (SUCCEEDED(hr)) { - get_variant_for_node(node, ret_val); - VariantClear(&v); - IRawElementProviderSimple_Release(elprov); + if (prov->is_nested_node_elprov) + { + LRESULT lr = uia_lresult_from_node(node); + + if (!lr) + return E_FAIL; + + V_VT(ret_val) = VT_I4; + V_I4(ret_val) = lr; + } + else + get_variant_for_node(node, ret_val); } break; } @@ -746,6 +744,46 @@ static HRESULT uia_provider_get_special_prop_val(struct uia_provider *prov, return S_OK; }
+static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode); +static HRESULT uia_provider_get_nested_node_prop_val(struct uia_provider *prov, + const struct uia_prop_info *prop_info, VARIANT *ret_val) +{ + HRESULT hr; + VARIANT v; + + if (prop_info->type == UIAutomationType_ElementArray) + { + FIXME("Element array property type currently unsupported for nested nodes.\n"); + return E_NOTIMPL; + } + + hr = IWineUiaNode_get_prop_val(prov->node, prop_info->guid, &v); + if (FAILED(hr)) + return hr; + + switch (prop_info->type) + { + case UIAutomationType_Element: + { + HUIANODE node; + + hr = uia_node_from_lresult((LRESULT)V_I4(&v), &node); + if (FAILED(hr)) + return hr; + + get_variant_for_node(node, ret_val); + VariantClear(&v); + break; + } + + default: + *ret_val = v; + break; + } + + return S_OK; +} + static HRESULT WINAPI uia_provider_get_prop_val(IWineUiaProvider *iface, const struct uia_prop_info *prop_info, VARIANT *ret_val) { @@ -755,15 +793,7 @@ static HRESULT WINAPI uia_provider_get_prop_val(IWineUiaProvider *iface,
VariantInit(ret_val); if (prov->node) - { - if (prop_info->type == UIAutomationType_Element || prop_info->type == UIAutomationType_ElementArray) - { - FIXME("Element property types currently unsupported for nested nodes.\n"); - return E_NOTIMPL; - } - - return IWineUiaNode_get_prop_val(prov->node, prop_info->guid, ret_val); - } + return uia_provider_get_nested_node_prop_val(prov, prop_info, ret_val);
switch (prop_info->prop_type) { @@ -1057,6 +1087,7 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU if (unwrap) { struct uia_node *node_data = unsafe_impl_from_IWineUiaNode(nested_node); + struct uia_provider *tmp_prov = impl_from_IWineUiaProvider(node_data->prov);
if (!node_data) { @@ -1068,6 +1099,7 @@ static HRESULT create_wine_uia_nested_node_provider(struct uia_node *node, LRESU prov = impl_from_IWineUiaProvider(node_data->prov); IWineUiaProvider_AddRef(node_data->prov); git_cookie = node_data->git_cookie; + tmp_prov->is_nested_node_elprov = FALSE;
node_data->git_cookie = 0; IWineUiaNode_Release(&node_data->IWineUiaNode_iface); @@ -1140,6 +1172,34 @@ static HRESULT uia_get_provider_from_hwnd(struct uia_node *node) return SendMessageW(client_thread.hwnd, WM_UIA_CLIENT_GET_NODE_PROV, (WPARAM)&args, (LPARAM)node); }
+static HRESULT uia_node_from_lresult(LRESULT lr, HUIANODE *huianode) +{ + struct uia_node *node; + HRESULT hr; + + *huianode = NULL; + node = heap_alloc_zero(sizeof(*node)); + if (!node) + return E_OUTOFMEMORY; + + node->IWineUiaNode_iface.lpVtbl = &uia_node_vtbl; + list_init(&node->prov_thread_list_entry); + list_init(&node->node_map_list_entry); + node->ref = 1; + + uia_start_client_thread(); + hr = create_wine_uia_nested_node_provider(node, lr, FALSE); + if (FAILED(hr)) + { + heap_free(node); + return hr; + } + + *huianode = (void *)&node->IWineUiaNode_iface; + + return hr; +} + /*********************************************************************** * UiaNodeFromHandle (uiautomationcore.@) */ diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index c94f2b6c1eb..c4797e70c8f 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -50,6 +50,21 @@ static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface) return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface); }
+struct uia_provider { + IWineUiaProvider IWineUiaProvider_iface; + LONG ref; + + IRawElementProviderSimple *elprov; + IWineUiaNode *node; + + BOOL is_nested_node_elprov; +}; + +static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *iface) +{ + return CONTAINING_RECORD(iface, struct uia_provider, IWineUiaProvider_iface); +} + /* uia_client.c */ int uia_compare_runtime_ids(SAFEARRAY *sa1, SAFEARRAY *sa2) DECLSPEC_HIDDEN;
@@ -59,3 +74,4 @@ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_H /* uia_provider.c */ void uia_stop_provider_thread(void) DECLSPEC_HIDDEN; void uia_provider_thread_remove_node(HUIANODE node) DECLSPEC_HIDDEN; +LRESULT uia_lresult_from_node(HUIANODE huianode) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 574fa3f08f2..84b206410ce 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1234,10 +1234,11 @@ void uia_provider_thread_disconnect_node(SAFEARRAY *sa) static HRESULT uia_provider_thread_add_node(HUIANODE node) { struct uia_node *node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + struct uia_provider *prov_data = impl_from_IWineUiaProvider(node_data->prov); SAFEARRAY *sa; HRESULT hr;
- node_data->nested_node = TRUE; + node_data->nested_node = prov_data->is_nested_node_elprov = TRUE;
hr = UiaGetRuntimeId(node, &sa); if (FAILED(hr)) @@ -1432,7 +1433,7 @@ void uia_stop_provider_thread(void) * Automation has to work regardless of whether or not COM is initialized on * the thread calling UiaReturnRawElementProvider. */ -static LRESULT uia_lresult_from_node(HUIANODE huianode) +LRESULT uia_lresult_from_node(HUIANODE huianode) { if (!uia_start_provider_thread()) {
v5:
- Cleanup behavior of `create_wine_uia_nested_node_provider()` to assign provider at end of function for both nested node and unwrap paths. - Store the `struct list` structure inside of `struct uia_node` instead of allocating new custom list structures. - Check provider thread reference count before attempting to disconnect any nodes. - Change `nested_node_prov` variable in `struct uia_provider` to `is_nested_node_elprov` to make its function more clear.