From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 133 +++++++++++++++++++++ 1 file changed, 133 insertions(+)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 6d2e859a7f6..f3e671526ec 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -20,6 +20,7 @@
#define COBJMACROS
+#include <assert.h> #include "windows.h" #include "initguid.h" #include "uiautomation.h" @@ -1291,10 +1292,14 @@ static void flush_method_sequence(void) sequence_cnt = sequence_size = 0; }
+static BOOL method_sequences_enabled = TRUE; static void add_method_call(struct Provider *prov, int method) { struct prov_method_sequence prov_method = {0};
+ if (!method_sequences_enabled) + return; + if (!sequence) { sequence_size = 10; @@ -1321,6 +1326,7 @@ static void ok_method_sequence_(const struct prov_method_sequence *expected_list const struct prov_method_sequence *actual; unsigned int count = 0;
+ assert(method_sequences_enabled); add_method_call(NULL, 0); actual = sequence;
@@ -12297,6 +12303,132 @@ static void test_CUIAutomation(void) CoUninitialize(); }
+static const struct prov_method_sequence default_hwnd_prov_props_seq[] = { + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Windows 7. */ + { &Provider_nc, PROV_GET_PROPERTY_VALUE }, /* UIA_NativeWindowHandlePropertyId */ + { &Provider_nc, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Windows 7. */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_NamePropertyId */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Windows 7. */ + { &Provider_nc, PROV_GET_PROPERTY_VALUE }, /* UIA_NamePropertyId */ + { &Provider_nc, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Windows 7. */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ClassNamePropertyId */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Windows 7. */ + { &Provider_nc, PROV_GET_PROPERTY_VALUE }, /* UIA_ClassNamePropertyId */ + { &Provider_nc, PROV_GET_PROVIDER_OPTIONS, METHOD_OPTIONAL }, /* Only done on Windows 7. */ + { &Provider, PROV_GET_PROPERTY_VALUE }, /* UIA_ProcessIdPropertyId */ + { &Provider, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, + { &Provider_nc, PROV_GET_PROPERTY_VALUE }, /* UIA_ProcessIdPropertyId */ + { &Provider_nc, PROV_GET_PROVIDER_OPTIONS, METHOD_TODO }, + { 0 } +}; + +#define test_node_hwnd_provider( node, hwnd ) \ + test_node_hwnd_provider_( (node), (hwnd), __FILE__, __LINE__) +static void test_node_hwnd_provider_(HUIANODE node, HWND hwnd, const char *file, int line) +{ + WCHAR buf[1024] = { 0 }; + HRESULT hr; + VARIANT v; + DWORD pid; + + winetest_push_context("UIA_NativeWindowHandlePropertyId"); + hr = UiaGetPropertyValue(node, UIA_NativeWindowHandlePropertyId, &v); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + todo_wine ok_(file, line)(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + if (V_VT(&v) == VT_I4) + ok_(file, line)(V_I4(&v) == HandleToUlong(hwnd), "V_I4(&v) = %#lx, expected %#lx\n", V_I4(&v), HandleToUlong(hwnd)); + VariantClear(&v); + winetest_pop_context(); + + winetest_push_context("UIA_NamePropertyId"); + SendMessageW(hwnd, WM_GETTEXT, ARRAY_SIZE(buf), (LPARAM)buf); + hr = UiaGetPropertyValue(node, UIA_NamePropertyId, &v); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + todo_wine ok_(file, line)(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); + if (V_VT(&v) == VT_BSTR) + ok(!lstrcmpW(V_BSTR(&v), buf), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + winetest_pop_context(); + + winetest_push_context("UIA_ClassNamePropertyId"); + memset(buf, 0, sizeof(buf)); + GetClassNameW(hwnd, buf, ARRAY_SIZE(buf)); + hr = UiaGetPropertyValue(node, UIA_ClassNamePropertyId, &v); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + todo_wine ok_(file, line)(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); + if (V_VT(&v) == VT_BSTR) + ok(!lstrcmpW(V_BSTR(&v), buf), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); + VariantClear(&v); + winetest_pop_context(); + + GetWindowThreadProcessId(hwnd, &pid); + winetest_push_context("UIA_ProcessIdPropertyId"); + hr = UiaGetPropertyValue(node, UIA_ProcessIdPropertyId, &v); + ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); + todo_wine ok_(file, line)(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + if (V_VT(&v) == VT_I4) + ok_(file, line)(V_I4(&v) == pid, "V_I4(&v) = %#lx, expected %#lx\n", V_I4(&v), pid); + VariantClear(&v); + winetest_pop_context(); +} + +static void test_default_clientside_providers(void) +{ + HUIANODE node; + HRESULT hr; + HWND hwnd; + VARIANT v; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + hwnd = create_test_hwnd("test_default_clientside_providers class"); + method_sequences_enabled = FALSE; + + /* + * Test default BaseHwnd provider. Unlike the other default providers, the + * default BaseHwnd IRawElementProviderSimple is not available to test + * directly. To isolate the BaseHwnd provider, we set the node's nonclient + * provider to Provider_nc, and its Main provider to Provider. These + * providers will return nothing so that we can isolate properties coming + * from the BaseHwnd provider. + */ + initialize_provider(&Provider_nc, ProviderOptions_ClientSideProvider | ProviderOptions_NonClientAreaProvider, hwnd, TRUE); + initialize_provider(&Provider, ProviderOptions_ClientSideProvider, hwnd, TRUE); + Provider_nc.ignore_hwnd_prop = Provider.ignore_hwnd_prop = TRUE; + prov_root = &Provider.IRawElementProviderSimple_iface; + SET_EXPECT(winproc_GETOBJECT_UiaRoot); + /* Only sent on Win7. */ + SET_EXPECT(winproc_GETOBJECT_CLIENT); + hr = UiaNodeFromProvider(&Provider_nc.IRawElementProviderSimple_iface, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(Provider.ref == 2, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider_nc.ref == 2, "Unexpected refcnt %ld\n", Provider_nc.ref); + CHECK_CALLED(winproc_GETOBJECT_UiaRoot); + called_winproc_GETOBJECT_CLIENT = expect_winproc_GETOBJECT_CLIENT = 0; + + hr = UiaGetPropertyValue(node, UIA_ProviderDescriptionPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc(V_BSTR(&v), L"Nonclient", L"Provider_nc", FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Main", L"Provider", FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Hwnd", NULL, TRUE); + VariantClear(&v); + + method_sequences_enabled = TRUE; + Provider.ret_invalid_prop_type = Provider_nc.ret_invalid_prop_type = TRUE; + test_node_hwnd_provider(node, hwnd); + ok_method_sequence(default_hwnd_prov_props_seq, "default_hwnd_prov_props_seq"); + + UiaNodeRelease(node); + ok(Provider.ref == 1, "Unexpected refcnt %ld\n", Provider.ref); + ok(Provider_nc.ref == 1, "Unexpected refcnt %ld\n", Provider_nc.ref); + + DestroyWindow(hwnd); + UnregisterClassA("test_default_clientside_providers class", NULL); + + CoUninitialize(); +} + /* * Once a process returns a UI Automation provider with * UiaReturnRawElementProvider it ends up in an implicit MTA until exit. This @@ -12364,6 +12496,7 @@ START_TEST(uiautomation) test_UiaNavigate(); test_UiaFind(); test_CUIAutomation(); + test_default_clientside_providers(); if (uia_dll) { pUiaProviderFromIAccessible = (void *)GetProcAddress(uia_dll, "UiaProviderFromIAccessible");
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 97 +++++++------- dlls/uiautomationcore/uia_client.c | 21 ++- dlls/uiautomationcore/uia_private.h | 1 + dlls/uiautomationcore/uia_provider.c | 141 +++++++++++++++++++++ 4 files changed, 205 insertions(+), 55 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index f3e671526ec..da9bb6aa5c9 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -6532,15 +6532,14 @@ static DWORD WINAPI uia_node_from_handle_test_thread(LPVOID param) ok(hr == S_OK, "Unexpected hr %#lx\n", hr);
memset(buf, 0, sizeof(buf)); - todo_wine ok(get_nested_provider_desc(V_BSTR(&v), L"Main", TRUE, buf), "Failed to get nested provider description\n"); - if (lstrlenW(buf)) - { - check_node_provider_desc_prefix(buf, GetCurrentProcessId(), hwnd); - 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); - } + 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); + 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_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, FALSE); VariantClear(&v);
Provider_child.ignore_hwnd_prop = FALSE; @@ -6749,7 +6748,7 @@ static void test_UiaNodeFromHandle(const char *name) SET_EXPECT_MULTI(winproc_GETOBJECT_CLIENT, 2); hr = UiaNodeFromHandle(hwnd, &node); /* Windows 10 and below return E_FAIL, Windows 11 returns S_OK. */ - todo_wine ok(hr == S_OK || broken(hr == E_FAIL), "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK || broken(hr == E_FAIL), "Unexpected hr %#lx.\n", hr); CHECK_CALLED(winproc_GETOBJECT_UiaRoot); todo_wine CHECK_CALLED(winproc_GETOBJECT_CLIENT); if (SUCCEEDED(hr)) @@ -6765,23 +6764,20 @@ static void test_UiaNodeFromHandle(const char *name) SET_EXPECT(winproc_GETOBJECT_UiaRoot); SET_EXPECT_MULTI(winproc_GETOBJECT_CLIENT, 2); hr = UiaNodeFromHandle(hwnd, &node); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); 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); - check_node_provider_desc(V_BSTR(&v), L"Annotation", NULL, FALSE); - check_node_provider_desc(V_BSTR(&v), L"Main", NULL, 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(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc_todo(V_BSTR(&v), L"Annotation", NULL, FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Main", NULL, FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE); + VariantClear(&v);
- todo_wine ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n");
/* * COM initialized, but provider passed into UiaReturnRawElementProvider @@ -6794,24 +6790,22 @@ static void test_UiaNodeFromHandle(const char *name) SET_EXPECT(winproc_GETOBJECT_UiaRoot); SET_EXPECT_MULTI(winproc_GETOBJECT_CLIENT, 2); hr = UiaNodeFromHandle(hwnd, &node); - todo_wine ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); 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); - check_node_provider_desc(V_BSTR(&v), L"Annotation", NULL, FALSE); - check_node_provider_desc(V_BSTR(&v), L"Main", NULL, 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(hr == S_OK, "Unexpected hr %#lx\n", hr); + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc_todo(V_BSTR(&v), L"Annotation", NULL, FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Main", NULL, FALSE); + check_node_provider_desc_todo(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_hwnd1, "node_from_hwnd1");
- todo_wine ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n");
/* * COM initialized, but provider passed into UiaReturnRawElementProvider @@ -6830,31 +6824,29 @@ static void test_UiaNodeFromHandle(const char *name) 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(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)) + ok(hr == S_OK, "Unexpected hr %#lx\n", hr); + + check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + if (Provider.ref == 1 || get_provider_desc(V_BSTR(&v), L"Annotation:", NULL)) { - check_node_provider_desc_prefix(V_BSTR(&v), GetCurrentProcessId(), hwnd); + check_node_provider_desc_todo(V_BSTR(&v), L"Annotation", NULL, FALSE); + check_node_provider_desc_todo(V_BSTR(&v), L"Main", NULL, FALSE); + } + else + check_node_provider_desc(V_BSTR(&v), L"Main", L"Provider", FALSE);
- 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_todo(V_BSTR(&v), L"Nonclient", NULL, FALSE); + check_node_provider_desc(V_BSTR(&v), L"Hwnd", NULL, TRUE); + VariantClear(&v);
- 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"); + ok(UiaNodeRelease(node), "UiaNodeRelease returned FALSE\n"); /* * Bug on Windows 8 through Win10v1709 - if we have a RuntimeId failure, * refcount doesn't get decremented. @@ -12335,9 +12327,8 @@ static void test_node_hwnd_provider_(HUIANODE node, HWND hwnd, const char *file, winetest_push_context("UIA_NativeWindowHandlePropertyId"); hr = UiaGetPropertyValue(node, UIA_NativeWindowHandlePropertyId, &v); ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); - todo_wine ok_(file, line)(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); - if (V_VT(&v) == VT_I4) - ok_(file, line)(V_I4(&v) == HandleToUlong(hwnd), "V_I4(&v) = %#lx, expected %#lx\n", V_I4(&v), HandleToUlong(hwnd)); + ok_(file, line)(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok_(file, line)(V_I4(&v) == HandleToUlong(hwnd), "V_I4(&v) = %#lx, expected %#lx\n", V_I4(&v), HandleToUlong(hwnd)); VariantClear(&v); winetest_pop_context();
diff --git a/dlls/uiautomationcore/uia_client.c b/dlls/uiautomationcore/uia_client.c index 92ff41e2c71..4c1f0e3e480 100644 --- a/dlls/uiautomationcore/uia_client.c +++ b/dlls/uiautomationcore/uia_client.c @@ -2646,6 +2646,10 @@ HRESULT WINAPI UiaHUiaNodeFromVariant(VARIANT *in_val, HUIANODE *huianode)
static SAFEARRAY WINAPI *default_uia_provider_callback(HWND hwnd, enum ProviderType prov_type) { + IRawElementProviderSimple *elprov = NULL; + SAFEARRAY *sa = NULL; + HRESULT hr; + switch (prov_type) { case ProviderType_Proxy: @@ -2657,14 +2661,27 @@ static SAFEARRAY WINAPI *default_uia_provider_callback(HWND hwnd, enum ProviderT break;
case ProviderType_BaseHwnd: - FIXME("Default ProviderType_BaseHwnd provider unimplemented.\n"); + hr = create_base_hwnd_provider(hwnd, &elprov); + if (FAILED(hr)) + WARN("create_base_hwnd_provider failed with hr %#lx\n", hr); break;
default: break; }
- return NULL; + if (elprov) + { + LONG idx = 0; + + sa = SafeArrayCreateVector(VT_UNKNOWN, 0, 1); + if (sa) + SafeArrayPutElement(sa, &idx, (void *)elprov); + + IRawElementProviderSimple_Release(elprov); + } + + return sa; }
static UiaProviderCallback *uia_provider_callback = default_uia_provider_callback; diff --git a/dlls/uiautomationcore/uia_private.h b/dlls/uiautomationcore/uia_private.h index 63c6f7dde22..3b1f21645fb 100644 --- a/dlls/uiautomationcore/uia_private.h +++ b/dlls/uiautomationcore/uia_private.h @@ -151,6 +151,7 @@ const struct uia_pattern_info *uia_pattern_info_from_id(PATTERNID pattern_id) DE const struct uia_control_type_info *uia_control_type_info_from_id(CONTROLTYPEID control_type_id) DECLSPEC_HIDDEN;
/* uia_provider.c */ +HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov) DECLSPEC_HIDDEN; 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 59be5d3782a..4d081b2c921 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1202,6 +1202,147 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD return S_OK; }
+/* + * Default ProviderType_BaseHwnd IRawElementProviderSimple interface. + */ +struct base_hwnd_provider { + IRawElementProviderSimple IRawElementProviderSimple_iface; + LONG refcount; + + HWND hwnd; +}; + +static inline struct base_hwnd_provider *impl_from_base_hwnd_provider(IRawElementProviderSimple *iface) +{ + return CONTAINING_RECORD(iface, struct base_hwnd_provider, IRawElementProviderSimple_iface); +} + +static HRESULT WINAPI base_hwnd_provider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + return E_NOINTERFACE; + + IRawElementProviderSimple_AddRef(iface); + return S_OK; +} + +static ULONG WINAPI base_hwnd_provider_AddRef(IRawElementProviderSimple *iface) +{ + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface); + ULONG refcount = InterlockedIncrement(&base_hwnd_prov->refcount); + + TRACE("%p, refcount %ld\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI base_hwnd_provider_Release(IRawElementProviderSimple *iface) +{ + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface); + ULONG refcount = InterlockedDecrement(&base_hwnd_prov->refcount); + + TRACE("%p, refcount %ld\n", iface, refcount); + + if (!refcount) + heap_free(base_hwnd_prov); + + return refcount; +} + +static HRESULT WINAPI base_hwnd_provider_get_ProviderOptions(IRawElementProviderSimple *iface, + enum ProviderOptions *ret_val) +{ + TRACE("%p, %p\n", iface, ret_val); + *ret_val = ProviderOptions_ClientSideProvider; + return S_OK; +} + +static HRESULT WINAPI base_hwnd_provider_GetPatternProvider(IRawElementProviderSimple *iface, + PATTERNID pattern_id, IUnknown **ret_val) +{ + FIXME("%p, %d, %p: stub\n", iface, pattern_id, ret_val); + *ret_val = NULL; + return E_NOTIMPL; +} + +static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSimple *iface, + PROPERTYID prop_id, VARIANT *ret_val) +{ + struct base_hwnd_provider *base_hwnd_prov = impl_from_base_hwnd_provider(iface); + HRESULT hr = S_OK; + + TRACE("%p, %d, %p\n", iface, prop_id, ret_val); + + VariantInit(ret_val); + if (!IsWindow(base_hwnd_prov->hwnd)) + return UIA_E_ELEMENTNOTAVAILABLE; + + switch (prop_id) + { + case UIA_ProviderDescriptionPropertyId: + V_VT(ret_val) = VT_BSTR; + V_BSTR(ret_val) = SysAllocString(L"Wine: HWND Proxy"); + break; + + case UIA_NativeWindowHandlePropertyId: + V_VT(ret_val) = VT_I4; + V_I4(ret_val) = HandleToUlong(base_hwnd_prov->hwnd); + break; + + default: + break; + } + + if (FAILED(hr)) + VariantClear(ret_val); + + return hr; +} + +static HRESULT WINAPI base_hwnd_provider_get_HostRawElementProvider(IRawElementProviderSimple *iface, + IRawElementProviderSimple **ret_val) +{ + TRACE("%p, %p\n", iface, ret_val); + *ret_val = NULL; + return S_OK; +} + +static const IRawElementProviderSimpleVtbl base_hwnd_provider_vtbl = { + base_hwnd_provider_QueryInterface, + base_hwnd_provider_AddRef, + base_hwnd_provider_Release, + base_hwnd_provider_get_ProviderOptions, + base_hwnd_provider_GetPatternProvider, + base_hwnd_provider_GetPropertyValue, + base_hwnd_provider_get_HostRawElementProvider, +}; + +HRESULT create_base_hwnd_provider(HWND hwnd, IRawElementProviderSimple **elprov) +{ + struct base_hwnd_provider *base_hwnd_prov; + + *elprov = NULL; + + if (!hwnd) + return E_INVALIDARG; + + if (!IsWindow(hwnd)) + return UIA_E_ELEMENTNOTAVAILABLE; + + if (!(base_hwnd_prov = heap_alloc_zero(sizeof(*base_hwnd_prov)))) + return E_OUTOFMEMORY; + + base_hwnd_prov->IRawElementProviderSimple_iface.lpVtbl = &base_hwnd_provider_vtbl; + base_hwnd_prov->refcount = 1; + base_hwnd_prov->hwnd = hwnd; + *elprov = &base_hwnd_prov->IRawElementProviderSimple_iface; + + return S_OK; +} + /* * UI Automation provider thread functions. */
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 5 ++--- dlls/uiautomationcore/uia_provider.c | 25 ++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index da9bb6aa5c9..009864d10c7 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -12357,9 +12357,8 @@ static void test_node_hwnd_provider_(HUIANODE node, HWND hwnd, const char *file, winetest_push_context("UIA_ProcessIdPropertyId"); hr = UiaGetPropertyValue(node, UIA_ProcessIdPropertyId, &v); ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); - todo_wine ok_(file, line)(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); - if (V_VT(&v) == VT_I4) - ok_(file, line)(V_I4(&v) == pid, "V_I4(&v) = %#lx, expected %#lx\n", V_I4(&v), pid); + ok_(file, line)(V_VT(&v) == VT_I4, "Unexpected VT %d\n", V_VT(&v)); + ok_(file, line)(V_I4(&v) == pid, "V_I4(&v) = %#lx, expected %#lx\n", V_I4(&v), pid); VariantClear(&v); winetest_pop_context(); } diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 4d081b2c921..1f5cce7ffed 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1202,6 +1202,16 @@ HRESULT WINAPI UiaProviderFromIAccessible(IAccessible *acc, long child_id, DWORD return S_OK; }
+static HRESULT uia_get_hr_for_last_error(void) +{ + DWORD last_err = GetLastError(); + + if (last_err == ERROR_INVALID_WINDOW_HANDLE) + return UIA_E_ELEMENTNOTAVAILABLE; + + return HRESULT_FROM_WIN32(last_err); +} + /* * Default ProviderType_BaseHwnd IRawElementProviderSimple interface. */ @@ -1292,6 +1302,21 @@ static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSim V_I4(ret_val) = HandleToUlong(base_hwnd_prov->hwnd); break;
+ case UIA_ProcessIdPropertyId: + { + DWORD pid; + + SetLastError(NOERROR); + if (!GetWindowThreadProcessId(base_hwnd_prov->hwnd, &pid)) + hr = uia_get_hr_for_last_error(); + else + { + V_VT(ret_val) = VT_I4; + V_I4(ret_val) = pid; + } + break; + } + default: break; }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 5 ++--- dlls/uiautomationcore/uia_provider.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index 009864d10c7..beb34fb400f 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -12347,9 +12347,8 @@ static void test_node_hwnd_provider_(HUIANODE node, HWND hwnd, const char *file, GetClassNameW(hwnd, buf, ARRAY_SIZE(buf)); hr = UiaGetPropertyValue(node, UIA_ClassNamePropertyId, &v); ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); - todo_wine ok_(file, line)(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); - if (V_VT(&v) == VT_BSTR) - ok(!lstrcmpW(V_BSTR(&v), buf), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); + ok_(file, line)(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), buf), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); VariantClear(&v); winetest_pop_context();
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 1f5cce7ffed..74930728362 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1317,6 +1317,21 @@ static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSim break; }
+ case UIA_ClassNamePropertyId: + { + WCHAR buf[256] = { 0 }; + + SetLastError(NOERROR); + if (!GetClassNameW(base_hwnd_prov->hwnd, buf, ARRAY_SIZE(buf))) + hr = uia_get_hr_for_last_error(); + else + { + V_VT(ret_val) = VT_BSTR; + V_BSTR(ret_val) = SysAllocString(buf); + } + break; + } + default: break; }
From: Connor McAdams cmcadams@codeweavers.com
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/uiautomation.c | 5 +-- dlls/uiautomationcore/uia_provider.c | 43 +++++++++++++++++++++- 2 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index beb34fb400f..ddeb3f3f055 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -12336,9 +12336,8 @@ static void test_node_hwnd_provider_(HUIANODE node, HWND hwnd, const char *file, SendMessageW(hwnd, WM_GETTEXT, ARRAY_SIZE(buf), (LPARAM)buf); hr = UiaGetPropertyValue(node, UIA_NamePropertyId, &v); ok_(file, line)(hr == S_OK, "Unexpected hr %#lx\n", hr); - todo_wine ok_(file, line)(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); - if (V_VT(&v) == VT_BSTR) - ok(!lstrcmpW(V_BSTR(&v), buf), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); + ok_(file, line)(V_VT(&v) == VT_BSTR, "Unexpected VT %d\n", V_VT(&v)); + ok(!lstrcmpW(V_BSTR(&v), buf), "Unexpected BSTR %s\n", wine_dbgstr_w(V_BSTR(&v))); VariantClear(&v); winetest_pop_context();
diff --git a/dlls/uiautomationcore/uia_provider.c b/dlls/uiautomationcore/uia_provider.c index 74930728362..cf131487d61 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1206,10 +1206,28 @@ static HRESULT uia_get_hr_for_last_error(void) { DWORD last_err = GetLastError();
- if (last_err == ERROR_INVALID_WINDOW_HANDLE) + switch (last_err) + { + case ERROR_INVALID_WINDOW_HANDLE: return UIA_E_ELEMENTNOTAVAILABLE;
- return HRESULT_FROM_WIN32(last_err); + case ERROR_TIMEOUT: + return UIA_E_TIMEOUT; + + default: + return HRESULT_FROM_WIN32(last_err); + } +} + +#define UIA_DEFAULT_MSG_TIMEOUT 10000 +static HRESULT uia_send_message_timeout(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, UINT timeout, LRESULT *lres) +{ + *lres = 0; + SetLastError(NOERROR); + if (!SendMessageTimeoutW(hwnd, msg, wparam, lparam, SMTO_NORMAL, timeout, (PDWORD_PTR)lres)) + return uia_get_hr_for_last_error(); + + return S_OK; }
/* @@ -1332,6 +1350,27 @@ static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSim break; }
+ case UIA_NamePropertyId: + { + LRESULT lres; + + V_VT(ret_val) = VT_BSTR; + V_BSTR(ret_val) = SysAllocString(L""); + hr = uia_send_message_timeout(base_hwnd_prov->hwnd, WM_GETTEXTLENGTH, 0, 0, UIA_DEFAULT_MSG_TIMEOUT, &lres); + if (FAILED(hr) || !lres) + break; + + if (!SysReAllocStringLen(&V_BSTR(ret_val), NULL, lres)) + { + hr = E_OUTOFMEMORY; + break; + } + + hr = uia_send_message_timeout(base_hwnd_prov->hwnd, WM_GETTEXT, SysStringLen(V_BSTR(ret_val)) + 1, + (LPARAM)V_BSTR(ret_val), UIA_DEFAULT_MSG_TIMEOUT, &lres); + break; + } + default: break; }
Esme Povirk (@madewokherd) commented about dlls/uiautomationcore/uia_provider.c:
V_I4(ret_val) = HandleToUlong(base_hwnd_prov->hwnd); break;
- case UIA_ProcessIdPropertyId:
- {
DWORD pid;
SetLastError(NOERROR);
Technically, NOERROR is an HRESULT, NO_ERROR is Win32 error.
This also doesn't make much sense to me. AFAICT GetWindowThreadProcessId in Wine should always set an error if it fails, and if it doesn't I'm not sure S_OK is an improvement.
On Mon Apr 3 19:09:38 2023 +0000, Esme Povirk wrote:
Technically, NOERROR is an HRESULT, NO_ERROR is Win32 error. This also doesn't make much sense to me. AFAICT GetWindowThreadProcessId in Wine should always set an error if it fails, and if it doesn't I'm not sure S_OK is an improvement.
Hm, I guess I hadn't considered a case where GetWindowThreadProcessId would return 0 without setting an error resulting in an S_OK return value.
I mainly did this to avoid accidentally running into a case where we're reusing an old error value (which is something we do in uia_client.c), but I guess in the case of GetWindowThreadProcessId, we can safely assume that a zero return value means the window is invalid and return `UIA_E_ELEMENTNOTAVAILABLE`