429 lines
14 KiB
C++
429 lines
14 KiB
C++
// -------
|
|
|
|
// Mag_Hook.cpp
|
|
|
|
// Accessibility Event trapper for ScreenX. Uses an in-context WinEvent
|
|
// hook to tell the applet where to magnify.
|
|
|
|
// Mainly what we want this to do is to watch for focus changes, caret
|
|
// movement, and mouse pointer movement, and then post the appropriate
|
|
// location to the ScreenX applet so it can magnify the correct area.
|
|
|
|
// -------
|
|
#define STRICT
|
|
|
|
#include <windows.h>
|
|
#include <windowsx.h>
|
|
|
|
// When building with VC5, we need winable.h since the active
|
|
// accessibility structures are not in VC5's winuser.h. winable.h can
|
|
// be found in the active accessibility SDK
|
|
#ifdef VC5_BUILD___NOT_NT_BUILD_ENVIRONMENT
|
|
#include <winable.h>
|
|
#else
|
|
// The Active Accessibility SDK used WINABLEAPI for the functions. When
|
|
// the functions were moved to winuser.h, WINABLEAPI was replaced with WINUSERAPI.
|
|
#define WINABLEAPI WINUSERAPI
|
|
#endif
|
|
|
|
#include <ole2.h>
|
|
#include <oleacc.h>
|
|
#define MAGHOOKAPI __declspec(dllexport)
|
|
#include "Mag_Hook.h"
|
|
|
|
|
|
// -------
|
|
|
|
// Definitions so we don't have to statically link to OLEACC.DLL
|
|
|
|
// We need the following three functions that were in the Active Accessibility SDK
|
|
|
|
// STDAPI AccessibleObjectFromEvent(HWND hwnd, DWORD dwId, DWORD dwChildId, IAccessible** ppacc, VARIANT* pvarChild);
|
|
// WINABLEAPI HWINEVENTHOOK WINAPI SetWinEventHook(DWORD eventMin, DWORD eventMax, HMODULE hmodWinEventProc, WINEVENTPROC lpfnWinEventProc, DWORD idProcess, DWORD idThread, DWORD dwFlags);
|
|
// WINABLEAPI BOOL WINAPI UnhookWinEvent(HWINEVENTHOOK hEvent);
|
|
|
|
// -------
|
|
typedef HRESULT (_stdcall *_tagAccessibleObjectFromEvent)(HWND hwnd, DWORD dwId, DWORD dwChildId, IAccessible** ppacc, VARIANT* pvarChild);
|
|
typedef WINABLEAPI HWINEVENTHOOK (WINAPI *_tagSetWinEventHook)(DWORD eventMin, DWORD eventMax, HMODULE hmodWinEventProc, WINEVENTPROC lpfnWinEventProc, DWORD idProcess, DWORD idThread, DWORD dwFlags);
|
|
typedef WINABLEAPI BOOL (WINAPI *_tagUnhookWinEvent)(HWINEVENTHOOK hEvent);
|
|
|
|
_tagAccessibleObjectFromEvent pAccessibleObjectFromEvent = NULL;
|
|
_tagSetWinEventHook pSetWinEventHook = NULL;
|
|
_tagUnhookWinEvent pUnhookWinEvent = NULL;
|
|
|
|
|
|
// -------
|
|
|
|
// GetAcctiveAccessibleFunctions()
|
|
|
|
// This function attempts to load the active accessibility functions we need
|
|
// from OLEACC.DLL and USER32.DLL
|
|
|
|
// If the functions are availible, this returns TRUE
|
|
|
|
// -------
|
|
BOOL GetAcctiveAccessibleFunctions()
|
|
{
|
|
// JMC: HACK: Until OLEACC Works correctly
|
|
return FALSE;
|
|
|
|
HMODULE hOleAcc = NULL;
|
|
HMODULE hUser;
|
|
if(!(hOleAcc = LoadLibrary(__TEXT("oleacc.dll"))))
|
|
return FALSE;
|
|
if(!(pAccessibleObjectFromEvent = (_tagAccessibleObjectFromEvent)GetProcAddress(hOleAcc, "AccessibleObjectFromEvent")))
|
|
return FALSE;
|
|
if(!(hUser = GetModuleHandle(__TEXT("user32.dll"))))
|
|
return FALSE;
|
|
if(!(pSetWinEventHook = (_tagSetWinEventHook)GetProcAddress(hUser, "SetWinEventHook")))
|
|
return FALSE;
|
|
if(!(pUnhookWinEvent = (_tagUnhookWinEvent)GetProcAddress(hUser, "UnhookWinEvent")))
|
|
return FALSE;
|
|
return TRUE;
|
|
};
|
|
|
|
|
|
// -------
|
|
|
|
// Per-process Variables
|
|
|
|
// -------
|
|
HMODULE g_hModEventDll;
|
|
|
|
|
|
// -------
|
|
|
|
// Shared Variables
|
|
|
|
// -------
|
|
#pragma data_seg("Shared")
|
|
HWINEVENTHOOK g_hEventHook = NULL;
|
|
HHOOK g_hMouseHook = NULL;
|
|
HWND g_hwndEventPost = NULL;
|
|
DWORD g_dwCursorHack = 0;
|
|
#pragma data_seg()
|
|
#pragma comment(linker, "/Section:Shared,RWS")
|
|
|
|
|
|
// -------
|
|
|
|
// Functions Prototypes (Foward References of callback functions
|
|
|
|
// -------
|
|
void CALLBACK NotifyProc(HWINEVENTHOOK hEvent, DWORD event, HWND hwndMsg, LONG idObject,
|
|
LONG idChild, DWORD idThread, DWORD dwEventTime);
|
|
|
|
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam);
|
|
|
|
|
|
// -------
|
|
|
|
// DllMain()
|
|
|
|
// -------
|
|
BOOL WINAPI DllMain (HINSTANCE hInst, DWORD dwReason, LPVOID fImpLoad)
|
|
{
|
|
switch (dwReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
g_hModEventDll = hInst;
|
|
break;
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
// -------
|
|
|
|
// InstallEventHookWithOleAcc
|
|
|
|
// This installs the WinEvent hook if hwndPostTo is not null, or removes the
|
|
// hook if the parameter is null. Does no checking for a valid window handle.
|
|
|
|
// If successful, this returns TRUE.
|
|
|
|
// -------
|
|
BOOL WINAPI InstallEventHookWithOleAcc (HWND hwndPostTo) {
|
|
if (hwndPostTo != NULL) {
|
|
if(g_hwndEventPost || g_hEventHook)
|
|
return FALSE; // We already have a hook installed - you can only have one at a time
|
|
|
|
// Install the hook
|
|
g_hwndEventPost = hwndPostTo; // Must set this before installing the hook
|
|
g_hEventHook = pSetWinEventHook(EVENT_MIN, EVENT_MAX, g_hModEventDll,
|
|
NotifyProc, 0, 0, WINEVENT_SKIPOWNPROCESS | WINEVENT_OUTOFCONTEXT/*WINEVENT_INCONTEXT*/);
|
|
if (!g_hEventHook) {
|
|
// Something went wrong - reset g_hwndEventPost to NULL
|
|
g_hwndEventPost = NULL;
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
// NOTE - We never fail if they are trying to uninstall the hook
|
|
g_hwndEventPost = NULL;
|
|
// Uninstalling the hook
|
|
if (g_hEventHook != NULL) {
|
|
pUnhookWinEvent(g_hEventHook);
|
|
g_hEventHook = NULL;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// -------
|
|
|
|
// InstallEventHookWithoutOleAcc
|
|
|
|
// This installs a mouse hook so we have some functionality when oleacc is
|
|
// not installed
|
|
|
|
// If successful, this returns TRUE.
|
|
|
|
// -------
|
|
BOOL WINAPI InstallEventHookWithoutOleAcc (HWND hwndPostTo) {
|
|
if (hwndPostTo != NULL) {
|
|
if(g_hwndEventPost || g_hMouseHook)
|
|
return FALSE; // We already have a hook installed - you can only have one at a time
|
|
|
|
// Install the hook
|
|
g_hwndEventPost = hwndPostTo; // Must set this before installing the hook
|
|
g_hMouseHook = SetWindowsHookEx(WH_MOUSE, MouseProc, g_hModEventDll, 0);
|
|
if (!g_hMouseHook) {
|
|
// Something went wrong - reset g_hwndEventPost to NULL
|
|
g_hwndEventPost = NULL;
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
// NOTE - We never fail if they are trying to uninstall the hook
|
|
g_hwndEventPost = NULL;
|
|
// Uninstalling the hook
|
|
if (g_hMouseHook != NULL) {
|
|
UnhookWindowsHookEx(g_hMouseHook);
|
|
g_hMouseHook = NULL;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// -------
|
|
|
|
// InstallEventHook
|
|
|
|
// This function checks to see if Ole Accessibility is installed, and if so
|
|
// uses the WinEvent hook. Otherwise, it uses a mouse hook.
|
|
|
|
// If successful, this returns TRUE.
|
|
|
|
// -------
|
|
|
|
BOOL g_bOleAccInstalled = FALSE;
|
|
BOOL g_bCheckOnlyOnceForOleAcc = TRUE;
|
|
|
|
BOOL WINAPI InstallEventHook (HWND hwndPostTo) {
|
|
// We check only the first time if Ole Acc is installed. From then on,
|
|
// we assume it remains constant.
|
|
if(g_bCheckOnlyOnceForOleAcc) {
|
|
g_bCheckOnlyOnceForOleAcc = FALSE;
|
|
g_bOleAccInstalled = GetAcctiveAccessibleFunctions();
|
|
}
|
|
|
|
if(g_bOleAccInstalled)
|
|
return InstallEventHookWithOleAcc(hwndPostTo);
|
|
else
|
|
return InstallEventHookWithoutOleAcc(hwndPostTo);
|
|
}
|
|
|
|
// -------
|
|
|
|
// GetCursorHack()
|
|
|
|
// This function returns the last known user cursor handle.
|
|
|
|
// -------
|
|
|
|
DWORD WINAPI GetCursorHack()
|
|
{
|
|
return g_dwCursorHack;
|
|
}
|
|
|
|
// -------
|
|
|
|
// NotifyProc()
|
|
|
|
// This is the callback function for the WinEvent Hook we install. This
|
|
// gets called whenever there is an event to process. The only things we
|
|
// care about are focus changes and mouse/caret movement. The way we handle
|
|
// the events is to post a message to the client (ScreenX) telling it
|
|
// where the focus/mouse/caret is right now. It can then decide where it
|
|
// should be magnifying.
|
|
|
|
// Parameters:
|
|
// hEvent A handle specific to this call back
|
|
// event The event being sent
|
|
// hwnd Window handle of the window generating the event or
|
|
// NULL if no window is associated with the event.
|
|
// idObject The object identifier or OBJID_WINDOW.
|
|
// idChild The child ID of the element triggering the event,
|
|
// or CHILDID_SELF if the event is for the object itself.
|
|
// dwThreadId The thread ID of the thread generating the event.
|
|
// Informational only.
|
|
// dwmsEventTime The time of the event in milliseconds.
|
|
// -------
|
|
/* Forward ref */ BOOL GetObjectLocation(IAccessible * pacc, VARIANT* pvarChild, LPRECT lpRect);
|
|
void CALLBACK NotifyProc(HWINEVENTHOOK hEvent, DWORD event, HWND hwndMsg, LONG idObject,
|
|
LONG idChild, DWORD idThread, DWORD dwmsEventTime) {
|
|
|
|
WPARAM wParam;
|
|
LPARAM lParam;
|
|
// Initialize pac to NULL so that we Release() pointers we've obtained.
|
|
// Otherwise we will continually leak a heck of memory.
|
|
IAccessible * pacc = NULL;
|
|
RECT LocationRect;
|
|
VARIANT varChild;
|
|
HRESULT hr;
|
|
// char sz[100];
|
|
|
|
switch (event) {
|
|
#if 0
|
|
case EVENT_OBJECT_HIDE:
|
|
switch (idObject) {
|
|
case OBJID_CARET:
|
|
OutputDebugString("Caret hidden\r\n"); break;
|
|
case OBJID_CURSOR:
|
|
OutputDebugString("Cursor hidden\r\n"); break;
|
|
}
|
|
break;
|
|
|
|
case EVENT_OBJECT_SHOW:
|
|
switch (idObject) {
|
|
case OBJID_CARET:
|
|
OutputDebugString("Caret shown\r\n"); break;
|
|
case OBJID_CURSOR:
|
|
OutputDebugString("Cursor shown\r\n"); break;
|
|
}
|
|
break;
|
|
#endif
|
|
case EVENT_OBJECT_FOCUS:
|
|
if (!IsWindowVisible(hwndMsg))
|
|
return;
|
|
hr = pAccessibleObjectFromEvent(hwndMsg, idObject, idChild, &pacc, &varChild);
|
|
if (!SUCCEEDED(hr))
|
|
return;
|
|
if (!GetObjectLocation(pacc,&varChild,&LocationRect))
|
|
break;
|
|
|
|
// center zoomed area on center of focus rect.
|
|
wParam = MAKELONG(((LocationRect.left + LocationRect.right) / 2), ((LocationRect.bottom + LocationRect.top) / 2));
|
|
lParam = dwmsEventTime;
|
|
|
|
// JMC: TODO: Make sure the top left corner of the object is in the zoom rect
|
|
PostMessage(g_hwndEventPost, WM_EVENT_FOCUSMOVE, wParam, lParam);
|
|
break;
|
|
|
|
case EVENT_OBJECT_LOCATIONCHANGE:
|
|
switch (idObject) {
|
|
case OBJID_CARET:
|
|
hr = pAccessibleObjectFromEvent (hwndMsg,idObject,idChild, &pacc, &varChild);
|
|
if (!SUCCEEDED(hr))
|
|
return;
|
|
if (!GetObjectLocation (pacc,&varChild,&LocationRect))
|
|
break;
|
|
|
|
// center zoomed area on center of focus rect.
|
|
wParam = MAKELONG(((LocationRect.left + LocationRect.right) / 2), ((LocationRect.bottom + LocationRect.top) / 2));
|
|
lParam = dwmsEventTime;
|
|
PostMessage(g_hwndEventPost, WM_EVENT_CARETMOVE, wParam, lParam);
|
|
break;
|
|
|
|
case OBJID_CURSOR:
|
|
hr = pAccessibleObjectFromEvent (hwndMsg,idObject,idChild, &pacc, &varChild);
|
|
if (!SUCCEEDED(hr))
|
|
return;
|
|
if (!GetObjectLocation (pacc,&varChild,&LocationRect))
|
|
break;
|
|
wParam = MAKELONG(LocationRect.left, LocationRect.top);
|
|
lParam = dwmsEventTime;
|
|
PostMessage(g_hwndEventPost, WM_EVENT_MOUSEMOVE, wParam, lParam);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (pacc)
|
|
pacc->Release();
|
|
}
|
|
|
|
// -------
|
|
|
|
// GetObjectLocation()
|
|
|
|
// This fills in a RECT that has the location of the Accessible object
|
|
// specified by pacc and idChild. The coordinates returned are screen
|
|
// coordinates.
|
|
|
|
// -------
|
|
BOOL GetObjectLocation(IAccessible * pacc, VARIANT* pvarChild, LPRECT lpRect) {
|
|
HRESULT hr;
|
|
SetRectEmpty(lpRect);
|
|
|
|
hr = pacc->accLocation(&lpRect->left, &lpRect->top, &lpRect->right, &lpRect->bottom, *pvarChild);
|
|
|
|
// the location is not a rect, but a top left, plus a width and height.
|
|
// I want it as a real rect, so I'll convert it.
|
|
lpRect->right = lpRect->left + lpRect->right;
|
|
lpRect->bottom = lpRect->top + lpRect->bottom;
|
|
|
|
if (!SUCCEEDED(hr))
|
|
return(FALSE);
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
// -------
|
|
|
|
// MouseProc()
|
|
|
|
// This is the callback function for the Mouse Hook we install.
|
|
|
|
// Parameters:
|
|
// -------
|
|
LRESULT CALLBACK MouseProc(int code, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
// For WM_MOUSEMOVE and WM_NCMOUSEMOVE messages, we post the main window
|
|
// WM_EVENT_MOUSEMOVE messages. We don't want to do this if we are in
|
|
// the address space of MAGNIFY.EXE. To avoid this, we also check taht
|
|
// g_bCheckOnlyOnceForOleAcc is TRUE. If g_bCheckOnlyOnceForOleAcc is TRUE,
|
|
// we are in another processes address space.
|
|
// If we posted ourselves WM_EVENT_MOUSEMOVE while in MAGNIFY.EXE, we got all
|
|
// sorts of weird crashes.
|
|
if((WM_MOUSEMOVE == wParam || WM_NCMOUSEMOVE == wParam) && g_bCheckOnlyOnceForOleAcc)
|
|
{
|
|
g_dwCursorHack = (DWORD)GetCursor(); // JMC: Hack to get cursor on systems that don't support new GetCursorInfo
|
|
MOUSEHOOKSTRUCT *pmhs = (MOUSEHOOKSTRUCT *)lParam;
|
|
PostMessage(g_hwndEventPost, WM_EVENT_MOUSEMOVE, MAKELONG(pmhs->pt.x, pmhs->pt.y), 0);
|
|
}
|
|
|
|
return CallNextHookEx(g_hMouseHook, code, wParam, lParam);
|
|
}
|
|
|
|
// -------
|
|
|
|
// FakeCursorMove
|
|
|
|
// This function is called to 'fake' the cursor moving. It is used by the
|
|
// magnifier app when a MouseProc is used. We run into problems when
|
|
// posting ourselves messages from the MouseProc of our own process. To
|
|
// avoid these problems, MouseProc() does not post a WM_EVENT_MOUSEMOUVE
|
|
// if we are in the address space of MAGNIFY.EXE. Instead, MAGNIFY.EXE
|
|
// is responsible for calling FakeCursorMove() whenever the mouse moves over
|
|
// a window of its own process. (NOTE: This is really easy to accomplish in
|
|
// MFC. We just call FakeCursorMove() from PreTranslateMessage() - see
|
|
// MagBar.cpp and MagDlg.cpp
|
|
|
|
// -------
|
|
|
|
void WINAPI FakeCursorMove(POINT pt)
|
|
{
|
|
g_dwCursorHack = (DWORD)GetCursor(); // JMC: Hack to get cursor on systems that don't support new GetCursorInfo
|
|
PostMessage(g_hwndEventPost, WM_EVENT_MOUSEMOVE, MAKELONG(pt.x, pt.y), 0);
|
|
}
|