Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

A template singleton class for easy implementation of Windows hooks

0.00/5 (No votes)
21 Mar 2003 2  
Simplifying the implementation of Windows hooking through the use of a templatized manager class

Introduction

I recently submitted a menu skinning article which used global Windows hooks to subclass all the menus in an application so that the standard Windows rendering of the menus could be overridden by the application.

The code for implementing the Windows hooks was fairly standard boilerplate code which I'd also used elsewhere and which I cut and pasted into the menu project.

It bugged me though that I still hadn't been able to see a solution whereby I didn't have to keep cutting and pasting the same code into new projects whenever I wanted a Windows hook.

Then, a few days ago, I read Shog9's excellent Balloon Help article about a replacement for MessageBox().

What particularly caught my eye was the use of thunking to pass a class member function as a callback to a Windows function which would normally only accept a global callback function. (I suggest you read the article if you want to know more about thunking.)

Note: the reason why this is so interesting is that many Windows global callback functions are a pain because there is often no facility to pass a user-defined piece of data (such as pointer to a class object) to allow the callback to be used in different contexts by different classes.

So here was a way to get around the global callback problem, albeit (after the excitement had died down) a rather tricky way.

You see, in this context, the thunk is effectively a piece of self-modifying code which, for someone who has always understood self-modifying code to be a big no-no, struck me as something I might want to avoid, since if it went wrong I wouldn't know how to fix it robustly.

However, due credit; it pointed me in the right direction for how to solve the global callback problem: templates.

I have to admit that I have been really tardy in getting to grips with templates. The principle issue is that without enough concrete experience of templates, I have tended to be unable to spot when they might be a useful solution to a given design problem, and without the benefit of implementing many template solutions I am unable to gain further insight into when best to use them, and so it goes on...

But once the seed had been planted, it all seemed so obvious.

By templatizing (is that a real word?) a base class, any class deriving from that base class could acquire its own set of static callback functions which would be entirely different to those of another derived class.

Granted, separate instances of the same class would still have to share a single function 'instance' but my particular experience of Windows hooks is that the classes I've written have always been singleton classes for the purpose of providing application wide hooking.

Note: if the base class was not templatized (there's that word again), then any static class functions would be the same, regardless of where you were in the class hierarchy, giving you no added benefit.

The implementation

With the design figured out, this turned out to be a fairly trivial task.

Essentially, I implemented a fairly standard singleton class and then added templates to it. (Note: as I understand it, there is no need to provide a default copy constructor if the singleton GetInstance() method is not public, but I welcome being proved wrong)

I also did the grunt work of adding the static functions for most of the Windows hooks, together with virtual equivalents that the derived class could override for the hooks it required.

Note: if you request a specific type of hook and then do not provide a handler for it, the base class will assert. This is similar to MFC's implementation of ownerdraw where CWnd::OnMeasureItem() and CWnd::OnDrawItem() assert if you forget to override them.

CHookMgr also make use of a partially implemented message tracing system which I propose to complete and then submit to CodeProject. What it does is simply take the pain out of mapping Windows message IDs to their string equivalents and extracting the WPARAMs and LPARAMs and converting whatever they represent to meaningful text.

Using the code

  • Add the following source files to your project:

    Note: in my demo project, these files are in a separate 'skinwindows' folder because they form a subset of a much larger skinning system, but there is no need for you to do the same.

    • CHookMgr (hookmgr.h/.cpp) - the templatized base manager
    • CWinClasses (winclasses.h/.cpp) - helper class for retrieving and testing window classes
    • ISMsgManager/ISMsgHandler (ismsgmanager.h/.cpp, ismsghandlers.h) - helper classes for converting MSGs to helpful string output. Note: these files can be dropped into any project where you need message trace output.
    • wclassdefines.h - convenient #defines for all window classes (and some others)
  • Derive your own hook manager class and provide overrides for whichever hook functions you need as follows:

    class CMyHookMgr : protected CHookMgr<CMyHookMgr>
    { 
       // so that the template class can see the protected c'tor
    
       friend class CHookMgr<CMyHookMgr>; 
                           
    public:
       virtual ~CMyHookMgr();
    
       static Initialize(UINT uMyFlags, ....)
       {
           // call base class Initialize to set up hooks required
    
           GetInstance().InitHooks(HM_CALLWNDPROC | HM_CBT | ...); 
           
           ... // any other initialization
    
       }
       
    protected:
       CMyHookMgr(); 
          
    protected:
       // virtual overrides
    
       virtual void OnCallWndProc(const MSG& msg);
       {
          ...
       }
       
       virtual BOOL OnCbt(int nCode, WPARAM wParam, LPARAM lParam)
       {   
          ...
       }
      
    };
  • Finally, at the appropriate point in your application startup code call:
    CMyHookMgr::Initialize(...);
  • That's it.

Copyright

The code is supplied here for you to use and abuse without restriction, except that you may not modify it and pass it off as your own.

History

  • 1.0 - Initial release

Notes

  • The sample application code is that of my menu skinning article, updated to use CHookMgr.
  • The source code is purely the 7 files noted above.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here