Add support for using the accNavigate method to retrieve an HWND for an IAccessible, and also only recursively go up the parent chain if the initial IAccessible didn't have an IOleWindow implementation.
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/main.c | 34 +++++++++-- dlls/oleacc/tests/main.c | 123 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 151 insertions(+), 6 deletions(-)
diff --git a/dlls/oleacc/main.c b/dlls/oleacc/main.c index dced84be7b5..641f4b22419 100644 --- a/dlls/oleacc/main.c +++ b/dlls/oleacc/main.c @@ -402,20 +402,36 @@ HRESULT WINAPI WindowFromAccessibleObject(IAccessible *acc, HWND *phwnd) { IDispatch *parent; IOleWindow *ow; + VARIANT v, cid; HRESULT hres;
TRACE("%p %p\n", acc, phwnd);
IAccessible_AddRef(acc); - while(1) { - hres = IAccessible_QueryInterface(acc, &IID_IOleWindow, (void**)&ow); - if(SUCCEEDED(hres)) { - hres = IOleWindow_GetWindow(ow, phwnd); - IOleWindow_Release(ow); + + ow = NULL; + hres = IAccessible_QueryInterface(acc, &IID_IOleWindow, (void**)&ow); + if(SUCCEEDED(hres)) { + hres = IOleWindow_GetWindow(ow, phwnd); + IOleWindow_Release(ow); + if(*phwnd) { IAccessible_Release(acc); return hres; } + } + + VariantInit(&v); + variant_init_i4(&cid, CHILDID_SELF); + hres = IAccessible_accNavigate(acc, 10, cid, &v); + if(SUCCEEDED(hres) && V_VT(&v) == VT_I4) + *phwnd = ULongToHandle(V_I4(&v)); + + if(ow) { + IAccessible_Release(acc); + return S_OK; + }
+ while(1) { hres = IAccessible_get_accParent(acc, &parent); IAccessible_Release(acc); if(FAILED(hres)) @@ -429,6 +445,14 @@ HRESULT WINAPI WindowFromAccessibleObject(IAccessible *acc, HWND *phwnd) IDispatch_Release(parent); if(FAILED(hres)) return hres; + + hres = IAccessible_QueryInterface(acc, &IID_IOleWindow, (void**)&ow); + if(SUCCEEDED(hres)) { + hres = IOleWindow_GetWindow(ow, phwnd); + IOleWindow_Release(ow); + IAccessible_Release(acc); + return hres; + } } }
diff --git a/dlls/oleacc/tests/main.c b/dlls/oleacc/tests/main.c index 7854764ec08..0bf49056a04 100644 --- a/dlls/oleacc/tests/main.c +++ b/dlls/oleacc/tests/main.c @@ -59,8 +59,10 @@ DEFINE_EXPECT(Accessible_get_accChildCount); DEFINE_EXPECT(Accessible_get_accChild); DEFINE_EXPECT(Accessible_get_accName); DEFINE_EXPECT(Accessible_get_accParent); +DEFINE_EXPECT(Accessible_accNavigate); DEFINE_EXPECT(Accessible_child_get_accName); DEFINE_EXPECT(Accessible_child_get_accParent); +DEFINE_EXPECT(Accessible_child_accNavigate);
static HANDLE (WINAPI *pGetProcessHandleFromHwnd)(HWND);
@@ -91,7 +93,11 @@ static BOOL iface_cmp(IUnknown *iface1, IUnknown *iface2) return unk1 == unk2; }
+static IAccessible Accessible; static IAccessible Accessible_child; +static IOleWindow OleWindow; +static HWND Accessible_accnav_hwnd = NULL; +static HWND OleWindow_hwnd = NULL;
static HRESULT WINAPI Accessible_QueryInterface( IAccessible *iface, REFIID riid, void **ppvObject) @@ -104,6 +110,12 @@ static HRESULT WINAPI Accessible_QueryInterface( return S_OK; }
+ if(IsEqualIID(riid, &IID_IOleWindow) && (iface == &Accessible)) { + *ppvObject = &OleWindow; + IAccessible_AddRef(iface); + return S_OK; + } + if(IsEqualIID(riid, &IID_IEnumVARIANT)) { CHECK_EXPECT(Accessible_QI_IEnumVARIANT); return E_NOINTERFACE; @@ -155,7 +167,12 @@ static HRESULT WINAPI Accessible_get_accParent( IAccessible *iface, IDispatch **ppdispParent) { if(iface == &Accessible_child) + { CHECK_EXPECT(Accessible_child_get_accParent); + if (OleWindow_hwnd) + return IAccessible_QueryInterface(&Accessible, &IID_IDispatch, + (void **)ppdispParent); + } else CHECK_EXPECT(Accessible_get_accParent);
@@ -295,7 +312,21 @@ static HRESULT WINAPI Accessible_accLocation(IAccessible *iface, LONG *pxLeft, static HRESULT WINAPI Accessible_accNavigate(IAccessible *iface, LONG navDir, VARIANT varStart, VARIANT *pvarEnd) { - ok(0, "unexpected call\n"); + if(iface == &Accessible_child) + CHECK_EXPECT(Accessible_child_accNavigate); + else + CHECK_EXPECT(Accessible_accNavigate); + + /* + * Magic number value for retrieving an HWND. Used by DynamicAnnotation + * IAccessible wrapper. + */ + if(navDir == 10) { + V_VT(pvarEnd) = VT_I4; + V_I4(pvarEnd) = HandleToUlong(Accessible_accnav_hwnd); + return S_OK; + } + return E_NOTIMPL; }
@@ -358,8 +389,44 @@ static IAccessibleVtbl AccessibleVtbl = { Accessible_put_accValue };
+static HRESULT WINAPI OleWindow_QueryInterface(IOleWindow *iface, REFIID riid, void **obj) +{ + return IAccessible_QueryInterface(&Accessible, riid, obj); +} + +static ULONG WINAPI OleWindow_AddRef(IOleWindow *iface) +{ + return IAccessible_AddRef(&Accessible); +} + +static ULONG WINAPI OleWindow_Release(IOleWindow *iface) +{ + return IAccessible_Release(&Accessible); +} + +static HRESULT WINAPI OleWindow_GetWindow(IOleWindow *iface, HWND *hwnd) +{ + *hwnd = OleWindow_hwnd; + return S_OK; +} + +static HRESULT WINAPI OleWindow_ContextSensitiveHelp(IOleWindow *iface, BOOL f_enter_mode) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static const IOleWindowVtbl OleWindowVtbl = { + OleWindow_QueryInterface, + OleWindow_AddRef, + OleWindow_Release, + OleWindow_GetWindow, + OleWindow_ContextSensitiveHelp +}; + static IAccessible Accessible = {&AccessibleVtbl}; static IAccessible Accessible_child = {&AccessibleVtbl}; +static IOleWindow OleWindow = {&OleWindowVtbl};
static void test_getroletext(void) { @@ -1826,6 +1893,59 @@ static void test_default_edit_accessible_object(void) DestroyWindow(hwnd); }
+static void test_WindowFromAccessibleObject(void) +{ + HWND hwnd; + HRESULT hr; + + hwnd = CreateWindowA("oleacc_test", "test", WS_OVERLAPPEDWINDOW, + 0, 0, 0, 0, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "CreateWindow failed\n"); + + /* Successfully retrieve an HWND from the IOleWindow interface. */ + Accessible_accnav_hwnd = NULL; + OleWindow_hwnd = (HWND)0xdeadf00d; + hwnd = (HWND)0xdeadbeef; + hr = WindowFromAccessibleObject(&Accessible, &hwnd); + ok(hr == S_OK, "got %lx\n", hr); + ok(hwnd == (HWND)0xdeadf00d, "hwnd != 0xdeadf00d!\n"); + + /* Successfully retrieve an HWND from IAccessible::accNavigate. */ + Accessible_accnav_hwnd = (HWND)0xdeadf00d; + OleWindow_hwnd = NULL; + hwnd = (HWND)0xdeadbeef; + SET_EXPECT(Accessible_accNavigate); + hr = WindowFromAccessibleObject(&Accessible, &hwnd); + ok(hr == S_OK, "got %lx\n", hr); + ok(hwnd == (HWND)0xdeadf00d, "hwnd != 0xdeadf00d!\n"); + CHECK_CALLED(Accessible_accNavigate); + + /* Return a NULL HWND from both methods, no accParent call. */ + Accessible_accnav_hwnd = NULL; + OleWindow_hwnd = NULL; + hwnd = (HWND)0xdeadbeef; + SET_EXPECT(Accessible_accNavigate); + hr = WindowFromAccessibleObject(&Accessible, &hwnd); + ok(hr == S_OK, "got %lx\n", hr); + ok(!hwnd, "hwnd %p\n", hwnd); + CHECK_CALLED(Accessible_accNavigate); + + /* Successfully retrieve an HWND from a parent IAccessible's IOleWindow interface. */ + Accessible_accnav_hwnd = NULL; + OleWindow_hwnd = (HWND)0xdeadf00d; + hwnd = (HWND)0xdeadbeef; + SET_EXPECT(Accessible_child_accNavigate); + SET_EXPECT(Accessible_child_get_accParent); + hr = WindowFromAccessibleObject(&Accessible_child, &hwnd); + ok(hr == S_OK, "got %lx\n", hr); + ok(hwnd == (HWND)0xdeadf00d, "hwnd != 0xdeadf00d!\n"); + CHECK_CALLED(Accessible_child_accNavigate); + CHECK_CALLED(Accessible_child_get_accParent); + + Accessible_accnav_hwnd = NULL; + OleWindow_hwnd = NULL; +} + START_TEST(main) { int argc; @@ -1867,6 +1987,7 @@ START_TEST(main) test_AccessibleObjectFromPoint(); test_CreateStdAccessibleObject_classes(); test_default_edit_accessible_object(); + test_WindowFromAccessibleObject();
unregister_window_class(); CoUninitialize();
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=114467
Your paranoid android.
=== w864 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064v1507 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064v1809 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064_2qxl (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064_adm (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064_tsign (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_en_AE_u8 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_ar (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_ja (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_zh_CN (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
On Wed, May 11, 2022 at 01:53:07PM -0500, Marvin wrote:
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=114467
Your paranoid android.
=== w864 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064v1507 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064v1809 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064_2qxl (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064_adm (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w1064_tsign (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_en_AE_u8 (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_ar (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_ja (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
=== w10pro64_zh_CN (64 bit report) ===
oleacc: main.c:1920: Test failed: hwnd != 0xdeadf00d!
Oof, guess I should've run the 64-bit tests on the testbot before sending this. This patch could also probably be split into two. I'll fix these issues and then resend.