Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
109 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Message Handling

Like all windows, our window will receive messages sent by the operating system. Most of the messages require nothing more than the default processing provided by windows.  Others we'll wish to provide custom handling for.  In standard Win32 programming this is accomplished through the window procedure associated with the window class.  CWindowImpl provides a more convenient method.  It uses a set of macros to map window messages to functions in a fashion very similar to MFC.  The message map is begun with the BEGIN_MSG_MAP macro and terminated with the END_MSG_MAP macro.

#include <atlwin.h>

class MainWnd : public CWindowImpl<MainWnd>
{
public:
  DECLARE_WND_CLASS(_T("Specific_Class_Name"))

  BEGIN_MSG_MAP(MainWnd)
  END_MSG_MAP()
};

The BEGIN_MSG_MAP macro takes the name of the class containing it as its only parameter. Sandwiched between the beginning and ending macros are the message handler macros that actually map window messages to functions [1], [2].  There are a number of message handling macros, but for now we'll only be working with one: the MESSAGE_HANDLER macro.  The following line maps the WM_DESTROY window message to a function called OnDestroy().

MESSAGE_HANDLER(WM_DESTROY, OnDestroy)

For each message that needs handling, add a MESSAGE_HANDLER macro with the message ID as the first parameter and the name of the function to call as the second. Let's edit mainwnd.h, and add a MESSAGE_HANDLER for the WM_DESTROY message.

#include <atlwin.h>

class MainWnd : public CWindowImpl<MainWnd>
{
public:
  DECLARE_WND_CLASS(_T("Specific_Class_Name"))

  BEGIN_MSG_MAP(MainWnd)
    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  END_MSG_MAP()

Functions mapped with the MESSAGE_HANDLER macro should have the following definition.

LRESULT SomeFunctionName( UINT, WPARAM, LPARAM, BOOL& );

The first parameter (UINT) is the message itself (WM_CREATE, WM_PAINT, etc.).  The second and third are the same LPARAM and WPARAM parameters sent to a typical window procedure.  The fourth is a flag that indicates whether the message has been handled or not, and is set to TRUE by default.  Setting the value to FALSE will cause the message to undergo default message processing once the function returns [5].

Let's add our handler for the WM_DESTROY message to our MainWnd class.  The code below shows how our new mainwnd.h file should look.

#include <atlwin.h>

class MainWnd : public CWindowImpl<MainWnd>
{
public:
  DECLARE_WND_CLASS(_T("Specific_Class_Name"))

  BEGIN_MSG_MAP(MainWnd)
    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
  END_MSG_MAP()

 LRESULT OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
  {
      PostQuitMessage(0);
      bHandled = FALSE;
      return 0;
  }

};

Having implemented our WM_DESTROY handler, the MainWnd class is now complete.  It doesn't do much.  It just posts the quit message when the window is closed, but this is all we need for our first attempt at using the WTL.  We're now ready to return to main.cpp, create our window, and set up the message loop.

Creating the Window

If you were to open atlwin.h and looked at the definition of CWindowImpl, you'd find that it only implements one method: Create().  All other functionality is inherited from CwindowImplBaseT and BaseT [2].  The definition of CWindowImpl's Create() method is listed below.

HWND Create(HWND hWndParent, RECT& rcPos, LPCTSTR szWindowName = NULL,
                  DWORD dwStyle = 0, DWORD dwExStyle = 0,
                  UINT nID = 0, LPVOID lpCreateParam = NULL)

As you can see, only the first two parameters are required; the rest provide default values.  The parameters themselves are pretty standard.  The first is the parent window, and the second is a RECT defining the size and position of the window.  The third is the windows caption.  The fourth and fifth specify the windows style, and the sixth is a control ID usually used with child windows like button's and menu's.  The last parameter is assigned to the lpParam value of the CREATESTRUCT structure passed with the WM_CREATE message [4], [2].

Let's return to main.cpp and add code to create our window.  First we'll need to include mainwnd.h.  Notice that I included it after the definition of _Module (see listing below).  This is necessary to prevent compilation errors since atlwin.h, which is included in mainwnd.h, requires that _Module be defined.  Let's also declare a variable of type MainWnd, called mainwnd; and add a calls to Create(), ShowWindow(), and UpdateWindow().

#include <windows.h>
#include <atlbase.h>
#include <atlapp.h>

CAppModule _Module;

#include "mainwnd.h"

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nCmdShow)
{
  MainWnd mainwnd;

  _Module.Init(NULL, hInstance);

  mainwnd.Create(NULL,CWindow::rcDefault,_T("Main Window"),
                                WS_OVERLAPPEDWINDOW);
  mainwnd.ShowWindow(nCmdShow);
  mainwnd.UpdateWindow();

  _Module.Term();

  return 0;
}

Everything here should be pretty straightforward with the possible exception of the value I passed for the second parameter (CWindow::rcDefault).  This is simply a RECT structure with default values.  CWindow exposes it for the sake of convenience [3].

The Message Loop

Moving right along we'll now add our message loop to main.cpp.  The WTL provides a class called CMessageLoop to simplify adding message loops.  This class, along with Module, work together to route messages to our windows.  CMessageLoop exposes a member function called Run() that actually starts the message loop.  Run() returns the WPARAM value of the WM_QUIT message which in turn is the value that was passed to PostQuitMessage(). I don't want to get too involved with the details of WTL's message loops here; we'll cover them in more detail in part 2 of this series.

We now have a completed, albeit simple WTL application.  The listing below shows our new, complete main.cpp file.

#include <windows.h>
#include <atlbase.h>
#include <atlapp.h>

CAppModule _Module;

#include "mainwnd.h"

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                     LPSTR lpCmdLine, int nCmdShow)
{
  CMessageLoop theLoop;
  MainWnd mainwnd;

  _Module.Init(NULL, hInstance);
  _Module.AddMessageLoop(&theLoop);

  mainwnd.Create(NULL,CWindow::rcDefault,_T("Main Window"),
                                WS_OVERLAPPEDWINDOW);
  mainwnd.ShowWindow(nCmdShow);
  mainwnd.UpdateWindow();

  int nRet = theLoop.Run();

  _Module.RemoveMessageLoop();
  _Module.Term();

  return nRet;
}




Conclusion

Contents
  Introduction
  Windows Class
  Message Handling
  Conclusion

  Source code
  Printable version
  Discuss this article

The Series
  Getting Started
  Windowing