[PATCH] oleacc: Improve WindowFromAccessibleObject implementation.

Connor McAdams cmcadams at codeweavers.com
Wed May 11 13:40:57 CDT 2022


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 at 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();
-- 
2.25.1




More information about the wine-devel mailing list