From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/uia_classes.idl | 1 + dlls/uiautomationcore/uia_client.c | 93 ++++++++--- dlls/uiautomationcore/uia_main.c | 33 ++-- dlls/uiautomationcore/uia_private.h | 23 +++ dlls/uiautomationcore/uia_provider.c | 220 ++++++++++++++++++++++++++ 5 files changed, 336 insertions(+), 34 deletions(-)
diff --git a/dlls/uiautomationcore/uia_classes.idl b/dlls/uiautomationcore/uia_classes.idl index f7d3bb90c73..030606cec93 100644 --- a/dlls/uiautomationcore/uia_classes.idl +++ b/dlls/uiautomationcore/uia_classes.idl @@ -50,6 +50,7 @@ library UIA_wine_private object, uuid(bccb6799-d831-4057-bd50-6425823ff1a3), pointer_default(unique), + oleautomation, ] interface IWineUiaNode : IUnknown { diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 0ff01eba5af..deb8dd0d7af 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -64,8 +64,32 @@ static void clear_uia_node_ptr_safearray(SAFEARRAY *sa, LONG elems) } }
-static void create_uia_node_safearray(VARIANT *in, VARIANT *out) +static void clear_uia_node_lresult_safearray(SAFEARRAY *sa, LONG elems) { + IWineUiaNode *node; + LRESULT lr; + HRESULT hr; + LONG i; + + for (i = 0; i < elems; i++) + { + hr = SafeArrayGetElement(sa, &i, &lr); + if (FAILED(hr)) + break; + if (!lr) + continue; + + hr = ObjectFromLresult(lr, &IID_IWineUiaNode, 0, (void **)&node); + if (FAILED(hr)) + break; + + IWineUiaNode_Release(node); + } +} + +static void create_uia_node_safearray(VARIANT *in, VARIANT *out, BOOL out_nested) +{ + const VARTYPE out_vt = out_nested ? VT_I4 : VT_UINT_PTR; LONG i, idx, lbound, elems; HUIANODE node; SAFEARRAY *sa; @@ -74,7 +98,7 @@ static void create_uia_node_safearray(VARIANT *in, VARIANT *out) if (FAILED(get_safearray_bounds(V_ARRAY(in), &lbound, &elems))) return;
- if (!(sa = SafeArrayCreateVector(VT_UINT_PTR, 0, elems))) + if (!(sa = SafeArrayCreateVector(out_vt, 0, elems))) return;
for (i = 0; i < elems; i++) @@ -97,19 +121,36 @@ static void create_uia_node_safearray(VARIANT *in, VARIANT *out) break;
IRawElementProviderSimple_Release(elprov); - hr = SafeArrayPutElement(sa, &i, &node); + if (out_nested) + { + LRESULT lr; + + lr = uia_lresult_from_node(node); + if (!lr) + { + hr = E_FAIL; + break; + } + + hr = SafeArrayPutElement(sa, &i, &lr); + } + else + hr = SafeArrayPutElement(sa, &i, &node); if (FAILED(hr)) break; }
if (FAILED(hr)) { - clear_uia_node_ptr_safearray(sa, elems); + if (out_nested) + clear_uia_node_lresult_safearray(sa, elems); + else + clear_uia_node_ptr_safearray(sa, elems); SafeArrayDestroy(sa); return; }
- V_VT(out) = VT_UINT_PTR | VT_ARRAY; + V_VT(out) = out_vt | VT_ARRAY; V_ARRAY(out) = sa; }
@@ -269,21 +310,6 @@ static IRawElementProviderSimple *get_provider_hwnd_fragment_root(IRawElementPro /* * IWineUiaNode interface. */ -struct uia_node { - IWineUiaNode IWineUiaNode_iface; - LONG ref; - - IWineUiaProvider *prov; - DWORD git_cookie; - - HWND hwnd; -}; - -static inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface) -{ - return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface); -} - static HRESULT WINAPI uia_node_QueryInterface(IWineUiaNode *iface, REFIID riid, void **ppv) { *ppv = NULL; @@ -328,6 +354,8 @@ static ULONG WINAPI uia_node_Release(IWineUiaNode *iface) }
IWineUiaProvider_Release(node->prov); + if (node->nested_node) + uia_stop_provider_thread(); heap_free(node); }
@@ -389,6 +417,7 @@ struct uia_provider { LONG ref;
IRawElementProviderSimple *elprov; + struct uia_node *node; };
static inline struct uia_provider *impl_from_IWineUiaProvider(IWineUiaProvider *iface) @@ -529,9 +558,23 @@ static HRESULT uia_provider_get_elem_prop_val(struct uia_provider *prov, hr = UiaNodeFromProvider(elprov, &node); if (SUCCEEDED(hr)) { - get_variant_for_node(node, ret_val); + if (prov->node->nested_node) + { + LRESULT lr = uia_lresult_from_node(node); + + if (lr) + { + V_VT(ret_val) = VT_I4; + V_I4(ret_val) = lr; + } + } + else + get_variant_for_node(node, ret_val); + VariantClear(&v); IRawElementProviderSimple_Release(elprov); + if (prov->node->nested_node && V_VT(ret_val) != VT_I4) + return E_FAIL; } break; } @@ -542,9 +585,10 @@ static HRESULT uia_provider_get_elem_prop_val(struct uia_provider *prov, WARN("Invalid vt %d for UIAutomationType_ElementArray\n", V_VT(&v)); goto exit; } - create_uia_node_safearray(&v, ret_val); - if (V_VT(ret_val) == (VT_UINT_PTR | VT_ARRAY)) - VariantClear(&v); + create_uia_node_safearray(&v, ret_val, prov->node->nested_node); + VariantClear(&v); + if (prov->node->nested_node && V_VT(ret_val) == VT_EMPTY) + return E_FAIL; break;
default: @@ -680,6 +724,7 @@ static HRESULT create_wine_uia_provider(struct uia_node *node, IRawElementProvid prov->IWineUiaProvider_iface.lpVtbl = &uia_provider_vtbl; prov->elprov = elprov; prov->ref = 1; + prov->node = node; node->prov = &prov->IWineUiaProvider_iface; node->hwnd = get_hwnd_from_provider(elprov);
diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c index 3a6f10c1647..f141b4ca716 100644 --- a/dlls/uiautomationcore/uia_main.c +++ b/dlls/uiautomationcore/uia_main.c @@ -27,6 +27,8 @@
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
+HMODULE huia_module; + struct uia_object_wrapper { IUnknown IUnknown_iface; @@ -297,16 +299,6 @@ HRESULT WINAPI UiaGetReservedNotSupportedValue(IUnknown **value) return S_OK; }
-/*********************************************************************** - * UiaReturnRawElementProvider (uiautomationcore.@) - */ -LRESULT WINAPI UiaReturnRawElementProvider(HWND hwnd, WPARAM wParam, - LPARAM lParam, IRawElementProviderSimple *elprov) -{ - FIXME("(%p, %Ix, %Ix, %p) stub!\n", hwnd, wParam, lParam, elprov); - return 0; -} - /*********************************************************************** * UiaRaiseAutomationEvent (uiautomationcore.@) */ @@ -359,3 +351,24 @@ HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *provider) FIXME("(%p): stub\n", provider); return E_NOTIMPL; } + +/*********************************************************************** + * DllMain (uiautomationcore.@) + */ +BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, void *reserved) +{ + TRACE("(%p, %ld, %p)\n", hinst, reason, reserved); + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinst); + huia_module = hinst; + break; + + default: + break; + } + + return TRUE; +} diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index ed53da58471..5dc254ec195 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -21,10 +21,33 @@ #include "uiautomation.h" #include "uia_classes.h"
+extern HMODULE huia_module DECLSPEC_HIDDEN; + enum uia_prop_type { PROP_TYPE_UNKNOWN, PROP_TYPE_ELEM_PROP, PROP_TYPE_SPECIAL, };
+struct uia_node { + IWineUiaNode IWineUiaNode_iface; + LONG ref; + + IWineUiaProvider *prov; + DWORD git_cookie; + + HWND hwnd; + BOOL nested_node; +}; + +inline struct uia_node *impl_from_IWineUiaNode(IWineUiaNode *iface) +{ + return CONTAINING_RECORD(iface, struct uia_node, IWineUiaNode_iface); +} + +/* uia_ids.c */ const struct uia_prop_info *uia_prop_info_from_id(PROPERTYID prop_id) DECLSPEC_HIDDEN; + +/* uia_provider.c */ +LRESULT uia_lresult_from_node(HUIANODE huianode) DECLSPEC_HIDDEN; +void uia_stop_provider_thread(void) DECLSPEC_HIDDEN; diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 9c2dfd65989..0f46a7a4429 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1124,3 +1124,223 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD
return S_OK; } + +/* + * UI Automation provider thread functions. + */ +struct uia_provider_thread +{ + HANDLE hthread; + HWND hwnd; + LONG ref; +}; + +static struct uia_provider_thread provider_thread; +static CRITICAL_SECTION provider_thread_cs; +static CRITICAL_SECTION_DEBUG provider_thread_cs_debug = +{ + 0, 0, &provider_thread_cs, + { &provider_thread_cs_debug.ProcessLocksList, &provider_thread_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": provider_thread_cs") } +}; +static CRITICAL_SECTION provider_thread_cs = { &provider_thread_cs_debug, -1, 0, 0, 0, 0 }; + +#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, + LPARAM lparam) +{ + switch (msg) + { + case WM_GET_OBJECT_UIA_NODE: + { + HUIANODE node = (HUIANODE)lparam; + struct uia_node *node_data; + SAFEARRAY *sa; + LRESULT lr; + HRESULT hr; + + hr = UiaGetRuntimeId(node, &sa); + if (FAILED(hr)) + { + WARN("Failed to get runtime ID for node %p\n", node); + UiaNodeRelease(node); + uia_stop_provider_thread(); + return 0; + } + + SafeArrayDestroy(sa); + lr = LresultFromObject(&IID_IWineUiaNode, 0, (IUnknown *)node); + + /* + * LresultFromObject returns an index into the global atom string table, + * which has a valid range of 0xc000-0xffff. + */ + if ((lr > 0xffff) || (lr < 0xc000)) + { + WARN("Got invalid lresult %Ix\n", lr); + uia_stop_provider_thread(); + lr = 0; + } + else + { + node_data = impl_from_IWineUiaNode((IWineUiaNode *)node); + node_data->nested_node = TRUE; + } + + /* + * LresultFromObject increases refcnt by 1. If LresultFromObject + * failed, this is expected to release the node. + */ + UiaNodeRelease(node); + return lr; + } + + case WM_UIA_PROVIDER_THREAD_STOP: + DestroyWindow(hwnd); + break; + + default: + break; + } + + return DefWindowProcW(hwnd, msg, wparam, lparam); +} + +static DWORD WINAPI uia_provider_thread_proc(void *arg) +{ + HANDLE initialized_event = arg; + HWND hwnd; + MSG msg; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + hwnd = CreateWindowW(L"Message", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); + if (!IsWindow(hwnd)) + { + WARN("CreateWindow failed: %ld\n", GetLastError()); + CoUninitialize(); + FreeLibraryAndExitThread(huia_module, 1); + } + + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)uia_provider_thread_msg_proc); + provider_thread.hwnd = hwnd; + + /* Initialization complete, thread can now process window messages. */ + SetEvent(initialized_event); + TRACE("Provider thread started.\n"); + while (GetMessageW(&msg, NULL, 0, 0)) + { + if (msg.message == WM_UIA_PROVIDER_THREAD_STOP) + break; + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + TRACE("Shutting down UI Automation provider thread.\n"); + + DestroyWindow(hwnd); + CoUninitialize(); + FreeLibraryAndExitThread(huia_module, 0); +} + +static BOOL uia_start_provider_thread(void) +{ + BOOL started = TRUE; + + EnterCriticalSection(&provider_thread_cs); + if (++provider_thread.ref == 1) + { + HANDLE ready_event; + HANDLE events[2]; + HMODULE hmodule; + DWORD wait_obj; + + GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + (const WCHAR *)uia_start_provider_thread, &hmodule); + + ready_event = CreateEventW(NULL, FALSE, FALSE, NULL); + if (!(provider_thread.hthread = CreateThread(NULL, 0, uia_provider_thread_proc, + ready_event, 0, NULL))) + { + WARN("Failed to create provider thread.\n"); + CloseHandle(ready_event); + FreeLibrary(huia_module); + memset(&provider_thread, 0, sizeof(provider_thread)); + LeaveCriticalSection(&provider_thread_cs); + return FALSE; + } + + events[0] = ready_event; + events[1] = provider_thread.hthread; + wait_obj = WaitForMultipleObjects(2, events, FALSE, INFINITE); + if (wait_obj != WAIT_OBJECT_0) + { + WARN("Failed to start provider thread\n"); + CloseHandle(provider_thread.hthread); + memset(&provider_thread, 0, sizeof(provider_thread)); + started = FALSE; + } + + CloseHandle(ready_event); + } + + LeaveCriticalSection(&provider_thread_cs); + return started; +} + +void uia_stop_provider_thread(void) +{ + EnterCriticalSection(&provider_thread_cs); + if (!--provider_thread.ref) + { + PostMessageW(provider_thread.hwnd, WM_UIA_PROVIDER_THREAD_STOP, 0, 0); + CloseHandle(provider_thread.hthread); + memset(&provider_thread, 0, sizeof(provider_thread)); + } + LeaveCriticalSection(&provider_thread_cs); +} + +LRESULT uia_lresult_from_node(HUIANODE huianode) +{ + if (!uia_start_provider_thread()) + { + UiaNodeRelease(huianode); + return 0; + } + + return SendMessageW(provider_thread.hwnd, WM_GET_OBJECT_UIA_NODE, 0, (LPARAM)huianode); +} + +/*********************************************************************** + * UiaReturnRawElementProvider (uiautomationcore.@) + */ +LRESULT WINAPI UiaReturnRawElementProvider(HWND hwnd, WPARAM wParam, + LPARAM lParam, IRawElementProviderSimple *elprov) +{ + HUIANODE node; + HRESULT hr; + + TRACE("(%p, %Ix, %#Ix, %p)\n", hwnd, wParam, lParam, elprov); + + if (!wParam && !lParam && !elprov) + { + FIXME("UIA-to-MSAA bridge not implemented, no provider map to free.\n"); + return 0; + } + + if (lParam != UiaRootObjectId) + { + FIXME("Unsupported object id %Id, ignoring.\n", lParam); + return 0; + } + + hr = UiaNodeFromProvider(elprov, &node); + if (FAILED(hr)) + { + WARN("Failed to create HUIANODE with hr %#lx\n", hr); + UiaNodeRelease(node); + return 0; + } + + return uia_lresult_from_node(node); +}