-- v2: uiautomationcore: Implement UIA_NamePropertyId for default HWND provider. uiautomationcore: Implement UIA_ClassNamePropertyId for default HWND provider. uiautomationcore: Implement UIA_ProcessIdPropertyId for default HWND provider.
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 | 12 ++++++++++++ 2 files changed, 14 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..afc44fe45ac 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1292,6 +1292,18 @@ static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSim V_I4(ret_val) = HandleToUlong(base_hwnd_prov->hwnd); break;
+ case UIA_ProcessIdPropertyId: + { + DWORD pid; + + if (!GetWindowThreadProcessId(base_hwnd_prov->hwnd, &pid)) + return UIA_E_ELEMENTNOTAVAILABLE; + + 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 | 24 ++++++++++++++++++++++ 2 files changed, 26 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 afc44fe45ac..9c9ad2a426e 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 E_FAIL; +} + /* * Default ProviderType_BaseHwnd IRawElementProviderSimple interface. */ @@ -1304,6 +1314,20 @@ static HRESULT WINAPI base_hwnd_provider_GetPropertyValue(IRawElementProviderSim break; }
+ case UIA_ClassNamePropertyId: + { + WCHAR buf[256] = { 0 }; + + 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 | 42 ++++++++++++++++++++-- 2 files changed, 42 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 9c9ad2a426e..ed00abaf33b 100644 --- a/dlls/uiautomationcore/uia_provider.c +++ b/dlls/uiautomationcore/uia_provider.c @@ -1206,10 +1206,27 @@ 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 E_FAIL; + case ERROR_TIMEOUT: + return UIA_E_TIMEOUT; + + default: + return E_FAIL; + } +} + +#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; + if (!SendMessageTimeoutW(hwnd, msg, wparam, lparam, SMTO_NORMAL, timeout, (PDWORD_PTR)lres)) + return uia_get_hr_for_last_error(); + + return S_OK; }
/* @@ -1328,6 +1345,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; }
V2:
- Don't call `SetLastError()` unnecessarily. - Return `E_FAIL` by default from `uia_get_hr_for_last_error()` instead of `HRESULT_FROM_WIN32`. - Return `UIA_E_ELEMENTNOTAVAILABLE` when `GetWindowThreadProcessId` fails, since it will only fail on an invalid window handle.
This merge request was approved by Esme Povirk.