blob: c7a622beb6cfe3cfd8067330d6cca1a19495c960 [file] [log] [blame]
/*
* 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 <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "ole2.h"
#include "commctrl.h"
#include "initguid.h"
#include "oleacc_private.h"
#include "resource.h"
#include "wine/unicode.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',':'};
static const WCHAR menuW[] = {'#','3','2','7','6','8',0};
static const WCHAR desktopW[] = {'#','3','2','7','6','9',0};
static const WCHAR dialogW[] = {'#','3','2','7','7','0',0};
static const WCHAR winswitchW[] = {'#','3','2','7','7','1',0};
static const WCHAR mdi_clientW[] = {'M','D','I','C','l','i','e','n','t',0};
static const WCHAR richeditW[] = {'R','I','C','H','E','D','I','T',0};
static const WCHAR richedit20aW[] = {'R','i','c','h','E','d','i','t','2','0','A',0};
static const WCHAR richedit20wW[] = {'R','i','c','h','E','d','i','t','2','0','W',0};
typedef HRESULT (WINAPI *accessible_create)(HWND, const IID*, void**);
static struct {
const WCHAR *name;
DWORD idx;
accessible_create create_client;
accessible_create create_window;
} builtin_classes[] = {
{WC_LISTBOXW, 0x10000, NULL, NULL},
{menuW, 0x10001, NULL, NULL},
{WC_BUTTONW, 0x10002, NULL, NULL},
{WC_STATICW, 0x10003, NULL, NULL},
{WC_EDITW, 0x10004, NULL, NULL},
{WC_COMBOBOXW, 0x10005, NULL, NULL},
{dialogW, 0x10006, NULL, NULL},
{winswitchW, 0x10007, NULL, NULL},
{mdi_clientW, 0x10008, NULL, NULL},
{desktopW, 0x10009, NULL, NULL},
{WC_SCROLLBARW, 0x1000a, NULL, NULL},
{STATUSCLASSNAMEW, 0x1000b, NULL, NULL},
{TOOLBARCLASSNAMEW, 0x1000c, NULL, NULL},
{PROGRESS_CLASSW, 0x1000d, NULL, NULL},
{ANIMATE_CLASSW, 0x1000e, NULL, NULL},
{WC_TABCONTROLW, 0x1000f, NULL, NULL},
{HOTKEY_CLASSW, 0x10010, NULL, NULL},
{WC_HEADERW, 0x10011, NULL, NULL},
{TRACKBAR_CLASSW, 0x10012, NULL, NULL},
{WC_LISTVIEWW, 0x10013, NULL, NULL},
{UPDOWN_CLASSW, 0x10016, NULL, NULL},
{TOOLTIPS_CLASSW, 0x10018, NULL, NULL},
{WC_TREEVIEWW, 0x10019, NULL, NULL},
{MONTHCAL_CLASSW, 0, NULL, NULL},
{DATETIMEPICK_CLASSW, 0, NULL, NULL},
{WC_IPADDRESSW, 0, NULL, NULL},
{richeditW, 0x1001c, NULL, NULL},
{richedit20aW, 0, NULL, NULL},
{richedit20wW, 0, NULL, NULL},
};
static HINSTANCE oleacc_handle = 0;
const char *debugstr_variant(const VARIANT *v)
{
if(!v)
return "(null)";
if(V_ISBYREF(v))
return wine_dbg_sprintf("{V_BYREF -> %s}", debugstr_variant(V_BYREF(v)));
switch(V_VT(v)) {
case VT_EMPTY:
return "{VT_EMPTY}";
case VT_NULL:
return "{VT_NULL}";
case VT_I2:
return wine_dbg_sprintf("{VT_I2: %d}", V_I2(v));
case VT_I4:
return wine_dbg_sprintf("{VT_I4: %d}", V_I4(v));
case VT_UI4:
return wine_dbg_sprintf("{VT_UI4: %u}", V_UI4(v));
case VT_R8:
return wine_dbg_sprintf("{VT_R8: %lf}", V_R8(v));
case VT_BSTR:
return wine_dbg_sprintf("{VT_BSTR: %s}", debugstr_w(V_BSTR(v)));
case VT_DISPATCH:
return wine_dbg_sprintf("{VT_DISPATCH: %p}", V_DISPATCH(v));
case VT_BOOL:
return wine_dbg_sprintf("{VT_BOOL: %x}", V_BOOL(v));
default:
return wine_dbg_sprintf("{vt %d}", V_VT(v));
}
}
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;
}
}
static accessible_create get_builtin_accessible_obj(HWND hwnd, LONG objid)
{
WCHAR class_name[64];
int i, idx;
if(!RealGetWindowClassW(hwnd, class_name, sizeof(class_name)/sizeof(WCHAR)))
return NULL;
TRACE("got window class: %s\n", debugstr_w(class_name));
for(i=0; i<sizeof(builtin_classes)/sizeof(builtin_classes[0]); i++) {
if(!strcmpiW(class_name, builtin_classes[i].name)) {
accessible_create ret;
ret = (objid==OBJID_CLIENT ?
builtin_classes[i].create_client :
builtin_classes[i].create_window);
if(!ret)
FIXME("unhandled window class: %s\n", debugstr_w(class_name));
return ret;
}
}
idx = SendMessageW(hwnd, WM_GETOBJECT, 0, OBJID_QUERYCLASSNAMEIDX);
if(idx) {
for(i=0; i<sizeof(builtin_classes)/sizeof(builtin_classes[0]); i++) {
if(idx == builtin_classes[i].idx) {
accessible_create ret;
ret = (objid==OBJID_CLIENT ?
builtin_classes[i].create_client :
builtin_classes[i].create_window);
if(!ret)
FIXME("unhandled class name idx: %x\n", idx);
return ret;
}
}
WARN("unhandled class name idx: %x\n", idx);
}
return NULL;
}
HRESULT WINAPI CreateStdAccessibleObject( HWND hwnd, LONG idObject,
REFIID riidInterface, void** ppvObject )
{
accessible_create create;
TRACE("%p %d %s %p\n", hwnd, idObject,
debugstr_guid( riidInterface ), ppvObject );
switch(idObject) {
case OBJID_CLIENT:
create = get_builtin_accessible_obj(hwnd, idObject);
if(create) return create(hwnd, riidInterface, ppvObject);
return create_client_object(hwnd, riidInterface, ppvObject);
case OBJID_WINDOW:
create = get_builtin_accessible_obj(hwnd, idObject);
if(create) return create(hwnd, riidInterface, ppvObject);
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[sizeof(lresult_atom_prefix)/sizeof(WCHAR)+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, sizeof(atom_str)/sizeof(WCHAR)))
return E_FAIL;
if(memcmp(atom_str, lresult_atom_prefix, sizeof(lresult_atom_prefix)))
return E_FAIL;
p = atom_str + sizeof(lresult_atom_prefix)/sizeof(WCHAR);
proc_id = strtoulW(p, &p, 16);
if(*p != ':')
return E_FAIL;
server_mapping = ULongToHandle( strtoulW(p+1, &p, 16) );
if(*p != ':')
return E_FAIL;
size = strtoulW(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);
memcpy(data, view, size);
UnmapViewOfFile(view);
if(!data)
return E_OUTOFMEMORY;
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 WCHAR atom_fmt[] = {'%','0','8','x',':','%','0','8','x',':','%','0','8','x',0};
static const LARGE_INTEGER seek_zero = {{0}};
WCHAR atom_str[sizeof(lresult_atom_prefix)/sizeof(WCHAR)+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));
sprintfW(atom_str+sizeof(lresult_atom_prefix)/sizeof(WCHAR),
atom_fmt, 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;
}
HRESULT WINAPI AccessibleObjectFromPoint( POINT ptScreen, IAccessible** ppacc, VARIANT* pvarChild )
{
FIXME("{%d,%d} %p %p: stub\n", ptScreen.x, ptScreen.y, ppacc, pvarChild );
return E_NOTIMPL;
}
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 TRUE;
}
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID iid, void **ppv)
{
FIXME("%s %s %p: stub\n", debugstr_guid(rclsid), debugstr_guid(iid), ppv);
return E_NOTIMPL;
}
HRESULT WINAPI DllRegisterServer(void)
{
FIXME("\n");
return S_OK;
}
HRESULT WINAPI DllUnregisterServer(void)
{
FIXME("\n");
return S_OK;
}
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));
}
}