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

Message Cracker Wizard for Win32 SDK Developers

0.00/5 (No votes)
5 Jul 2017 4  
A ClassWizard-like helper tool for using WINDOWSX.H message crackers.

Download source and Release:

https://github.com/hernandp/MessageCrackerWizard

 

Sample screenshot

Index

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 where is your e.g., WM_COMMAND or WM_CHAR, the block of code becomes a real nightmare.

The problem of the thousand lines window procedure can be solved with a header file that is 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 resumed 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.

Since 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 to work with them in your code.

When you are programming 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, etc., and you get a thousand-lines 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 in easy to maintain handling functions, like MFC.

And the second advantage is the proper parameter format you use in your handling functions. Instead of doing those 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 we'll write to process that message. To better explain, the following code in 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, 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, as you can jump to your message processing function with the Visual Studio IDE:

The problem is that you must search in the definitions of 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 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 that expects parameters such as PostMessage, SendMessage, CallWindowProc, etc.

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 LPARAM parameter is the handle to the control which we get here with the GetDlgItem API.

The above code can be converted to WINDOWSX.H message forwarding macros, 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 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 you are specifying the message. Common IDs are MainWnd, About (for about dialogs), etc. 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. Using 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 on 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. As 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 suffix in cracking macros and processing functions. Of course, you'll want to maintain it consistent 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 on 'Make Window Procedure' check box, and be sure that 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 on our source code. Press on 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 on 'Copy Function' button. Switch to your source code, locate your cursor where you want the functions bodies to be inserted, and paste with Ctrl+V or Edit/Paste menu. 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 using the two checkboxes in the main window.

More Message Cracker Wizard Features

There are a few more features present in the program, which are rather intuitive.

Message Filtering

This was a suggestion by some users of the program and it was implemented. Click on "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 on the type (this classification criteria was taken from Microsoft Spy++ utility).

Note that a present issue 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 disappear).

Compact Window Mode

You may want to reduce the window size of the 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 on the View menu, 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 "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 save (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.

History

  • 1.0
    • First release, Sep, 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 option.
    • Fixed keyboard logic.
  • 2.1
    • Fixed clipboard copy bug (thanks to Agnel Kurian).
    • Now the program is licensed under the GPL.
  • 2.2
    •  C++ code modernization to near- modern standards
    • Updated for compilation with VS2015
    • changed to Unicode for all strings
    • fixed analysis, warnings, and related issues
    • changed to MIT license

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