🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: borland.public.cppbuilder.nativeapi
Article #587

Hooks, once again

#587
From: Anders Johansen
Date: Sat, 01 Mar 2008 09:54
174 lines
5991 bytes
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
Technical Details
Message-ID: <47c935cb$1@newsgroups.borland.com>
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!proxad.net!feeder1-2.proxad.net!194.25.134.126.MISMATCH!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail

Show Full Headers
Complete Article Headers
X-Google-Thread: 10cc67,4ebd883022a89a5d,start
X-Google-Attributes: gid10cc67,public,usenet
X-Google-Language: ENGLISH,ASCII-7-bit
From: "Anders Johansen" <home@dduck.dk>
Sender: "Anders Johansen" <home@dduck.dk>
Reply-To: "Anders Johansen" <home@dduck.dk>
Subject: Hooks, once again
Newsgroups: borland.public.cppbuilder.nativeapi
X-User-Info: 90.184.163.204     
NNTP-Posting-Host: 207.105.83.66
Message-ID: <47c935cb$1@newsgroups.borland.com>
Date: 1 Mar 2008 02:54:03 -0700
X-Trace: newsgroups.borland.com 1204368843 207.105.83.66 (1 Mar 2008 02:54:03 -0700)
Lines: 174
Path: arc1-ca.pugleaf.net!g2news1.google.com!news3.google.com!proxad.net!feeder1-2.proxad.net!194.25.134.126.MISMATCH!newsfeed01.sul.t-online.de!t-online.de!newsgroups.borland.com!not-for-mail