Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- dlls/oleacc/main.c | 52 +++++++++++++++++++++++++++++++++++++++++ dlls/oleacc/oleacc.spec | 2 +- 2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/dlls/oleacc/main.c b/dlls/oleacc/main.c index f6b66a8bcab..db05d9dac92 100644 --- a/dlls/oleacc/main.c +++ b/dlls/oleacc/main.c @@ -331,6 +331,58 @@ HRESULT WINAPI AccessibleObjectFromPoint( POINT ptScreen, IAccessible** ppacc, V return E_NOTIMPL; }
+HRESULT WINAPI AccessibleObjectFromEvent( HWND hwnd, DWORD dwObjectID, DWORD dwChildID, + IAccessible** ppacc, VARIANT* pvarChild ) +{ + IAccessible *iface = NULL; + IDispatch *disp = NULL; + VARIANT vid; + HRESULT hr; + + TRACE("%p %d %d %p %p\n", hwnd, dwObjectID, dwChildID, ppacc, pvarChild); + + hr = AccessibleObjectFromWindow(hwnd, dwObjectID, &IID_IAccessible, (void **)&iface); + if (!SUCCEEDED(hr)) + return hr; + + *ppacc = iface; + VariantInit(&vid); + V_VT(&vid) = VT_I4; + V_I4(&vid) = dwChildID; + + /* + * FIXME: Should also probably handle the possibility that the child isn't + * an object itself. + */ + hr = IAccessible_get_accChild(iface, vid, &disp); + + /* + * If we fail to get a child with the given ChildID, we instead return the + * IAccessible interface we retrieved from AccessibleObjectFromWindow. + */ + if (SUCCEEDED(hr)) + { + hr = IDispatch_QueryInterface(disp, &IID_IAccessible, (void **)ppacc); + if (SUCCEEDED(hr)) + { + V_I4(&vid) = CHILDID_SELF; + IAccessible_Release(iface); + } + else + { + *ppacc = iface; + } + + IDispatch_Release(disp); + } + + VariantInit(pvarChild); + V_VT(pvarChild) = VT_I4; + V_I4(pvarChild) = V_I4(&vid); + + return S_OK; +} + HRESULT WINAPI AccessibleObjectFromWindow( HWND hwnd, DWORD dwObjectID, REFIID riid, void** ppvObject ) { diff --git a/dlls/oleacc/oleacc.spec b/dlls/oleacc/oleacc.spec index 4cff0b8effc..9ad6e915965 100644 --- a/dlls/oleacc/oleacc.spec +++ b/dlls/oleacc/oleacc.spec @@ -1,5 +1,5 @@ @ stdcall AccessibleChildren(ptr long long ptr ptr) -@ stub AccessibleObjectFromEvent +@ stdcall AccessibleObjectFromEvent(ptr long long ptr ptr) @ stdcall AccessibleObjectFromPoint(int64 ptr ptr) @ stdcall AccessibleObjectFromWindow(ptr long ptr ptr) @ stdcall CreateStdAccessibleObject(ptr long ptr ptr)
Signed-off-by: Connor McAdams cmcadams@codeweavers.com --- -This may need more tests, I'm struggling to think up more. --- dlls/oleacc/tests/main.c | 620 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 620 insertions(+)
diff --git a/dlls/oleacc/tests/main.c b/dlls/oleacc/tests/main.c index ce44e06abe4..7f8211cd004 100644 --- a/dlls/oleacc/tests/main.c +++ b/dlls/oleacc/tests/main.c @@ -304,6 +304,567 @@ static IAccessibleVtbl AccessibleVtbl = {
static IAccessible Accessible = {&AccessibleVtbl};
+/* + * Create a tree of objects that looks like this: + * --ACC_OBJ_ID_MAIN, + * ----ACC_OBJ_ID_CHILD_0, + * ----ACC_OBJ_ID_CHILD_1, + * ------ACC_OBJ_ID_CHILD_1_0, + * ------ACC_OBJ_ID_CHILD_1_1, + * --------ACC_OBJ_ID_CHILD_1_1_0 + * ------ACC_OBJ_ID_CHILD_1_2, + * ----ACC_OBJ_ID_CHILD_2, + * ------ACC_OBJ_ID_CHILD_2_0, + */ +enum { + ACC_OBJ_ID_NONE = -1, + ACC_OBJ_ID_MAIN = 1, + ACC_OBJ_ID_CHILD_0, + ACC_OBJ_ID_CHILD_1, + ACC_OBJ_ID_CHILD_1_0, + ACC_OBJ_ID_CHILD_1_1, + ACC_OBJ_ID_CHILD_1_1_0, + ACC_OBJ_ID_CHILD_1_2, + ACC_OBJ_ID_CHILD_2, + ACC_OBJ_ID_CHILD_2_0, +}; + +/* + * Custom accesibility tree object info structure. + */ +typedef struct { + const WCHAR *obj_name; + + int parent_id, child_id, child_pos; + unsigned int child_count, role; +} acc_object_info; + +typedef struct { + IAccessible IAccessible_iface; + IEnumVARIANT IEnumVARIANT_iface; + + LONG ref; + + BSTR name; + HWND hwnd; + UINT child_id, role, enum_pos; + + IAccessible *parent; + + UINT child_count; + IAccessible **children; +} Server; + +Server *server_obj_tree = NULL; + +const acc_object_info obj_tree_info[] = { + { .obj_name = L"acc_main", + .parent_id = ACC_OBJ_ID_NONE, + .child_id = ACC_OBJ_ID_MAIN, + .child_pos = 0, + .child_count = 3, + .role = ROLE_SYSTEM_DIALOG, + }, + { .obj_name = L"acc_child_0", + .parent_id = ACC_OBJ_ID_MAIN, + .child_id = ACC_OBJ_ID_CHILD_0, + .child_pos = 0, + .child_count = 0, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, + { .obj_name = L"acc_child_1", + .parent_id = ACC_OBJ_ID_MAIN, + .child_id = ACC_OBJ_ID_CHILD_1, + .child_pos = 1, + .child_count = 3, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, + { .obj_name = L"acc_child_1_0", + .parent_id = ACC_OBJ_ID_CHILD_1, + .child_id = ACC_OBJ_ID_CHILD_1_0, + .child_pos = 0, + .child_count = 0, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, + { .obj_name = L"acc_child_1_1", + .parent_id = ACC_OBJ_ID_CHILD_1, + .child_id = ACC_OBJ_ID_CHILD_1_1, + .child_pos = 1, + .child_count = 1, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, + { .obj_name = L"acc_child_1_1_0", + .parent_id = ACC_OBJ_ID_CHILD_1_1, + .child_id = ACC_OBJ_ID_CHILD_1_1_0, + .child_pos = 0, + .child_count = 0, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, + { .obj_name = L"acc_child_1_2", + .parent_id = ACC_OBJ_ID_CHILD_1, + .child_id = ACC_OBJ_ID_CHILD_1_2, + .child_pos = 2, + .child_count = 0, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, + { .obj_name = L"acc_child_2", + .parent_id = ACC_OBJ_ID_MAIN, + .child_id = ACC_OBJ_ID_CHILD_2, + .child_pos = 2, + .child_count = 1, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, + { .obj_name = L"acc_child_2_0", + .parent_id = ACC_OBJ_ID_CHILD_2, + .child_id = ACC_OBJ_ID_CHILD_2_0, + .child_pos = 0, + .child_count = 0, + .role = ROLE_SYSTEM_CHECKBUTTON, + }, +}; + +/* + * Custom server IAccessible. + */ +static inline Server* impl_from_Server(IAccessible *iface) +{ + return CONTAINING_RECORD(iface, Server, IAccessible_iface); +} + +static void find_accessible(IAccessible *iface, IAccessible **found, UINT child_id) +{ + Server *data = impl_from_Server(iface); + LONG i; + + for (i = 0; i < data->child_count; i++) + { + Server *child = impl_from_Server(data->children[i]); + + if (child->child_id == child_id) + { + *found = &child->IAccessible_iface; + return; + } + + if (child->child_count) + find_accessible(&child->IAccessible_iface, found, child_id); + + if (*found) + return; + } +} + +HRESULT WINAPI Server_QueryInterface(IAccessible *iface, REFIID riid, void **ppv) +{ + Server *This = impl_from_Server(iface); + + if(IsEqualIID(riid, &IID_IAccessible) || + IsEqualIID(riid, &IID_IDispatch) || + IsEqualIID(riid, &IID_IUnknown)) { + *ppv = iface; + }else if(IsEqualIID(riid, &IID_IEnumVARIANT)) { + *ppv = &This->IEnumVARIANT_iface; + }else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IAccessible_AddRef(iface); + return S_OK; +} + +ULONG WINAPI Server_AddRef(IAccessible *iface) +{ + Server *This = impl_from_Server(iface); + return InterlockedIncrement(&This->ref); +} + +ULONG WINAPI Server_Release(IAccessible *iface) +{ + Server *This = impl_from_Server(iface); + wprintf(L"%S: iface %p, This->ref %d\n", __func__, iface, This->ref); + return InterlockedDecrement(&This->ref); +} + +HRESULT WINAPI Server_GetTypeInfoCount( + IAccessible *iface, UINT *pctinfo) +{ + *pctinfo = 0; + + return S_OK; +} + +HRESULT WINAPI Server_GetTypeInfo(IAccessible *iface, + UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + *ppTInfo = NULL; + + return S_OK; +} + +HRESULT WINAPI Server_GetIDsOfNames(IAccessible *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_Invoke(IAccessible *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + return S_OK; +} + +HRESULT WINAPI Server_get_accParent( + IAccessible *iface, IDispatch **ppdispParent) +{ + Server *This = impl_from_Server(iface); + + if (This->parent) + { + Server_QueryInterface(This->parent, &IID_IDispatch, (void **)ppdispParent); + return S_OK; + } + + *ppdispParent = NULL; + + return S_FALSE; +} + +HRESULT WINAPI Server_get_accChildCount( + IAccessible *iface, LONG *pcountChildren) +{ + Server *This = impl_from_Server(iface); + + *pcountChildren = This->child_count; + + return S_OK; +} + +HRESULT WINAPI Server_get_accChild(IAccessible *iface, + VARIANT varChildID, IDispatch **ppdispChild) +{ + IAccessible *child = NULL; + + if (V_VT(&varChildID) != VT_I4) + return E_FAIL; + + if (V_I4(&varChildID) == CHILDID_SELF) + child = iface; + else + find_accessible(iface, &child, V_I4(&varChildID)); + + if (child) + { + Server_QueryInterface(child, &IID_IDispatch, (void **)ppdispChild); + return S_OK; + } + + return E_FAIL; +} + +HRESULT WINAPI Server_get_accName(IAccessible *iface, + VARIANT varID, BSTR *pszName) +{ + Server *This = impl_from_Server(iface); + + *pszName = This->name; + + return S_OK; +} + +HRESULT WINAPI Server_get_accValue(IAccessible *iface, + VARIANT varID, BSTR *pszValue) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accDescription(IAccessible *iface, + VARIANT varID, BSTR *pszDescription) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accRole(IAccessible *iface, + VARIANT varID, VARIANT *pvarRole) +{ + Server *This = impl_from_Server(iface); + + if ((V_VT(&varID) == VT_I4) && (V_I4(&varID) == CHILDID_SELF)) + { + V_VT(pvarRole) = VT_I4; + V_I4(pvarRole) = This->role; + + return S_OK; + } + + return E_INVALIDARG; +} + +HRESULT WINAPI Server_get_accState(IAccessible *iface, + VARIANT varID, VARIANT *pvarState) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accHelp(IAccessible *iface, + VARIANT varID, BSTR *pszHelp) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accHelpTopic(IAccessible *iface, + BSTR *pszHelpFile, VARIANT varID, LONG *pidTopic) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accKeyboardShortcut(IAccessible *iface, + VARIANT varID, BSTR *pszKeyboardShortcut) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accFocus(IAccessible *iface, VARIANT *pvarID) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accSelection( + IAccessible *iface, VARIANT *pvarID) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_get_accDefaultAction(IAccessible *iface, + VARIANT varID, BSTR *pszDefaultAction) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_accSelect(IAccessible *iface, + LONG flagsSelect, VARIANT varID) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_accLocation(IAccessible *iface, LONG *pxLeft, + LONG *pyTop, LONG *pcxWidth, LONG *pcyHeight, VARIANT varID) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_accNavigate(IAccessible *iface, + LONG navDir, VARIANT varStart, VARIANT *pvarEnd) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_accHitTest(IAccessible *iface, + LONG xLeft, LONG yTop, VARIANT *pvarID) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_accDoDefaultAction( + IAccessible *iface, VARIANT varID) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_put_accName(IAccessible *iface, + VARIANT varID, BSTR pszName) +{ + return E_NOTIMPL; +} + +HRESULT WINAPI Server_put_accValue(IAccessible *iface, + VARIANT varID, BSTR pszValue) +{ + return E_NOTIMPL; +} + +IAccessibleVtbl ServerAccessibleVtbl = { + Server_QueryInterface, + Server_AddRef, + Server_Release, + Server_GetTypeInfoCount, + Server_GetTypeInfo, + Server_GetIDsOfNames, + Server_Invoke, + Server_get_accParent, + Server_get_accChildCount, + Server_get_accChild, + Server_get_accName, + Server_get_accValue, + Server_get_accDescription, + Server_get_accRole, + Server_get_accState, + Server_get_accHelp, + Server_get_accHelpTopic, + Server_get_accKeyboardShortcut, + Server_get_accFocus, + Server_get_accSelection, + Server_get_accDefaultAction, + Server_accSelect, + Server_accLocation, + Server_accNavigate, + Server_accHitTest, + Server_accDoDefaultAction, + Server_put_accName, + Server_put_accValue +}; + +/* + * Server enumVARIANT Vtbl. + */ +inline Server* impl_from_Server_EnumVARIANT(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, Server, IEnumVARIANT_iface); +} + +HRESULT WINAPI Server_EnumVARIANT_QueryInterface(IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + Server *This = impl_from_Server_EnumVARIANT(iface); + return IAccessible_QueryInterface(&This->IAccessible_iface, riid, ppv); +} + +ULONG WINAPI Server_EnumVARIANT_AddRef(IEnumVARIANT *iface) +{ + Server *This = impl_from_Server_EnumVARIANT(iface); + return IAccessible_AddRef(&This->IAccessible_iface); +} + +ULONG WINAPI Server_EnumVARIANT_Release(IEnumVARIANT *iface) +{ + Server *This = impl_from_Server_EnumVARIANT(iface); + return IAccessible_Release(&This->IAccessible_iface); +} + +HRESULT WINAPI Server_EnumVARIANT_Next(IEnumVARIANT *iface, + ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + Server *This = impl_from_Server_EnumVARIANT(iface); + ULONG fetched = 0; + UINT i; + + for (i = This->enum_pos; i < This->child_count; i++) + { + Server *child = impl_from_Server(This->children[i]); + + V_VT(&rgVar[i]) = VT_I4; + V_I4(&rgVar[i]) = child->child_id; + fetched++; + } + + *pCeltFetched = fetched; + + return celt == fetched ? S_OK : S_FALSE; +} + +HRESULT WINAPI Server_EnumVARIANT_Skip(IEnumVARIANT *iface, ULONG celt) +{ + Server *This = impl_from_Server_EnumVARIANT(iface); + + if ((celt + This->enum_pos) < This->child_count) + { + This->enum_pos += celt; + return S_OK; + } + + return S_FALSE; +} + +HRESULT WINAPI Server_EnumVARIANT_Reset(IEnumVARIANT *iface) +{ + Server *This = impl_from_Server_EnumVARIANT(iface); + + This->enum_pos = 0; + + return S_OK; +} + +HRESULT WINAPI Server_EnumVARIANT_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + return E_NOTIMPL; +} + +const IEnumVARIANTVtbl ServerEnumVARIANTVtbl = { + Server_EnumVARIANT_QueryInterface, + Server_EnumVARIANT_AddRef, + Server_EnumVARIANT_Release, + Server_EnumVARIANT_Next, + Server_EnumVARIANT_Skip, + Server_EnumVARIANT_Reset, + Server_EnumVARIANT_Clone +}; + +static HRESULT create_server_obj_tree(Server **tree, HWND hwnd) +{ + Server *acc_objs; + unsigned int i; + + if (!IsWindow(hwnd)) + return E_FAIL; + + acc_objs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(*acc_objs) * ARRAY_SIZE(obj_tree_info)); + if (!acc_objs) + return E_OUTOFMEMORY; + + for (i = 0; i < ARRAY_SIZE(obj_tree_info); i++) + { + const acc_object_info *obj_info = &obj_tree_info[i]; + Server *obj = &acc_objs[i]; + + obj->IAccessible_iface.lpVtbl = &ServerAccessibleVtbl; + obj->IEnumVARIANT_iface.lpVtbl = &ServerEnumVARIANTVtbl; + obj->ref = 1; + obj->hwnd = hwnd; + obj->name = SysAllocString(obj_info->obj_name); + if (!obj->name && obj_info->obj_name) + return E_OUTOFMEMORY; + obj->child_id = obj_info->child_id; + obj->role = obj_info->role; + obj->child_count = obj_info->child_count; + if (obj->child_count) + { + obj->children = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(*obj->children) * obj->child_count); + if (!obj->children) + return E_OUTOFMEMORY; + } + + if (obj_info->parent_id >= 0) + { + obj->parent = &acc_objs[obj_info->parent_id - 1].IAccessible_iface; + acc_objs[obj_info->parent_id - 1].children[obj_info->child_pos] = &obj->IAccessible_iface; + } + } + + *tree = acc_objs; + + return S_OK; +} + +static void free_server_obj_tree(Server *tree) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(obj_tree_info); i++) + { + Server *obj = &tree[i]; + + if (obj->name) + SysFreeString(obj->name); + if (obj->children) + HeapFree(GetProcessHeap(), 0, obj->children); + IAccessible_Release(&obj->IAccessible_iface); + } + + HeapFree(GetProcessHeap(), 0, tree); +} + static void test_getroletext(void) { INT ret, role; @@ -564,7 +1125,13 @@ static LRESULT WINAPI test_window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARA if(lparam == (DWORD)OBJID_CURSOR) return E_UNEXPECTED; if(lparam == (DWORD)OBJID_CLIENT) + { + if (server_obj_tree) + return LresultFromObject(&IID_IAccessible, wparam, + (IUnknown *)&server_obj_tree->IAccessible_iface); + return LresultFromObject(&IID_IUnknown, wparam, &Object); + } if(lparam == (DWORD)OBJID_WINDOW) return 0;
@@ -621,6 +1188,58 @@ static void test_AccessibleObjectFromWindow(void) DestroyWindow(hwnd); }
+static void test_AccessibleObjectFromEvent(void) +{ + IAccessible *acc = NULL; + BSTR name = NULL; + VARIANT var; + HRESULT hr; + HWND hwnd; + + hwnd = CreateWindowA("oleacc_test", "test", WS_OVERLAPPEDWINDOW, + 0, 0, 0, 0, NULL, NULL, NULL, NULL); + ok(hwnd != NULL, "CreateWindow failed\n"); + + hr = create_server_obj_tree(&server_obj_tree, hwnd); + ok(SUCCEEDED(hr), "got %#x\n", hr); + + hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, CHILDID_SELF, &acc, &var); + ok(SUCCEEDED(hr), "got %#x\n", hr); + + hr = IAccessible_get_accName(acc, var, &name); + ok(SUCCEEDED(hr), "got %#x\n", hr); + ok(!lstrcmpW(name, L"acc_main"), "name = %s\n", wine_dbgstr_w(name)); + IAccessible_Release(acc); + + acc = NULL; + hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_OBJ_ID_CHILD_1_1_0, &acc, &var); + ok(SUCCEEDED(hr), "got %#x\n", hr); + ok(V_VT(&var) == VT_I4, "got %#x, expected %#x\n", V_VT(&var), VT_I4); + ok(V_I4(&var) == CHILDID_SELF, "got %#x, expected %#x\n", V_I4(&var), CHILDID_SELF); + + hr = IAccessible_get_accName(acc, var, &name); + ok(SUCCEEDED(hr), "got %#x\n", hr); + ok(!lstrcmpW(name, L"acc_child_1_1_0"), "name = %s\n", wine_dbgstr_w(name)); + IAccessible_Release(acc); + + /* + * Invalid Child ID, in this case we just get the same result as + * AccessibleObjectFromWindow. + */ + acc = NULL; + hr = AccessibleObjectFromEvent(hwnd, OBJID_CLIENT, ACC_OBJ_ID_CHILD_2_0 + 1, &acc, &var); + ok(SUCCEEDED(hr), "got %#x\n", hr); + + hr = IAccessible_get_accName(acc, var, &name); + ok(SUCCEEDED(hr), "got %#x\n", hr); + ok(!lstrcmpW(name, L"acc_main"), "name = %s\n", wine_dbgstr_w(name)); + IAccessible_Release(acc); + + DestroyWindow(hwnd); + free_server_obj_tree(server_obj_tree); + server_obj_tree = NULL; +} + static void test_GetProcessHandleFromHwnd(void) { HANDLE proc; @@ -1061,6 +1680,7 @@ START_TEST(main) test_GetProcessHandleFromHwnd(); test_default_client_accessible_object(); test_AccessibleChildren(&Accessible); + test_AccessibleObjectFromEvent();
unregister_window_class(); CoUninitialize();