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 WPARAM
s and
LPARAM
s 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>
{
friend class CHookMgr<CMyHookMgr>;
public:
virtual ~CMyHookMgr();
static Initialize(UINT uMyFlags, ....)
{
GetInstance().InitHooks(HM_CALLWNDPROC | HM_CBT | ...);
...
}
protected:
CMyHookMgr();
protected:
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
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.