From: Connor McAdams cmcadams@codeweavers.com
If we pass a node to the provider thread that contains a provider that was created in an STA with the ProviderOptions_UseComThreading flag set, we can deadlock when attempting to get a runtime ID from the proxy. To avoid this, use SendMessageCallback and pump the message queue until we get a result from the provider thread.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 32 +++++++++++++++++++ dlls/uiautomationcore/uia_provider.c | 36 +++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index a92f15c6617..d41024a6253 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -6303,6 +6303,16 @@ static const struct prov_method_sequence node_from_hwnd9[] = { { 0 } };
+static const struct prov_method_sequence node_from_hwnd10[] = { + NODE_CREATE_SEQ(&Provider), + /* Next two only done on Windows 8+. */ + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, + { &Provider, PROV_GET_PROVIDER_OPTIONS }, + { &Provider, FRAG_GET_RUNTIME_ID, METHOD_OPTIONAL }, /* Only done on Win11+. */ + { 0 } +}; + static const struct prov_method_sequence disconnect_prov1[] = { { &Provider_child, PROV_GET_PROVIDER_OPTIONS }, /* Win10v1507 and below call this. */ @@ -6574,6 +6584,28 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) Sleep(50); ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref);
+ /* ProviderOptions_UseComThreading test from a separate thread. */ + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + /* Only sent on Win7. */ + SET_EXPECT(winproc_GETOBJECT_CLIENT); + prov_root = &Provider.IRawElementProviderSimple_iface; + initialize_provider(&Provider, ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading, NULL, FALSE); + Provider.frag_root = NULL; + Provider.runtime_id[0] = Provider.runtime_id[1] = 0xdeadbeef; + 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); + called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + + ok_method_sequence(node_from_hwnd10, "node_from_hwnd10"); + + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + /* Win10v1809 can be slow to call Release on Provider. */ + if (Provider.ref != 1) + Sleep(50); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + if (!pUiaDisconnectProvider) { win_skip("UiaDisconnectProvider not exported by uiautomationcore.dll\n"); diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 76b5c03537a..40df4f53d15 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1941,6 +1941,19 @@ void uia_stop_provider_thread(void) LeaveCriticalSection(&provider_thread_cs); }
+struct uia_lresult_from_node_data { + LRESULT lr; + BOOL returned; +}; + +static void CALLBACK uia_lresult_from_node_callback(HWND hwnd, UINT msg, ULONG_PTR data, LRESULT lresult) +{ + struct uia_lresult_from_node_data *cb_data = (struct uia_lresult_from_node_data *)data; + + cb_data->lr = lresult; + cb_data->returned = TRUE; +} + /* * Pass our IWineUiaNode interface to the provider thread for marshaling. UI * Automation has to work regardless of whether or not COM is initialized on @@ -1948,13 +1961,34 @@ void uia_stop_provider_thread(void) */ LRESULT uia_lresult_from_node(HUIANODE huianode) { + struct uia_lresult_from_node_data cb_data = { 0 }; + if (!uia_start_provider_thread()) { UiaNodeRelease(huianode); return 0; }
- return SendMessageW(provider_thread.hwnd, WM_GET_OBJECT_UIA_NODE, 0, (LPARAM)huianode); + if (!SendMessageCallbackW(provider_thread.hwnd, WM_GET_OBJECT_UIA_NODE, 0, (LPARAM)huianode, + uia_lresult_from_node_callback, (ULONG_PTR)&cb_data)) + { + WARN("SendMessageCallback failed, error %ld\n", GetLastError()); + UiaNodeRelease(huianode); + return 0; + } + + while (!cb_data.returned) + { + MSG msg; + + if (PeekMessageW(&msg, 0, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + return cb_data.lr; }
/***********************************************************************