Using Message Crackers in the Win32 API with the MCW Tool

Environment: MFC, C++, Win32, SDK

Table of Contents


Introduction: The WINDOWSX.H Header Facilities for Win32 SDK Programmers

Many beginner and intermediate programmers are often faced with the problem of spaghetti switch…case code blocks when programming with the Windows API in C/C++. When you add a lot of messages to catch in your window procedure, looking for your WM_COMMAND or WM_CHAR block of code becomes a real nightmare.

The problem of the thousand-line window procedure can be solved with a header file that has shipped since the days of the C/C++ 7.0 compiler and the Windows Software Development Kit for Windows 3.1. That header is <windowsx.h> and contains a lot of useful macros. According to Microsoft, the facilities of this header file can be placed in the following groups:


  • Stricter type checking for C programs with the STRICT macro.

  • Macros to simplify many common operations in Windows programming.

  • Control macros to simplify communication with Windows controls.

  • Message crackers (which are a convenient, portable, and type-safe method to handle messages) and their associated parameters and return values in the Windows environment.

Because Message Cracker Wizard is designed to aid with the message crackers, I will skip the other useful macros the header file makes available. If you are interested in a brief description of what you can do with the WINDOWSX.H file, you can look at the MS Knowledge Base Article #83456.

Well, let’s introduce the advantages of the message crackers and, of course, why the tool offered here can be useful when working with them in your code.

When you program with the Win32 SDK, you process window and dialog messages with a window procedure, commonly named WndProc. It is very common in Windows C programming that the window procedure catches every window message using a switch keyword and a bunch of case labels for every message we want to catch.

Suppose that we want to process WM_COMMAND, WM_KEYUP, WM_CLOSE. and WM_DESTROY for our main window. We could write a window procedure with a logic like this:


LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg,
WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_COMMAND:
// …
break;

case WM_KEYUP:
// …
break;

case WM_CLOSE:
// …
break;

case WM_DESTROY:
//…
break;

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}

This is the most used manner, since Windows 1.0 days, to process window messages, and surely, it works. But the problem is when you begin to add more and more complex features to your program, such as MDI, OLE, common controls, and so forth, and you get a thousand-line window procedure. You begin to jump with PageDn and PageUp keys looking for a message you want to modify.

This is the first advantage of using message crackers: They convert that case label spaghetti into easy-to-maintain handling functions, such as MFC.

And the second advantage is the proper parameter format you use in your handling functions. Instead of doing switch(LOWORD(wparam)), you can simply use switch(id) because the message function that you provide passes id as one of the “cracked” parameters, which equals to LOWORD(wparam).

The Message handling macro, HANDLE_MSG, is defined in windowsx.h as follows:

#define HANDLE_MSG(hwnd, message, fn) \
        case (message) : return HANDLE_##message((hwnd), (wParam),
                                                 (lParam), (fn))

As you may expect from the macro definition above, to convert your code to the “message-cracked” version, you must supply the cracking macro, HANDLE_MSG, and the function to process that message. The HANDLE_MSG macro goes into the window procedure. It needs three parameters: the window handle (hwnd), the message you want to process (WM_xxxxx), and the function you’ll write to process that message. To better explain, the following code is the above window procedure converted to message crackers:


LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
{
switch(msg)
{
HANDLE_MSG (hwnd, WM_COMMAND, OnCommand);
HANDLE_MSG (hwnd, WM_KEYUP, OnKeyup);
HANDLE_MSG (hwnd, WM_CLOSE, OnClose);
HANDLE_MSG (hwnd, WM_DESTROY, OnDestroy);
default: return DefWindowProc(hwnd, msg, wParam, lParam);
}
}

Wow! This is a better, more compact, and easily manageable window procedure. Now, you would want to define your message processing functions (OnKeyUp, OnClose, and OnDestroy). This is a real advantage because you can jump to your message processing function with the Visual Studio IDE:

The problem is that you must search in the definitions of the WINDOWSX.H header and look for the parameters of the matching message processing function every time you add a message handler, because you can’t use any parameters you want: The format of the handling function is explicit. Doing this repeated searching in the header file can become a tedious task and can lead to errors. The Message Cracker Wizard Tool comes to the rescue: It allows you to paste the correct function parameters for every message handler you want. And, if you’re writing from scratch, it can also write a template window or dialog procedure to begin with the window messages you will process.

Message Forwarding Macros: Another XWINDOWS.H Feature

Another useful feature in the windowsx.h header is the possibility of message forwarding. This is used for “unpacking” the message processing function parameters into suitable WPARAM and LPARAM values to call another function thats expect that parameters such as PostMessage, SendMessage, CallWindowProc, and so forth.

Suppose that we want to use SendMessage to send a WM_COMMAND message to a parent window that “simulates” the double-clicking of a control named IDC_USERCTL by sending a notification code of BN_DBLCLK. You would normally use:


SendMessage (hwndParent, WM_COMMAND,
MAKEWPARAM(IDC_USERCTL, BN_DBLCLK),
(LPARAM)GetDlgItem(hwnd, ID_USERCTL));

This is a rather complex syntax: The SendMessage expects a WPARAM parameter where the low-order word is the control ID and the high-order word is the notification code; and the LPARAM parameter is the handle to the control that we get here with the GetDlgItem API.

The preceding code can be converted to a WINDOWSX.H message forwarding macro, FORWARD_WM_xxxxx. For each message, the forwarding macros use the same “packed” parameters as the message handling functions that Message Cracker Wizard creates, plus the function you want to call and pass the unpacked LPARAM/WPARAMs. For example, Message Cracker Wizard will generate the following function prototype for a WM_COMMAND message and a myWnd Window ID:


void myWnd_OnCommand (HWND hwnd, int id,
HWND hwndCtl, UINT codeNotify)

Well, those cracked parameters are the same to be used by the forwarding macro—so, as you may expect, the confusing SendMessage call we showed above can be reduced to:


FORWARD_WM_COMMAND (hwndParent, IDC_USERCTL,
GetDlgItem(hwnd, ID_USERCTL),
BN_DBLCLK, SendMessage);

That’s easy and works with all Message Cracker Wizard-supported messages.

Using the Message Cracker Wizard Tool

When you fire up the Message Cracker Wizard, its interface appears, and looks like the following:

The Wizard offers you all the messages handled by WINDOWSX.H in the top-left list box where you can click one or multiple messages. The Window ID Edit box allows you to specify an identifier for the window where you are specifying the message. Common IDs are MainWnd, About (for about dialogs), and so forth. This will appear in both the message handling function, in the HANDLE_MSG macro, and in the name of the window/dialog procedure if you want to create one from scratch. The “Make Window Procedure” check box allows you to do that: create from scratch a window or dialog procedure with all the selected message cracker macros ready. If you use this approach when beginning a Windows API project, you can cleanly write and organize your code, and of course, avoid mistakes. The two edit boxes at the bottom of the window will contain the generated code for the cracking macros and the functions to handle those messages (prototypes only). Note that the window procedure template code won’t appear here when you check “Make Window Procedure”; it will appear when you paste the code to your C++ editor only by clicking “Copy Macro”.

To quickly tour the features of the Message Cracker Wizard Tool, let’s do it by example. Remember that you must include the <windowsx.h> header with your project, using the #include <windowsx.h> directive in your .C / .CPP file.

Quick Tour of the Message Cracker Wizard Features

Let’s begin. Suppose you’ve already written your WinMain basic code: you’ve successfully filled the WNDCLASS structure, registered the window, and wrote a functioning message loop. Now, you need a window procedure for your main window.

Open the Message Cracker Wizard. We need to select messages for our window because MCW needs it to create our main window procedure from scratch. As you may know, it is very common for Windows programs to handle the WM_CLOSE, WM_DESTROY, and WM_CREATE messages, so let’s build the window procedure with message crackers for those messages. After that, we’ll also build the body of the message processing functions for that window procedure.

Select WM_CLOSE, WM_DESTROY, and WM_CREATE in the list box. Because this window will be the main window of our program, go the Window ID and type main. This is a Window ID that identifies your window/dialog and is put as a suffix in cracking macros and processing functions. Of course, you’ll want to maintain it consistently for all the message handling of a particular window. Look at the bottom edit boxes. They show the HANDLE_MSG cracker macro and the related prototypes of the message processing functions.

But wait… we said that we want a ready window procedure. So click the ‘Make Window Procedure’ check box and be sure that the Window radio button is selected. Now, we are ready. Keep in mind that Dialog works just like this, but modifies the procedure to be a dialog-type procedure.

First, we need the window procedure for our source code. Click the ‘Copy Macro’ button (or press Ctrl-M), minimize the Wizard (or keep it at hand, since it remains top-most), go to your IDE, and paste from the Clipboard (Ctrl-V) in the place you want your window procedure. Voilà! You will get code like this:


//
// main Window Procedure
//

LRESULT CALLBACK main_WndProc (HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam)
{
switch(msg)
{
HANDLE_MSG (hwnd, WM_CLOSE, main_OnClose);
HANDLE_MSG (hwnd, WM_CREATE, main_OnCreate);
HANDLE_MSG (hwnd, WM_DESTROY, main_OnDestroy);

//// TODO: Add window message crackers here…

default: return DefWindowProc (hwnd, msg, wParam, lParam);
}
}

That’s the window procedure with the three message cracking macros ready to work! And also, with a TODO comment to remember that you must add new message cracker macros there. Remember to unselect ‘Make Window Procedure’ checkbox when you want to add a HANDLE_MSG macro to the window procedure.

But the code above does nothing, because we need the functions that process those three messages we want. Simply return to the Message Cracker Wizard tool and now click the ‘Copy Function’ button. Switch to your source code, locate your cursor where you want the function’s bodies to be inserted, and paste with Ctrl+V or the Edit/Paste menu choice. The wizard automatically creates the functions with the main Window ID and the correct parameters expected by the WINDOWSX.H header macros:


//
// Process WM_CLOSE message for window/dialog: main
//

void main_OnClose(HWND hwnd)
{
// TODO: Add your message processing code here…
}

//
// Process WM_CREATE message for window/dialog: main
//

BOOL main_OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
// TODO: Add your message processing code here…
}

//
// Process WM_DESTROY message for window/dialog: main
//

void main_OnDestroy(HWND hwnd)
{
// TODO: Add your message processing code here…
}

The Wizard also automatically creates a heading comment and a TODO line to remind you to add code. Now, you can add your message handling and processing logic easily and write complex window procedures. You can remove the comments if you want by using the two checkboxes in the main window.

More Message Cracker Wizard Features

There a few more features present in the program; they are rather intuitive.

Message Filtering

This was a suggestion by some users of the program and it was implemented. Click the “Filters..” button (or press Ctrl+L) and you get the following dialog box. There you can select which messages appear listed on the listbox, classified by the type (this classification criteria was taken from Microsoft Spy++ utility).

Note: A issue present in v2.0, when using message filtering dialog, is that the list box is filled up again when you click OK, so the previous selection is lost (this does not mean that your previous selected messages that appear on the target code window will dissapear).

Compact Window Mode

You may want to reduce the window size of Message Cracker Wizard. This is possible by disabling “Show Target Code” option in the View menu (or by pressing Ctrl+F11). The main window will appear without the target code area:

Window Transparency, Exclude Comments, and Stay On Top

Another feature that can be useful for low-resolution displays or cluttered desktops is the window transparency feature. Click the View, Window Transparency menu and select a transparency percentage (Solid is 100% opaque and 75% is 25% opaque). This feature is only available for Windows 2000/XP and Server 2003 users. On 9X OSes, only the “Solid” option is available.

The Exclude Comments feature allows the code generator to exclude comments, either heading or “TODO” style commenting. Just select or unselect the checkboxes on the main window.

Finally, the Stay On Top feature is pretty self-descriptive.

Planned Features

The following features may appear in the following releases:


  • Help file.

  • Integrated Help for every message cracker parameter and message.

  • WTL Support!

  • Window ID and settings saving (this may be implemented in next 2.x)

  • Per-Project settings and “message-mappings” (a la MFC) (this also may be implemented in a later 2.5 release)

Have Fun and Good Programming!

I hope this little tool to be of interest to any Windows SDK programmer and of course, to be a potential method to write cleaner Win32 API programs. I’m open to suggestions to improve the tool. If you find this program useful, mail me, because I will be very happy to listen to any good comment.

Thanks for all support!! You know who you are!

As always, check my home page where I mention the updates to this program.

Version History


  • 1.0


    • First Release, September, 2003.

  • 1.2

    • Added multiple selection feature!!

    • Added missing crackers for WM_COPYDATA and WM_HOTKEY messages.

    • Fixed little interface bugs.

  • 2.0

    • Added message filtering.

    • Added window transparency option (only for Windows 2000/XP/Server 2003).

    • Added show/hide Target code.

    • Added enable/disable stay on top window.

    • Added WM_CTLCOLORxxxx message support.

    • Added message-type bitmaps on list box.

    • Added include/exclude comments options.

    • Fixed keyboard logic.


Downloads

Download source code – 24 Kb
Download MCW Tool Release 2.0 – 24 Kb

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read