Widget Technologies Home Page Free code Free Programs Almost free programs Free How-To ... Open Source License View what others had to say Favorite links Awards won by Widget Technologies About Widget Technologies Widget Technologies privacy statement Contact me Site map
Widget Technologies freeware and shareware site
 
First off I would like to say that researching hooks is not an easy subject to find information out about. To their credit, the vast majority of information that I have on hooks, other than trial and error, came from the MicroSoft Knowledge base. I am by no means a fan of MicroSoft, but I will give credit to where credit is due. The MicroSoft Knowledge base is an excellent source of information to program in Windows. I have found that the older versions of code that is available is usually more stable, more reliable, and much more complete than the newer versions. The older versions is for 3.1 so you will have to do a little bit of tweaking to make it work, but in the long run I think that it is worth it. So anyway, on with the show.
 
Hooks are a user defined function that can be used to monitor system events like mouse movements or clicks, key strokes, or messages. This function can monitor these actions and perform steps depending upon what the developer wants. The developer can, in some cases, modify or even discard the actions. Of course there are limits on what the function can do and most of them are beyond the scope of this page. I will give out the links that I have where you can view and/or download more information on hooks at the end of this page.
 
Hooks are used when you need to monitor or change an action that happens inside or outside the scope of your program. There are 2 types of hooks that a developer can use, Thread or System. A thread hook is speicific to the application that called it and can be contained with in the program. A system hook is a dll and can monitor all activity on a PC.
 
Some of the different hooks are:
  • WH_CALLWNDPROC: Thread or system hook. This hook is called whenever Windows SendMessage is called. You cannot alter this message
  • WH_CBT: Thread or system hook. This hook is used with Computer Based Training.
  • WH_DEBUG: Thread or system hook. This hook is called when Windows is about to call a filter function. ie. a hook.
  • WH_GETMESSAGE: Thread or system hook. This hook is called when the GetMessage or PeekMessage function is about to be called.
  • WH_JOURNALRECORD: System only hook. Windows calls this hook when a message is removed from the system queue. Function cannot modify or discard the message, only save it to disk or to memory.
  • WH_JOURNALPLAYBACK: System only hook. This hook is used to play provide mouse and keyboard messages to Windows as if an application was inserting them in to the queue. ie. Playing back of the WH_JOURNALRECORD.
  • WH_KEYBOARD: Thread or system hook. This hook is called when a WM_KEYDOWN, WM_SYSKEYUP, WM_SYSKEYDOWN, or WM_CHAR is returned from the GetMessage or PeekMessage function.
  • WH_MOUSE: Thread or system hook. This hook is called when Windows has a mouse message to process with a GetMessage or PeekMessage.
  • WH_MSGFILTER: Thread or system hook. This hook is called when a dialog box, message box, scroll bar, or menu retrieves a message.
  • WH_SYSMSGFILTER: System only hook. Same as WH_MSGFILTER, but at a global level.
 
The reasons why a developer would want to use a hook is numerous and ofter mysterious. I have used them because I needed to monitor key messages, record and playback what a user did on a PC, and had to monitor mouse movements no matter which application generated them. A developer needs to use hooks with caution because it can and sometimes does cause a great deal of overhead on a PC. If you install a hook, you should always give the user a way to stop the hook if the drain becomes to much. You will also want to make your code as tight as possible because the instructions with in the hook function is ran each time that the action is implemented.
 
Debugging hooks can be quite a chore and be prepared to crash your system the more you play with them. I have been programming for a long time and I believe that crashing your system is part of the process. But before I started playing with hooks I had never received kernel error messages before. When I got the first one I thought that was pretty cool, but after the 5th one I decided to be a little more cautious. Normally, when I am debugging an application I use the Windows MessageBox to see what is going on at the crash site. When I first started playing with hooks I tried this same style, but quickly found that Windows did not like it. The best that I can figure is that because the MessageBox is modal the dll did not pass the message to the next hook and Windows got pretty upset over that. I then started using OutputDebugString to debug the dll, but quickly found that there where some quirks with that as well. I found when I used the OutputDebugString my for loops would become infinite and some of the variables would contain junk information. The best way that I found to debug a hook or a dll was to pass messages to a regular program that could notify me of the problem. In the called program I could use the message box with out any problem.
 
Hooks use four functions:
  1. SetWindowsHookEx. This function takes 4 arguments and is used to set/start the hook.
    1. Integer code of the hook to set.
    2. Address of the hook function.
    3. The instance handle to the module that contains the hook function.
    4. The thread for which the hook is to be installed. If it is a system hook this is set to zero.
    5. Example:
      HHOOK hkKeyHook = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)hkProcKeyHook, hPrintInstDLL, 0);
  2. User defined hook function. This means that function name can be called anything that you would like, but it has to have the correct parameters, which are 3.
    1. Integer code telling the function to process the message or to let it pass. Usually anything less than zero, the hook function should pass on.
    2. WPARAM will pass information that is needed by the hook function to process a message.
    3. LPARAM will pass information that is needed by the hook function to process a message.
    4. Example:
      LRESULT CALLBACK setWTPSKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
  3. UnhookWindowsHookEx: This function takes one parameter and is used to stop the hook.
    1. Example:
      UnhookWindowsHookEx(hkPlayBackJournal)
      Note: hkPlayBackJournal is the handle to HHOOK of hook function
  4. CallNextHookEx: This function takes four parameters and is used to pass the message to the next hook in the chain.
    1. HHOOK is the handle to the current hook
    2. Integer code telling the function to process the message or to let it pass. Usually anything less than zero, the hook function should pass on.
    3. WPARAM will pass information that is needed by the hook function to process a message.
    4. LPARAM will pass information that is needed by the hook function to process a message.
    5. Example:
      CallNextHookEx(HHOOK hkPlayBackJournal, int nCode, WPARAM wParam, LPARAM lParam)
 
Hook examples:
  • System wide mouse hook: testmouse2.zip
    The program will set a mouse hook, will notify the creating program when a mouse click, left or right, has been generated, and will unhook the mouse hook when the creating program exits. The hook will notify the calling program by sending it a message of the action and the creating program will write a message to a memo box of the action. This example was created with Builder 6. There is very minimal amount of error checking in this example due to brevity.
  • System wide Journal hook
All examples where created with Builder 6 with very minimal error checking to save on space.
 
Below is one possible way of setting a WH_MOUSE hook, monitoring the movements, writing the movements to a file, and closing the hook. This is down by loading the DLL dynamically so I can get the handles that I need to load the hook. To save space I took out all error checking, in the real program I checked to make sure that the DLL and all of it's functions loaded properly.
Note: There are a few different ways that this program can be done and I choose to use files to maintain state. I used files for a different reasons. The main reason is that I am more comfortable with files and I was unable to find a great deal of information concerning sharing memory between applications.
//In the DLL itself declare the mouse hook function and get it's address	  
// This is the keyboard hook method
LRESULT CALLBACK setWTPSKeyboardHook(int nCode, WPARAM wParam, LPARAM lParam);      
// Address of the keyboard hook procedure that will monitor the keyboard
FARPROC hkProcKeyHook = {
                          (FARPROC)setWTPSKeyboardHook
                        };
						
// In the calling application
inst.dllInstance = LoadLibrary(load);
startWTPSMouseHook = (startMouse)GetProcAddress(inst.dllInstance, "startWTPSMouseHook");
startWTPSMouseHook();

// Back in the DLL at startWTPSMouseHook()
extern "C" __declspec(dllexport)
__stdcall void startWTPSMouseHook()
 {
   if (hPrintInstDLL)
    {
      getHomePath();            // Registry entry with home path
      getDLLInstance();         // Registry entry with DLL instance
    }
   if (hPrintInstDLL)
    {
      if (!hkProcMouseHook)
        writeWTInfoMessage(WT_MESSAGE_WARN, "Mouse hook NOT set. Region not working");
      else
       {
         hkMouseHook = SetWindowsHookEx(WH_MOUSE, (HOOKPROC)hkProcMouseHook, hPrintInstDLL, 0);
         writeHook(MOUSE_HOOK_INDEX, hkMouseHook);
         writeMonitorMouse(false);
         writeWTInfoMessage(WT_MESSAGE_INFO, "Mouse hook set. Region working");
       }  // End the else
    }  // End the if
   else
     writeWTInfoMessage(WT_MESSAGE_WARN, "Mouse hook NOT set. hPrintInstDLL is NULL");
 }  // End the startMouseHook function
 
// At SetWindowsHookEx
// In Windows every time an application is started, a copy of this DLL will be loaded into it's memory space.
// When this happens all variables in the DLL are brand new and contain no values. Because of this, I wrote the 
// the values of variables that I needed to the registry so I could retrieve them latter. There are other ways of
// doing this such as FileMapping, but I choose this way
LRESULT CALLBACK setWTPSMouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
  if (!hPrintInstDLL || !hkMouseHook)             // Another process started, fill in the values
    {
      getHomePath();
      getDLLInstance();
      hkMouseHook = getHook(MOUSE_HOOK_INDEX);
      readMonitorMouse();
    }  // End the else
   LPMOUSEHOOKSTRUCT mouseCoords;

   static bool bCapture = true;
   if ((nCode >= 0) && (bDoMouseClick))
    {
      mouseCoords = (MOUSEHOOKSTRUCT *)lParam;
      if (wParam == WM_LBUTTONDOWN)
       {
         if (bCapture)
          {
            ::RECT rcClip;
            rcClip.left = rcClip.right  = mouseCoords->pt.x;
            rcClip.top  = rcClip.bottom = mouseCoords->pt.y;
            char fPath[MAXPATH + 20];
            sprintf(fPath, "%s%s", hDir.homePath, CFGFileName[RECT_SIZE_INDEX]);
            ofstream outfile(fPath, ios::out | ios::trunc | ios::binary | ios::beg);
            if (outfile)
             {
               outfile.write((char *)&rcClip, sizeof(::RECT));
               outfile.close();
             }  // End the if
            writeWTInfoMessage(WT_MESSAGE_INFO, "Captured left mouse button DOWN");
            bCapture = false;
          }  // End the if
       }  // End the if

      if (wParam == WM_LBUTTONUP)
       {
         writeMonitorMouse(false);
         bCapture = true;        // Reset if for the next one
         ::RECT endClip;
         char fPath[MAXPATH + 20];
         sprintf(fPath, "%s%s", hDir.homePath, CFGFileName[RECT_SIZE_INDEX]);
         ifstream infile(fPath, ios::beg | ios::binary | ios::in);
         if (infile)
          {
            infile.read((char *)&endClip, sizeof(::RECT));
            infile.close();
          }  // End the if
         else
           endClip.left = endClip.top = 0;
         endClip.right  = mouseCoords->pt.x;
         endClip.bottom = mouseCoords->pt.y;
         ofstream outfile(fPath, ios::out | ios::trunc | ios::binary | ios::beg);
         if (outfile)
          {
            outfile.write((char *)&endClip, sizeof(::RECT));
            outfile.close();
          }  // End the if

         sendWTPSMessage(DO_REGION_INDEX);
         writeWTInfoMessage(WT_MESSAGE_INFO, "Captured left mouse button UP");
       }  // End the if
    }  // End the if
  return (CallNextHookEx(hkMouseHook, nCode, wParam, lParam));
}  // End the setWTPSMouseHook function

// Back at the calling application, the user is terminating the application
unHookWTPSMouseHook = (shutMouse)GetProcAddress(inst.dllInstance, "unHookWTPSMouseHook");
if (unHookWTPSMouseHook)
 {
   unHookWTPSMouseHook();
   writeWTInfoMessage(WT_MESSAGE_INFO, "Shut down, UnHooking the mouse hook");
 }  // End the if
else
  writeWTInfoMessage(WT_MESSAGE_INFO, "Shut down, Could not unhooking mouse hook");
  
// Back at the DLL, the mouse hook shuts down
extern "C" __declspec(dllexport)
__stdcall void unHookWTPSMouseHook()
 {
   if (!hPrintInstDLL || !hkMouseHook)             // Another process started, fill in the values
    {
      getHomePath();
      getDLLInstance();
      hkMouseHook = getHook(MOUSE_HOOK_INDEX);
    }  // End the else

   if (UnhookWindowsHookEx(hkMouseHook))           // Get rid of the hook
     writeWTInfoMessage(WT_MESSAGE_INFO, "Unhook Mouse hook");
   else
     writeWTInfoMessage(WT_MESSAGE_WARN, "Unhook Mouse hook NOT");
 }  // End the clearMouseHook function
 

 
 
Home | Open Source | Freeware | Shareware | Components | How-To
| O.S. License | Comments | Links | Awards | About | Privacy statement | Contact Me | Site Map