/* * Implementation of the OLEACC dll * * Copyright 2003 Mike McCormack for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #include "initguid.h" #include "oleacc_private.h" #include "resource.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(oleacc); static const WCHAR lresult_atom_prefix[] = {'w','i','n','e','_','o','l','e','a','c','c',':'}; extern HRESULT WINAPI OLEACC_DllGetClassObject(REFCLSID, REFIID, void**) DECLSPEC_HIDDEN; extern BOOL WINAPI OLEACC_DllMain(HINSTANCE, DWORD, void*) DECLSPEC_HIDDEN; extern HRESULT WINAPI OLEACC_DllRegisterServer(void) DECLSPEC_HIDDEN; extern HRESULT WINAPI OLEACC_DllUnregisterServer(void) DECLSPEC_HIDDEN; static HINSTANCE oleacc_handle = 0; int convert_child_id(VARIANT *v) { switch(V_VT(v)) { case VT_I4: return V_I4(v); default: FIXME("unhandled child ID variant type: %d\n", V_VT(v)); return -1; } } const struct win_class_data* find_class_data(HWND hwnd, const struct win_class_data *classes) { WCHAR class_name[64]; int i, idx; if(!RealGetWindowClassW(hwnd, class_name, ARRAY_SIZE(class_name))) return NULL; TRACE("got window class: %s\n", debugstr_w(class_name)); for(i=0; classes[i].name; i++) { if(!wcsicmp(class_name, classes[i].name)) { if(classes[i].stub) FIXME("unhandled window class: %s\n", debugstr_w(class_name)); return &classes[i]; } } idx = SendMessageW(hwnd, WM_GETOBJECT, 0, OBJID_QUERYCLASSNAMEIDX); if(idx) { for(i=0; classes[i].name; i++) { if(idx == classes[i].idx) { if(classes[i].stub) FIXME("unhandled window class: %s\n", debugstr_w(class_name)); return &classes[i]; } } WARN("unhandled class name idx: %x\n", idx); } return NULL; } HRESULT WINAPI CreateStdAccessibleObject( HWND hwnd, LONG idObject, REFIID riidInterface, void** ppvObject ) { TRACE("%p %d %s %p\n", hwnd, idObject, debugstr_guid( riidInterface ), ppvObject ); switch(idObject) { case OBJID_CLIENT: return create_client_object(hwnd, riidInterface, ppvObject); case OBJID_WINDOW: return create_window_object(hwnd, riidInterface, ppvObject); default: FIXME("unhandled object id: %d\n", idObject); return E_NOTIMPL; } } HRESULT WINAPI ObjectFromLresult( LRESULT result, REFIID riid, WPARAM wParam, void **ppObject ) { WCHAR atom_str[ARRAY_SIZE(lresult_atom_prefix)+3*8+3]; HANDLE server_proc, server_mapping, mapping; DWORD proc_id, size; IStream *stream; HGLOBAL data; void *view; HRESULT hr; WCHAR *p; TRACE("%ld %s %ld %p\n", result, debugstr_guid(riid), wParam, ppObject ); if(wParam) FIXME("unsupported wParam = %lx\n", wParam); if(!ppObject) return E_INVALIDARG; *ppObject = NULL; if(result != (ATOM)result) return E_FAIL; if(!GlobalGetAtomNameW(result, atom_str, ARRAY_SIZE(atom_str))) return E_FAIL; if(memcmp(atom_str, lresult_atom_prefix, sizeof(lresult_atom_prefix))) return E_FAIL; p = atom_str + ARRAY_SIZE(lresult_atom_prefix); proc_id = wcstoul(p, &p, 16); if(*p != ':') return E_FAIL; server_mapping = ULongToHandle( wcstoul(p+1, &p, 16) ); if(*p != ':') return E_FAIL; size = wcstoul(p+1, &p, 16); if(*p != 0) return E_FAIL; server_proc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, proc_id); if(!server_proc) return E_FAIL; if(!DuplicateHandle(server_proc, server_mapping, GetCurrentProcess(), &mapping, 0, FALSE, DUPLICATE_CLOSE_SOURCE|DUPLICATE_SAME_ACCESS)) return E_FAIL; CloseHandle(server_proc); GlobalDeleteAtom(result); view = MapViewOfFile(mapping, FILE_MAP_READ, 0, 0, 0); CloseHandle(mapping); if(!view) return E_FAIL; data = GlobalAlloc(GMEM_FIXED, size); if(!data) { UnmapViewOfFile(view); return E_OUTOFMEMORY; } memcpy(data, view, size); UnmapViewOfFile(view); hr = CreateStreamOnHGlobal(data, TRUE, &stream); if(FAILED(hr)) { GlobalFree(data); return hr; } hr = CoUnmarshalInterface(stream, riid, ppObject); IStream_Release(stream); return hr; } LRESULT WINAPI LresultFromObject( REFIID riid, WPARAM wParam, LPUNKNOWN pAcc ) { static const LARGE_INTEGER seek_zero = {{0}}; WCHAR atom_str[ARRAY_SIZE(lresult_atom_prefix)+3*8+3]; IStream *stream; HANDLE mapping; STATSTG stat; HRESULT hr; ATOM atom; void *view; TRACE("%s %ld %p\n", debugstr_guid(riid), wParam, pAcc); if(wParam) FIXME("unsupported wParam = %lx\n", wParam); if(!pAcc) return E_INVALIDARG; hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); if(FAILED(hr)) return hr; hr = CoMarshalInterface(stream, riid, pAcc, MSHCTX_LOCAL, NULL, MSHLFLAGS_NORMAL); if(FAILED(hr)) { IStream_Release(stream); return hr; } hr = IStream_Seek(stream, seek_zero, STREAM_SEEK_SET, NULL); if(FAILED(hr)) { IStream_Release(stream); return hr; } hr = IStream_Stat(stream, &stat, STATFLAG_NONAME); if(FAILED(hr)) { CoReleaseMarshalData(stream); IStream_Release(stream); return hr; }else if(stat.cbSize.u.HighPart) { FIXME("stream size to big\n"); CoReleaseMarshalData(stream); IStream_Release(stream); return E_NOTIMPL; } mapping = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, stat.cbSize.u.HighPart, stat.cbSize.u.LowPart, NULL); if(!mapping) { CoReleaseMarshalData(stream); IStream_Release(stream); return hr; } view = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0); if(!view) { CloseHandle(mapping); CoReleaseMarshalData(stream); IStream_Release(stream); return E_FAIL; } hr = IStream_Read(stream, view, stat.cbSize.u.LowPart, NULL); UnmapViewOfFile(view); if(FAILED(hr)) { CloseHandle(mapping); hr = IStream_Seek(stream, seek_zero, STREAM_SEEK_SET, NULL); if(SUCCEEDED(hr)) CoReleaseMarshalData(stream); IStream_Release(stream); return hr; } memcpy(atom_str, lresult_atom_prefix, sizeof(lresult_atom_prefix)); swprintf(atom_str+ARRAY_SIZE(lresult_atom_prefix), 3*8 + 3, L"%08x:%08x:%08x", GetCurrentProcessId(), HandleToUlong(mapping), stat.cbSize.u.LowPart); atom = GlobalAddAtomW(atom_str); if(!atom) { CloseHandle(mapping); hr = IStream_Seek(stream, seek_zero, STREAM_SEEK_SET, NULL); if(SUCCEEDED(hr)) CoReleaseMarshalData(stream); IStream_Release(stream); return E_FAIL; } IStream_Release(stream); return atom; } static void variant_init_i4( VARIANT *v, int val ) { V_VT(v) = VT_I4; V_I4(v) = val; } HRESULT WINAPI AccessibleObjectFromPoint( POINT point, IAccessible** acc, VARIANT* child_id ) { IAccessible *cur; HRESULT hr; VARIANT v; HWND hwnd; TRACE("{%d,%d} %p %p\n", point.x, point.y, acc, child_id); if (!acc || !child_id) return E_INVALIDARG; *acc = NULL; V_VT(child_id) = VT_EMPTY; hwnd = WindowFromPoint(point); if (!hwnd) return E_FAIL; hwnd = GetAncestor(hwnd, GA_ROOT); hr = AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, &IID_IAccessible, (void **)&cur); if (FAILED(hr)) return hr; if (!cur) return E_FAIL; V_VT(&v) = VT_EMPTY; while (1) { hr = IAccessible_accHitTest(cur, point.x, point.y, &v); if (FAILED(hr)) { IAccessible_Release(cur); return hr; } if (V_VT(&v) == VT_I4) { *acc = cur; variant_init_i4(child_id, V_I4(&v)); return S_OK; } else if (V_VT(&v) == VT_DISPATCH) { IAccessible_Release(cur); hr = IDispatch_QueryInterface(V_DISPATCH(&v), &IID_IAccessible, (void**)&cur); VariantClear(&v); if (FAILED(hr)) return hr; if (!cur) return E_FAIL; } else { VariantClear(&v); IAccessible_Release(cur); FIXME("unhandled variant type: %d\n", V_VT(&v)); return E_NOTIMPL; } } return S_OK; } HRESULT WINAPI AccessibleObjectFromEvent( HWND hwnd, DWORD object_id, DWORD child_id, IAccessible **acc_out, VARIANT *child_id_out ) { VARIANT child_id_variant; IAccessible *acc = NULL; IDispatch *child = NULL; HRESULT hr; TRACE("%p %d %d %p %p\n", hwnd, object_id, child_id, acc_out, child_id_out); if (!acc_out) return E_INVALIDARG; *acc_out = NULL; VariantInit(child_id_out); hr = AccessibleObjectFromWindow(hwnd, object_id, &IID_IAccessible, (void **)&acc); if (FAILED(hr)) return hr; variant_init_i4(&child_id_variant, child_id); hr = IAccessible_get_accChild(acc, child_id_variant, &child); if (FAILED(hr)) TRACE("get_accChild failed with %#x!\n", hr); if (SUCCEEDED(hr) && child) { IAccessible_Release(acc); hr = IDispatch_QueryInterface(child, &IID_IAccessible, (void **)&acc); IDispatch_Release(child); if (FAILED(hr)) return hr; variant_init_i4(&child_id_variant, CHILDID_SELF); } *acc_out = acc; *child_id_out = child_id_variant; return S_OK; } HRESULT WINAPI AccessibleObjectFromWindow( HWND hwnd, DWORD dwObjectID, REFIID riid, void** ppvObject ) { TRACE("%p %d %s %p\n", hwnd, dwObjectID, debugstr_guid( riid ), ppvObject ); if(!ppvObject) return E_INVALIDARG; *ppvObject = NULL; if(IsWindow(hwnd)) { LRESULT lres; lres = SendMessageW(hwnd, WM_GETOBJECT, 0xffffffff, dwObjectID); if(FAILED(lres)) return lres; else if(lres) return ObjectFromLresult(lres, riid, 0, ppvObject); } return CreateStdAccessibleObject(hwnd, dwObjectID, riid, ppvObject); } HRESULT WINAPI WindowFromAccessibleObject(IAccessible *acc, HWND *phwnd) { IDispatch *parent; IOleWindow *ow; 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); IAccessible_Release(acc); return hres; } hres = IAccessible_get_accParent(acc, &parent); IAccessible_Release(acc); if(FAILED(hres)) return hres; if(hres!=S_OK || !parent) { *phwnd = NULL; return hres; } hres = IDispatch_QueryInterface(parent, &IID_IAccessible, (void**)&acc); IDispatch_Release(parent); if(FAILED(hres)) return hres; } } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { TRACE("%p, %d, %p\n", hinstDLL, fdwReason, lpvReserved); switch (fdwReason) { case DLL_PROCESS_ATTACH: oleacc_handle = hinstDLL; DisableThreadLibraryCalls(hinstDLL); break; } return OLEACC_DllMain(hinstDLL, fdwReason, lpvReserved); } HRESULT WINAPI DllRegisterServer(void) { return OLEACC_DllRegisterServer(); } HRESULT WINAPI DllUnregisterServer(void) { return OLEACC_DllUnregisterServer(); } HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID iid, void **ppv) { if(IsEqualGUID(&CLSID_CAccPropServices, rclsid)) { TRACE("(CLSID_CAccPropServices %s %p)\n", debugstr_guid(iid), ppv); return get_accpropservices_factory(iid, ppv); } if(IsEqualGUID(&CLSID_PSFactoryBuffer, rclsid)) { TRACE("(CLSID_PSFactoryBuffer %s %p)\n", debugstr_guid(iid), ppv); return OLEACC_DllGetClassObject(rclsid, iid, ppv); } FIXME("%s %s %p: stub\n", debugstr_guid(rclsid), debugstr_guid(iid), ppv); return E_NOTIMPL; } void WINAPI GetOleaccVersionInfo(DWORD* pVersion, DWORD* pBuild) { *pVersion = MAKELONG(0,7); /* Windows 7 version of oleacc: 7.0.0.0 */ *pBuild = MAKELONG(0,0); } HANDLE WINAPI GetProcessHandleFromHwnd(HWND hwnd) { DWORD proc_id; TRACE("%p\n", hwnd); if(!GetWindowThreadProcessId(hwnd, &proc_id)) return NULL; return OpenProcess(PROCESS_DUP_HANDLE | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | SYNCHRONIZE, TRUE, proc_id); } UINT WINAPI GetRoleTextW(DWORD role, LPWSTR lpRole, UINT rolemax) { INT ret; WCHAR *resptr; TRACE("%u %p %u\n", role, lpRole, rolemax); /* return role text length */ if(!lpRole) return LoadStringW(oleacc_handle, role, (LPWSTR)&resptr, 0); ret = LoadStringW(oleacc_handle, role, lpRole, rolemax); if(!(ret > 0)){ if(rolemax > 0) lpRole[0] = '\0'; return 0; } return ret; } UINT WINAPI GetRoleTextA(DWORD role, LPSTR lpRole, UINT rolemax) { UINT length; WCHAR *roletextW; TRACE("%u %p %u\n", role, lpRole, rolemax); if(lpRole && !rolemax) return 0; length = GetRoleTextW(role, NULL, 0); if(!length) { if(lpRole && rolemax) lpRole[0] = 0; return 0; } roletextW = HeapAlloc(GetProcessHeap(), 0, (length + 1)*sizeof(WCHAR)); if(!roletextW) return 0; GetRoleTextW(role, roletextW, length + 1); length = WideCharToMultiByte( CP_ACP, 0, roletextW, -1, NULL, 0, NULL, NULL ); if(!lpRole){ HeapFree(GetProcessHeap(), 0, roletextW); return length - 1; } if(rolemax < length) { HeapFree(GetProcessHeap(), 0, roletextW); lpRole[0] = 0; return 0; } WideCharToMultiByte( CP_ACP, 0, roletextW, -1, lpRole, rolemax, NULL, NULL ); if(rolemax < length){ lpRole[rolemax-1] = '\0'; length = rolemax; } HeapFree(GetProcessHeap(), 0, roletextW); return length - 1; } UINT WINAPI GetStateTextW(DWORD state_bit, WCHAR *state_str, UINT state_str_len) { DWORD state_id; TRACE("%x %p %u\n", state_bit, state_str, state_str_len); if(state_bit & ~(STATE_SYSTEM_VALID | STATE_SYSTEM_HASPOPUP)) { if(state_str && state_str_len) state_str[0] = 0; return 0; } state_id = IDS_STATE_NORMAL; while(state_bit) { state_id++; state_bit /= 2; } if(state_str) { UINT ret = LoadStringW(oleacc_handle, state_id, state_str, state_str_len); if(!ret && state_str_len) state_str[0] = 0; return ret; }else { WCHAR *tmp; return LoadStringW(oleacc_handle, state_id, (WCHAR*)&tmp, 0); } } UINT WINAPI GetStateTextA(DWORD state_bit, CHAR *state_str, UINT state_str_len) { DWORD state_id; TRACE("%x %p %u\n", state_bit, state_str, state_str_len); if(state_str && !state_str_len) return 0; if(state_bit & ~(STATE_SYSTEM_VALID | STATE_SYSTEM_HASPOPUP)) { if(state_str && state_str_len) state_str[0] = 0; return 0; } state_id = IDS_STATE_NORMAL; while(state_bit) { state_id++; state_bit /= 2; } if(state_str) { UINT ret = LoadStringA(oleacc_handle, state_id, state_str, state_str_len); if(!ret && state_str_len) state_str[0] = 0; return ret; }else { CHAR tmp[256]; return LoadStringA(oleacc_handle, state_id, tmp, sizeof(tmp)); } } HRESULT WINAPI AccessibleChildren(IAccessible *container, LONG start, LONG count, VARIANT *children, LONG *children_cnt) { IEnumVARIANT *ev; LONG i, child_no; HRESULT hr; TRACE("%p %d %d %p %p\n", container, start, count, children, children_cnt); if(!container || !children || !children_cnt) return E_INVALIDARG; for(i=0; i