|
Anders Johansen
CBuilder Developer |
Moving hooke callback to thread - suggestions? (was:Hooks, once again)
2008-05-07 05:40:21 PM
cppbuilder23
Hi all,
The code below has a potential flaw: The callback code is
running in the main thread, and so may block the system event
processing or at least delay it.
Is there an easy/obvious/recommended way to do this? A
suggestion that has one of the listed attibutes is fine, but
I would of course love one that has all three :)
Anders
"Anders Johansen" < XXXX@XXXXX.COM >wrote:
Quote
Hi all.
I have so far been using the "old fashioned" approach to
keyboard and mouse hooking, meaning putting the hook procedures
in a DLL, and using the SetWindowsHookEx with WH_KEYBOARD and
WH_MOUSE. Now Vista arrives, and this approach is apparently
blocked for some applications (notable IE) when not running as
Administrator. I understand the rationale is that this allows
for code injection, which I guess is fair enough.
This is my alternate approach, submitted for comments and
inspiration.
I understand that the _LL versions of the hooks will work. They
have the disadvantage of not being available on systems older
than NT4 SP3. On the other hand, the callback does not have to
be in a DLL, which seems to work - can anybody confirm that this
is correct, e.g. that a DLL is NOT needed when using the _LL
hooks?
If, as my testing seems to validate, the hooking does not have
to happen in a DLL, this allows for a much nicer implementation
than the usual approach. Here's my first attempt, in case
anybody wants to comment or inspect...
Header:
#ifndef HookMasterH
#define HookMasterH
//---------------------------------------------------------------------------
#include <windows.h>
#include <map>
#include <set>
class HookObserver {
public:
virtual void keyDown(const UINT vkey, const bool altDown, const int keyDownNumber) = 0;
};
/*
The HookMaster uses hooks to monitor keyboard and mouse, and MSAA to
monitor focus change and window move/resize.
*/
class HookMaster {
private:
static HHOOK LLKeyHook;
static HHOOK LLMouseHook;
static int keyDownNumber;
static std::set<HookObserver*>observers;
static LRESULT CALLBACK LLKeyHookProc(int nCode, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK LLMouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
static void notifyKeyDown(const UINT vkey, const bool altDown);
public:
static void addObserver(HookObserver *o);
static void removeObserver(HookObserver *o);
static int getKeyDownNumber();
static bool setHooks();
static bool removeHooks();
};
CPP file:
//---------------------------------------------------------------------------
#pragma hdrstop
#include "HookMaster.h"
#include <SysInit.hpp>
//---------------------------------------------------------------------------
#pragma package(smart_init)
HHOOK HookMaster::LLKeyHook = NULL;
HHOOK HookMaster::LLMouseHook = NULL;
int HookMaster::keyDownNumber = 0;
std::set<HookObserver*>HookMaster::observers;
LRESULT CALLBACK HookMaster::LLKeyHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
try {
if (nCode<0 || nCode==HC_NOREMOVE )
return CallNextHookEx(LLKeyHook, nCode, wParam, lParam);
KBDLLHOOKSTRUCT * pData = ( KBDLLHOOKSTRUCT *) lParam;
bool Down = (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN);
bool Alt=(pData->flags & LLKHF_ALTDOWN); // Alt is down
UINT vkey = pData->vkCode;
if (Down) {
if (GetKeyState(VK_CONTROL)>=0 ) {
notifyKeyDown(vkey, Alt);
}
}
} catch (...) {
}
return CallNextHookEx(LLKeyHook, nCode, wParam, lParam);
}
LRESULT CALLBACK HookMaster::LLMouseHookProc(int nCode, WPARAM wParam, LPARAM lParam) {
try {
if (nCode==HC_ACTION) {
switch (wParam) {
case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK:
notifyKeyDown(VK_RBUTTON, false);
break;
case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK:
notifyKeyDown(VK_LBUTTON, false);
break;
case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK:
notifyKeyDown(VK_MBUTTON, false);
break;
}
}
} catch (...) {
}
return CallNextHookEx(LLMouseHook, nCode, wParam, lParam);
}
void HookMaster::addObserver(HookObserver *o) {
observers.insert(o);
}
void HookMaster::removeObserver(HookObserver *o) {
observers.erase(o);
}
int HookMaster::getKeyDownNumber() {
return keyDownNumber;
}
bool HookMaster::setHooks() {
if (LLKeyHook == NULL) {
LLKeyHook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)LLKeyHookProc, HInstance, 0);
}
if (LLMouseHook == NULL) {
LLMouseHook = SetWindowsHookEx(WH_MOUSE_LL, (HOOKPROC)LLMouseHookProc, HInstance, 0);
}
return (LLKeyHook != NULL && LLMouseHook != NULL);
}
bool HookMaster::removeHooks() {
if (LLKeyHook != NULL) {
if (UnhookWindowsHookEx(LLKeyHook)) {
LLKeyHook = NULL;
}
}
if (LLMouseHook != NULL) {
if (UnhookWindowsHookEx(LLMouseHook)) {
LLMouseHook = NULL;
}
}
return (LLKeyHook == NULL && LLMouseHook == NULL);
}
void HookMaster::notifyKeyDown(const UINT vkey, const bool altDown) {
for (std::set<HookObserver*>::iterator i = observers.begin(); i != observers.end(); ++i) {
try {
HookObserver *obs = *i;
obs->keyDown(vkey, altDown, keyDownNumber);
} catch (...) {
// Something bad happened with the observer
}
}
++keyDownNumber;
}
Yes, it needs a little clean-up in sethooks, as it may return
false and leave a keyboard hook dangling if setting the mouse
hook fails...
A
|