Click here to Skip to main content
15,867,453 members
Articles / Desktop Programming / MFC
Article

A template singleton class for easy implementation of Windows hooks

Rate me:
Please Sign up or sign in to vote.
4.64/5 (18 votes)
21 Mar 20035 min read 119.4K   1.7K   51   15
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


Written By
Software Developer Maptek
Australia Australia
.dan.g. is a naturalised Australian and has been developing commercial windows software since 1998.

Comments and Discussions

 
GeneralInformation Needed Pin
Exaltech28-Aug-04 7:52
Exaltech28-Aug-04 7:52 
GeneralRe: Information Needed Pin
.dan.g.7-Sep-04 19:24
professional.dan.g.7-Sep-04 19:24 
QuestionIs it possible to use instances of classes derived from templatized base classe across dlls ? Pin
Hawkeye4-Aug-04 4:42
Hawkeye4-Aug-04 4:42 
QuestionIs it possible to use instances of classes derived from templatized base classe across dlls ? Pin
Hawkeye4-Aug-04 4:42
Hawkeye4-Aug-04 4:42 
AnswerRe: Is it possible to use instances of classes derived from templatized base classe across dlls ? Pin
.dan.g.8-Aug-04 0:54
professional.dan.g.8-Aug-04 0:54 
QuestionI want to ask a question ? Pin
vihieu6-Apr-04 23:10
vihieu6-Apr-04 23:10 
AnswerRe: I want to ask a question ? Pin
.dan.g.6-Apr-04 23:20
professional.dan.g.6-Apr-04 23:20 
GeneralHelp me Pin
vihieu6-Apr-04 20:49
vihieu6-Apr-04 20:49 
Hi DanG,
I am very interested in your article, but I can not down load codes to test. Can you help me to solve this problem ?

vi
GeneralRe: Help me Pin
.dan.g.6-Apr-04 20:57
professional.dan.g.6-Apr-04 20:57 
GeneralRe: Help me Pin
vihieu6-Apr-04 21:43
vihieu6-Apr-04 21:43 
GeneralRe: Help me Pin
Exaltech26-Aug-04 7:56
Exaltech26-Aug-04 7:56 
GeneralRe: Help me Pin
.dan.g.26-Aug-04 11:48
professional.dan.g.26-Aug-04 11:48 
GeneralRe: Help me Pin
Exaltech27-Aug-04 22:35
Exaltech27-Aug-04 22:35 
GeneralSingletons, suggestions, congrats... Pin
Paul Evans15-May-03 22:31
Paul Evans15-May-03 22:31 
GeneralRe: Singletons, suggestions, congrats... Pin
.dan.g.18-May-03 14:53
professional.dan.g.18-May-03 14:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.