Achieving good results in window skinning requires an exacting knowledge of exactly what Windows messages are sent to whom and when.

In the past, like many of you, I would reach for Spy++ and/or generate reams of TRACE statements and then stare for what seemed like hours trying to make sense of the output.

Just recently though, a number of specific issues had really begun to bug me:

Spy++ doesn't allow you to spy on windows until after they have been created which means you generally miss a very interesting period in the window's life cycle.

For the same reason, its very hard to spy on transient windows like menus, tooltips, combobox droplists, etc

Spy++ doesn't easily (or at all?) allow you to spy on a whole bunch of windows all at the same time.

using TRACE statements generally leads to output which has the message type expressed as a number rather than its usual WM_xxx identifier.

Windows messages are hard to filter using TRACE statements, resulting usually in either too many or too few.

Out of this was born InterSpy, an integrated Windows message tracing utility, which solves all the problems I previously outlined and provided me with hours of enjoyment.

Important Note

This utility requires you to build some source code into your application and cannot therefore be applied to a 3rd party application (unless you also have the source code).

The challenge

Because this solution requires the inclusion of source code, the greatest challenge was to create something simple and accessible; that could be used with the minimum of fuss but giving the maximum result.

I happy to agree that I probably haven't reached that goal, but I find its always a useful place to aim for.

The following positive attributes were sought:

Compact form

Clearly delineated information

Easily navigable

Filterable

The solution

The first 'problem' to solve was how to get a handle on all the Windows messages flowing through an application.

The simplest solution to this I've found is to use application-wide Windows hooks.

Note: In the interest of those who do not care for Windows hooks I did spend sometime investigating the possible use of PreTranslateMessage to see an application's messages but it was a dismal failure; MFC's implementation seems to leave out more messages than it shows and it certainly leaves out the interesting ones at the start and end of a window's life. (if anyone has anymore specific knowledge about exactly what PreTranslateMessage() does and does not show I would be interested to learn more.)

To implement this I used CHookMgr which removes the ugly hooking details and simply requires the overriding of a few virtual functions to handle the message hooks.

As far as I can tell, the required Windows hooks to trap all Windows messages are WH_CALLWNDPROC and WH_GETMESSAGE (which translate to passing HM_CALLWNDPROC | HM_GETMESSAGE to CHookMgr).

The next step was to decide what to do with the messages.

Coming from an engineering background, I always favor figuring out the larger elements of a project before doing too much detail. That way, if I stumble up against something nasty in the woodshed (joke requiring some knowledge of an English dramatized play called 'cold comfort farm') I will not have wasted too much hard work.

It was already obvious that the messages would need to be sent to a separate window but the 'where, what and how' had not been decided.

My first prototype created a window in the same process as the application that was being traced, but the biggest problem was that the window was destroyed when the application terminated, which was an unacceptable 'feature'.

I was also interested in the possibility of outputting to a Visual Studio tool window, but not knowing the first thing about writing plug-ins for Visual Studio, rather put the dampeners on that idea.

So I was left with creating a separate application with a specially registered class name so that the code built into the client application could find it easily.

The only other architectural issue to resolve was the means of communicating between two, although strictly the communication is only one-way. I'd used WM_COPYDATA a number of times before and so stuck with that because I was familiar with it.

Implementation details

Client-side (code built into the application)

One complication of having a sort-of client-server architecture is that the two sides do not share the same address space.

What this means simply is that the message processing would have to be done on the client side and not in the logging window, as I had initially wanted (remember: my first prototype created the logging window in the same process as the application to take advantage of the shared address space).

This was a nuisance because I had wanted to keep the source code to be compiled into the client application to be the absolute minimum necessary. Oh well, plans of mice and men and stuff ...

All Windows messages essentially take the same form: that of the MSG structure. However, after that, every one is different in how the wParam and lParam values are used.

Some messages have no parameters, some use only one parameter and some use both parameters. Further, whilst most often the parameters are used to supply simple UINT values, some use the lParam (typically) to point to a memory structure supplying more detail than could otherwise be shoe-horned into the simple parameters.

I therefore first decided to implement a simple base-class message handler, CISMsgHandler, which would do no more than crack each message into its constituent parameters.

Then, taking a pointer from MFC, I wrote a message map of sorts (implemented using macros) to assign an instance of this class to all the possible Windows messages I could find (and then some). These include: all the standard messages, the MFC specific messages in afxpriv.h and a bunch I have come across during my searches of the news groups and my own debugging.

Note: I am sure that some of the messages are obsolete but, since these will simply not be triggered if that is the case, I see little harm in leaving them in.

The obvious and intended benefit of this default message handler is that it allowed me to get the message map working so that I could then build the 'server-side' logging window to receive the messages.

Thereafter, it was plain old slog work to implement specific handlers for all the different message types. Fortunately, many messages such as WM_LBUTTONDOWN share common parameters with other mouse button messages so that a single handler could be used for 6 or 7 messages. Elsewhere, its one for one, and in this case you'll see that I have not quite finished the task.

In cracking the messages, you may also note that I have not always gone as far as I might have. e.g. where a parameter describes a window handle (HWND) I could have also retrieved the window's class and text to provide more information. The reason I have not done this at present is simply an issue of efficiency and performance.

Server-side (Message logging window)

There's not much more to say that cannot be gleaned from the screenshot above.

The main output view is a CTreeCtrl, chosen simply because the data is hierarchical in nature and the tree control allows data to be hidden and displayed with such ease.

I also like the specific implementation of custom-draw on trees and the fact that you can embolden items just by setting a flag.

The logging window currently provides the following 'features' to simplify the output and navigation of messages:

Facility to disable a specific message across all windows (disabled messages appear light-grey). Note: once disabled, the message will no longer appear until the logging window is restarted or you re-enable the message.

Facility to clear all disabled messages from the tree.

Facility to disable all messages for a specific window (disabled windows and their messages appear dark-grey).

Single stepping forwards and backwards through the message list in the strict order in which the messages were received (indicated thus [nn] in the output window).

Facility to save message lists as plain text or XML.

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.

CInterSpy (interspy.h/.cpp) - the derived hook manager which intercepts all messages, cracks them, and forwards the details to the logging window.

CHookMgr (hookmgr.h/.cpp) - the hook manager base class.

ISMsgManager/ISMsgHandler (ismsgmanager.h/.cpp, ismsghandlers.h) - message handler classes and manager which converts each MSG into an array of parameter strings. Note: these files can be dropped into any project where you need message trace output.

// assumes files are in same folder as rest of the project
#include<spanclass="code-string">"interspy.h"</span>
BOOL CMyApp::InitInstance()
{
:
:
// if there are specific messages that
// you always want excluded then
// these can be done statically in advance
CInterSpy::ExcludeMsg(WM_NCHITTEST);
CInterSpy::ExcludeMsg(WM_MOUSEMOVE);
CInterSpy::ExcludeMsg(WM_SETCURSOR);
CInterspy::Initialize(<flags>);
:
:
}

The flags which may be passed to CInterspy::Initialize() are as follows:

IS_AUTOSTARTOUTPUT - automatically starts the log window if it is not running.

Further work

Provide built-in and user-definable filters to handle certain window types. e.g. a filter for menus would display only menu windows together with menu related messages sent to other windows.

Provide more detail on HWND parameters, perhaps with the ability to jump to the tree item representing the window being referenced.

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. The concept and design, however, remains my intellectual property.

History

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.

First of all thanks for such a wonderful debugging tool. It's a life saver. However it does not work with UNICODE MFC programs. I have ported your old code to unicode and I also intend to implement a few enhancements. I would like to share this with the general public, need your blessings for this.

Regards,
Sandeep Datta.

The best way to accelerate a Macintosh is at 9.8m/sec-sec - Marcus Dolengo

I am working on an application which can restrict notepad application from moving. For that, I am writing a HOOK dll and using WH_CALLWNDPROC to trap WM_WINDOWPOSCHANGED & WM_WINDOWPOSCHANGING messages. As per the MSDN we can not modify the message in WH_CALLWNDPROC hook.

I'm working in VISUAL C++ embedded 3.0. with Windows CE. My goal is to monitorize mouse global events.
By the moment I'm working with a WH_JOURNALRECORD global hook that catches all input global events of the

operating system, basically keyboard and mouse events. But when I catch a mouse event, it only gives me
information about the application that receives this event, and the position (x,y) where the user pushes
in the PDA's screen. I want to know, for example, if the user clicks one option in a menu, so what option
the user clicks, or if it clicks a desktop icon, so i want to know icon's name, and so on.

I want to make a program for windows CE mobile devices, like Smartphones and PocketPCs, and the goal of the
program will be monitorize all mouse global events in order to help the user in his navegation in the system. I

want to make a program like "Narrator.exe" of Windows XP, that processes all mouse events and after a speech

voice synthetizer says the option that the user clicks. Is this possible in Windows CE? Microsoft says that NO,

because in windows ce isn't support COM Architecture due to the specific capabilities of the hardware in the

Is it possible to use InterSpy with a WTL project. If the answer is yes then are there any specific instructions to follow other than the information in the article. I tried to include it in the project but keep getting the compile error message that WINDOWS.H should not be included in the MFC projects.

I have a problem. I'm using a WH_KEYBOARD_LL hook in order to capture all system keyboard events.
I create the hook without problems, but when I want to delete it, the function unhookWindowsHookEx()
returns a FALSE (that wants to say error in the function), but the hook is correctly deleted,
because if after I create another WK_KEYBOARD_LL hook, it will be created correctly.

But my main problem is that in the LowLevelKeyboardProc (the hook process), I cannot detect
all keyboard events of all the applications of my pda pocket pc. I only detect the message
WM_KEYDOWN in a few number of keys such as: caps lock, shift, esc, and so on. And I want to detect
all keyboard events. What do you think that could be the problem?

m_pfSetWindowsJournalHook is a pointer to the QASetWindowsJournalHook function, that's in the
coredll.dll library.

I have a JournalRecordProc process, and finally I want to delete the global hook:

m_pfUnhookWindowsJournalHook(m_hHkJournalRec)

where m_pfUnhookWindowsJournalHook is a pointer to the QAUnhookWindowsJournalHook function, that's in
the coredll.dll library.

And my problem, is that this function returns false, instead of true, and that wants to say that
the global hook isn't deleted correctly, and the system resources are not free. In addition, when
my application finishes in the Pocket PC, the PDA becomes blocked, and it doesn't detect any other
keyboard or mouse event, and I have to do a software reset.

There is a function, called GetLastError(), that gives you the last error that happens in the
system, but this function returns 0, that wants to say, "all is correct". Someone can help me, and
knows how delete this global hook correctly?

HINSTANCE aInstance = GetModuleHandle(NULL); --> aInstance makes reference to real module, not
to a dll, it's not necessary! (it can be also an EXE file);

You can help me? I'm working in a WINDOWS CE project with Visual Studio 2003 .net, and I want to use a global hooks application with a project in C# for my pocket PC device under windows ce .net. I ask you if it's possible to do this? In other words, I ask you if it's possible to build an application that works under Pocket PC?

These examples that are in the other messages, work under pocket pc for global hooks???

Although I havent gone through your article yet, but I wanted to ask this question. Is there any method of hooking to hook the internal windows messages. e.g., memory allocation and de-allocations. Can it be hooked and the contents of these messages be seen. If yes, the please elaborate a little.

I am about to create some kind of a table, with 5 columns and 6 rows, where each field are going to act like some kind of a ComboBox, but the arrow for the combobox are only allowed to be shown when the user hits the field.
This has to be solved in some way, and because of that I am going to figure out how for instance the combobox acts in Windows. And here Spy++ couldn’t help me, but your excellence program have lead me so far, very very good!

If you some day is going to make improvements, I have a little wish… I very often uses Alt+Tab to switch between my programs, but when InterSpy gets focus, I have to grab my mouse, in order to set focus to the view containing all the logged messages…. Maybe the focus should be set to this view, when the inspector gets focus, or it should be possible to switch to the view the TAB. I don’t know, and maybe you have a much better idea

Anyway this was just a little wish from a guy located in the cold Denmark.

hi,,,
Thanx for the tool it is good one...
I need one more info...
I have added a combo to the existing dialog....
if i want to know all the messaged coming for the combo
such as CBN_SELENDOK and etc.....
Can it be extended to accomodate the same ??