Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- -v4: replace cast to long with HandleToUlong. dlls/uiautomationcore/Makefile.in | 1 + dlls/uiautomationcore/uia_main.c | 125 +++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-)
diff --git a/dlls/uiautomationcore/Makefile.in b/dlls/uiautomationcore/Makefile.in index 71ea7b99c94..1f6ad85c24f 100644 --- a/dlls/uiautomationcore/Makefile.in +++ b/dlls/uiautomationcore/Makefile.in @@ -1,5 +1,6 @@ MODULE = uiautomationcore.dll IMPORTLIB = uiautomationcore +IMPORTS = oleaut32 user32
EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/uiautomationcore/uia_main.c b/dlls/uiautomationcore/uia_main.c index 2dada95af80..1e46b6e7e32 100644 --- a/dlls/uiautomationcore/uia_main.c +++ b/dlls/uiautomationcore/uia_main.c @@ -16,9 +16,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define COBJMACROS + +#include "windows.h" +#include "initguid.h" #include "uiautomation.h"
#include "wine/debug.h" +#include "wine/heap.h"
WINE_DEFAULT_DEBUG_CHANNEL(uiautomation);
@@ -84,10 +89,126 @@ void WINAPI UiaRegisterProviderCallback(UiaProviderCallback *callback) FIXME("(%p): stub\n", callback); }
+typedef struct { + IRawElementProviderSimple IRawElementProviderSimple_iface; + LONG ref; + + HWND hwnd; +} HwndProvider; + +static inline HwndProvider *impl_from_HwndProvider(IRawElementProviderSimple *iface) +{ + return CONTAINING_RECORD(iface, HwndProvider, IRawElementProviderSimple_iface); +} + +HRESULT WINAPI HwndProvider_QueryInterface(IRawElementProviderSimple *iface, REFIID riid, void **ppv) +{ + if (IsEqualIID(riid, &IID_IRawElementProviderSimple) || + IsEqualIID(riid, &IID_IUnknown)) + *ppv = iface; + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + + IRawElementProviderSimple_AddRef(iface); + return S_OK; +} + +ULONG WINAPI HwndProvider_AddRef(IRawElementProviderSimple *iface) +{ + HwndProvider *This = impl_from_HwndProvider(iface); + return InterlockedIncrement(&This->ref); +} + +ULONG WINAPI HwndProvider_Release(IRawElementProviderSimple *iface) +{ + HwndProvider *This = impl_from_HwndProvider(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + if (!ref) + heap_free(This); + + return ref; +} + +HRESULT WINAPI HwndProvider_get_ProviderOptions(IRawElementProviderSimple *iface, + enum ProviderOptions *ret_val) +{ + *ret_val = ProviderOptions_ServerSideProvider; + return S_OK; +} + +HRESULT WINAPI HwndProvider_GetPatternProvider(IRawElementProviderSimple *iface, + PATTERNID pattern_id, IUnknown **ret_val) +{ + *ret_val = NULL; + return S_OK; +} + +HRESULT WINAPI HwndProvider_GetPropertyValue(IRawElementProviderSimple *iface, + PROPERTYID property_id, VARIANT *ret_val) +{ + HwndProvider *This = impl_from_HwndProvider(iface); + + switch (property_id) + { + case UIA_NativeWindowHandlePropertyId: + V_VT(ret_val) = VT_I4; + V_I4(ret_val) = HandleToUlong(This->hwnd); + break; + + case UIA_ProviderDescriptionPropertyId: + V_VT(ret_val) = VT_BSTR; + V_BSTR(ret_val) = SysAllocString(L"Wine: HWND Provider Proxy"); + break; + + default: + V_VT(ret_val) = VT_EMPTY; + break; + } + + return S_OK; +} + +HRESULT WINAPI HwndProvider_get_HostRawElementProvider(IRawElementProviderSimple *iface, + IRawElementProviderSimple **ret_val) +{ + *ret_val = NULL; + return S_OK; +} + +IRawElementProviderSimpleVtbl HwndProviderVtbl = { + HwndProvider_QueryInterface, + HwndProvider_AddRef, + HwndProvider_Release, + HwndProvider_get_ProviderOptions, + HwndProvider_GetPatternProvider, + HwndProvider_GetPropertyValue, + HwndProvider_get_HostRawElementProvider, +}; + HRESULT WINAPI UiaHostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **provider) { - FIXME("(%p, %p): stub\n", hwnd, provider); - return E_NOTIMPL; + HwndProvider *hwnd_prov; + + TRACE("(%p, %p)\n", hwnd, provider); + + *provider = NULL; + if (!IsWindow(hwnd)) + return E_INVALIDARG; + + hwnd_prov = heap_alloc_zero(sizeof(*hwnd_prov)); + if (!hwnd_prov) + return E_OUTOFMEMORY; + + hwnd_prov->IRawElementProviderSimple_iface.lpVtbl = &HwndProviderVtbl; + hwnd_prov->ref = 1; + hwnd_prov->hwnd = hwnd; + *provider = &hwnd_prov->IRawElementProviderSimple_iface; + + return S_OK; }
HRESULT WINAPI UiaDisconnectProvider(IRawElementProviderSimple *provider)
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/uiautomationcore/tests/Makefile.in | 2 +- dlls/uiautomationcore/tests/uiautomation.c | 57 +++++++++++++++++++--- 2 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/dlls/uiautomationcore/tests/Makefile.in b/dlls/uiautomationcore/tests/Makefile.in index c39b062b6fd..d780b9feaf2 100644 --- a/dlls/uiautomationcore/tests/Makefile.in +++ b/dlls/uiautomationcore/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = uiautomationcore.dll -IMPORTS = uiautomationcore user32 +IMPORTS = uiautomationcore user32 oleaut32
C_SRCS = \ uiautomation.c diff --git a/dlls/uiautomationcore/tests/uiautomation.c b/dlls/uiautomationcore/tests/uiautomation.c index cbcba1af294..4ebd3eac33b 100644 --- a/dlls/uiautomationcore/tests/uiautomation.c +++ b/dlls/uiautomationcore/tests/uiautomation.c @@ -34,9 +34,12 @@ static LRESULT WINAPI test_wnd_proc(HWND hwnd, UINT message, WPARAM wParam, LPAR static void test_UiaHostProviderFromHwnd(void) { IRawElementProviderSimple *p, *p2; + enum ProviderOptions prov_opt; + unsigned int i; WNDCLASSA cls; HRESULT hr; HWND hwnd; + VARIANT v;
cls.style = 0; cls.lpfnWndProc = test_wnd_proc; @@ -58,17 +61,13 @@ static void test_UiaHostProviderFromHwnd(void)
p = (void *)0xdeadbeef; hr = UiaHostProviderFromHwnd(NULL, &p); -todo_wine { ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); ok(p == NULL, "Unexpected instance.\n"); -} p = NULL; + hr = UiaHostProviderFromHwnd(hwnd, &p); -todo_wine ok(hr == S_OK, "Failed to get host provider, hr %#x.\n", hr);
-if (hr == S_OK) -{ p2 = NULL; hr = UiaHostProviderFromHwnd(hwnd, &p2); ok(hr == S_OK, "Failed to get host provider, hr %#x.\n", hr); @@ -79,10 +78,54 @@ if (hr == S_OK) ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(p2 == NULL, "Unexpected instance.\n");
- IRawElementProviderSimple_Release(p); -} + hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_NativeWindowHandlePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v)); + ok(V_I4(&v) == (INT)hwnd, "V_I4(&v) = %#x\n", V_I4(&v));
+ hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_ProviderDescriptionPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(V_VT(&v) == VT_BSTR, "V_VT(&v) = %d\n", V_VT(&v)); + SysFreeString(V_BSTR(&v)); + + /* No patterns are implemented on the HWND provider. */ + for (i = 10000; i < 10034; i++) + { + IUnknown *unk; + + unk = NULL; + hr = IRawElementProviderSimple_GetPatternProvider(p, i, &unk); + ok(hr == S_OK, "Unexpected hr %#x, %d.\n", hr, i); + ok(!unk, "Pattern %d returned %p\n", i, unk); + } + + hr = IRawElementProviderSimple_get_ProviderOptions(p, &prov_opt); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok((prov_opt == ProviderOptions_ServerSideProvider) || + broken(prov_opt == ProviderOptions_ClientSideProvider), /* Windows < 10 1507 */ + "Unexpected provider options %#x\n", prov_opt); + + /* Test behavior post Window destruction. */ DestroyWindow(hwnd); + + hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_NativeWindowHandlePropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v)); + ok(V_I4(&v) == (INT)hwnd, "V_I4(&v) = %#x\n", V_I4(&v)); + + hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_ProviderDescriptionPropertyId, &v); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok(V_VT(&v) == VT_BSTR, "V_VT(&v) = %d\n", V_VT(&v)); + SysFreeString(V_BSTR(&v)); + + hr = IRawElementProviderSimple_get_ProviderOptions(p, &prov_opt); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); + ok((prov_opt == ProviderOptions_ServerSideProvider) || + broken(prov_opt == ProviderOptions_ClientSideProvider), /* Windows < 10 1507 */ + "Unexpected provider options %#x\n", prov_opt); + + IRawElementProviderSimple_Release(p); + UnregisterClassA("HostProviderFromHwnd class", NULL); }
On 9/25/21 2:01 AM, Connor McAdams wrote:
- hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_NativeWindowHandlePropertyId, &v);
- ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
- ok(V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v));
- ok(V_I4(&v) == (INT)hwnd, "V_I4(&v) = %#x\n", V_I4(&v));
This should use same casting macro that you're using in implementation now.
- hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_ProviderDescriptionPropertyId, &v);
- ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
- ok(V_VT(&v) == VT_BSTR, "V_VT(&v) = %d\n", V_VT(&v));
- SysFreeString(V_BSTR(&v));
VariantClear() is more appropriate here.
- /* No patterns are implemented on the HWND provider. */
- for (i = 10000; i < 10034; i++)
- {
IUnknown *unk;
unk = NULL;
hr = IRawElementProviderSimple_GetPatternProvider(p, i, &unk);
ok(hr == S_OK, "Unexpected hr %#x, %d.\n", hr, i);
ok(!unk, "Pattern %d returned %p\n", i, unk);
- }
I'm only curious, does this particular index range has some defined meaning that you need to test all of it?
Note that "unk" check doesn't tell you if pointer was reset by this method.
On Sun, Sep 26, 2021 at 12:40:00PM +0300, Nikolay Sivov wrote:
On 9/25/21 2:01 AM, Connor McAdams wrote:
- hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_NativeWindowHandlePropertyId, &v);
- ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
- ok(V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v));
- ok(V_I4(&v) == (INT)hwnd, "V_I4(&v) = %#x\n", V_I4(&v));
This should use same casting macro that you're using in implementation now.
- hr = IRawElementProviderSimple_GetPropertyValue(p, UIA_ProviderDescriptionPropertyId, &v);
- ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
- ok(V_VT(&v) == VT_BSTR, "V_VT(&v) = %d\n", V_VT(&v));
- SysFreeString(V_BSTR(&v));
VariantClear() is more appropriate here.
- /* No patterns are implemented on the HWND provider. */
- for (i = 10000; i < 10034; i++)
- {
IUnknown *unk;
unk = NULL;
hr = IRawElementProviderSimple_GetPatternProvider(p, i, &unk);
ok(hr == S_OK, "Unexpected hr %#x, %d.\n", hr, i);
ok(!unk, "Pattern %d returned %p\n", i, unk);
- }
I'm only curious, does this particular index range has some defined meaning that you need to test all of it?
Note that "unk" check doesn't tell you if pointer was reset by this method.
Yeah, this index range is the range of UIA_InvokePatternId through the last defined UIA_CustomNavigationPatternId. I could add definitions for these in the headers, I was just trying to avoid adding extra data that really doesn't get used per se in this patch. But it probably makes sense to just add the definitions for these into the headers.
Thanks for the review. I'll fix these things and send in a v5.
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=98757
Your paranoid android.
=== debiant2 (32 bit report) ===
uiautomationcore: uiautomation.c:62: Test succeeded inside todo block: Unexpected hr 0x80070057. uiautomation.c:63: Test succeeded inside todo block: Unexpected instance. uiautomation.c:68: Test succeeded inside todo block: Failed to get host provider, hr 0.
=== debiant2 (32 bit Chinese:China report) ===
uiautomationcore: uiautomation.c:62: Test succeeded inside todo block: Unexpected hr 0x80070057. uiautomation.c:63: Test succeeded inside todo block: Unexpected instance. uiautomation.c:68: Test succeeded inside todo block: Failed to get host provider, hr 0.
=== debiant2 (32 bit WoW report) ===
uiautomationcore: uiautomation.c:62: Test succeeded inside todo block: Unexpected hr 0x80070057. uiautomation.c:63: Test succeeded inside todo block: Unexpected instance. uiautomation.c:68: Test succeeded inside todo block: Failed to get host provider, hr 0.
=== debiant2 (64 bit WoW report) ===
uiautomationcore: uiautomation.c:62: Test succeeded inside todo block: Unexpected hr 0x80070057. uiautomation.c:63: Test succeeded inside todo block: Unexpected instance. uiautomation.c:68: Test succeeded inside todo block: Failed to get host provider, hr 0.
On 9/25/21 2:01 AM, Connor McAdams wrote:
+ULONG WINAPI HwndProvider_Release(IRawElementProviderSimple *iface) +{
- HwndProvider *This = impl_from_HwndProvider(iface);
- ULONG ref = InterlockedDecrement(&This->ref);
- if (!ref)
heap_free(This);
- return ref;
+}
For newly added parts there is an opportunity to use cleaner types and names from the start, like "struct hwndprovider" and "provider" instead of non-specific "This".
It's obviously not a blocker, but something to consider.