  | 
   Win32 Message Routing With The STL 
   Submitted by  |   
  
  
BeOS's native API supports a C++ class hierarchy that maps BeOS windows to C++ classes, and tools like
Delphi and MFC support similar paradigms in Win32. There is no such beast however in the pure-C Win32
API, which game programmers typically prefer. I wanted a window class that had the message handling
functionality that these high-level tools provide without the overhead of MFC or VCL.
 A simple way to connect C++ classes to Win32 windows is to use the STL's <map> container with the
window handle as the <map>::key_type and a base window class pointer (CWnd*) as the <map>::value_type.
I trap key messages WM_CREATE and WM_DESTROY and map the C++ objects to the corresponding HWNDs.  
 
 
  // Windows
 case WM_CREATE:
  {
   if(IsWindow(hWnd))
   {
    // Window instance passed in CreateWindow()
    CWnd* pWnd = NULL;
      // Get a CREATESTRUCT
    LPCREATESTRUCT pCreateStruct = reinterpret_cast<LPCREATESTRUCT(lParam);
      // Extract the pointer to the window
    if(pCreateStruct)
     pWnd = static_cast<CWnd*(pCreateStruct-lpCreateParams);
      // Attach C++ class to windows handle
    if(pWnd)
    {
     // Created
     if(pWnd-Msg(hWnd, uMsg, wParam, lParam))
     {
      // Set the window handle
      pWnd-m_hWnd = hWnd;
        // And mapped...
      WndMap[hWnd] = pWnd;
        // Diagnostics
      DumpHandleMap();
        // Success
      return FALSE;
     }
    }
   }
  }
    break;
   // Clean up...
 case WM_DESTROY:
  {
   if(IsWindow(hWnd))
   {
    // Look for this window in the handle map
    if(WndMap.find(hWnd) != WndMap.end())
    {
     // Get a pointer to window
     CWnd* pWnd = WndMap[hWnd];
       if(pWnd)
     {
      //  Let window handle WM_DESTROY
      pWnd-Msg(hWnd, uMsg, wParam, lParam);
        // Free the C++ object
      delete pWnd;
        // Erase the handle mapping
      WndMap.erase(hWnd);
        // Diagnostics
      DumpHandleMap();
     }
    }
   }
  }
    break;  |  
  
  
 
 
As messages arrive at the global WndProc() they are routed to the appropriate C++ class and
processed. I use message crackers to simplify the message processing itself.
    
 
 
 default:
  {
   // Look for this window in the handle map
   if(WndMap.find(hWnd) != WndMap.end())
   {
    // Get a pointer to window
    CWnd* pWnd = WndMap[hWnd]; 
      // Route the message to the correct window
    if(pWnd)
     return pWnd-Msg(hWnd, uMsg, wParam, lParam);
   }
  }  |  
  
 
  I used a template function to create the windows, this introduced complications as VC++ apparently does
not support template function instantiation outside the file of the template function definition itself
(Search under 'PRB: LNK2001 on Template Member Functions' for related info). The macro
DECLARE_WINDOW_CLASS() provides the necessary verbiage to the compiler.
  
/////////////////////////////////////////////////////////////////////////////
// Window creation
template <class T T* CreateWnd(HINSTANCE hInstance, LPCTSTR szClass, 
        DWORD dwStyles, LPCTSTR szTitle /* = NULL */, HWND hParent /* = NULL */)
{
 WNDCLASSEX wcex;
 ZeroMemory(&wcex, sizeof (WNDCLASSEX));
 wcex.cbSize        = sizeof(WNDCLASSEX);
    wcex.style         = 0;
    wcex.lpfnWndProc   = CWnd::WndProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = hInstance;
    wcex.hIcon         = 0;
    wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = HBRUSH(GetSysColorBrush(COLOR_BTNFACE));
    wcex.lpszMenuName  = NULL;
    wcex.lpszClassName = szClass;
    wcex.hIconSm       = NULL;
      // Register the window class
    RegisterClassEx(&wcex);
   // New window
 T* pWnd = new T();
   HWND hWnd = CreateWindow(
            szClass,     // Class of the window to create
            szTitle,     // Window caption text
            dwStyles,     // Window styles
            CW_USEDEFAULT, 0,
   CW_USEDEFAULT, 0,   
            hParent,     // Parent window handle
            (HMENU)0,     // Child window ID
            hInstance,     // Instance of module owning window
            pWnd);      // Pass a pointer to the derived class
 if(IsWindow(hWnd))
 {
  ShowWindow(hWnd, SW_SHOW);
  UpdateWindow(hWnd);
    // Find the window, perform sanity check
  if(WndMap.find(hWnd) != WndMap.end())
   pWnd = static_cast<T*(WndMap[hWnd]);
   } else
 {
  // Bad error, eject!
  delete pWnd, pWnd = NULL;
 }
   return pWnd;
}  |  
  
 
 
Here is what the sample application's WinMain() function looks like;
 
  
/////////////////////////////////////////////////////////////////////////////
// Window properties
LPCTSTR szClass = _T("PictureWnd");
LPCTSTR szTitle = _T("C++ Window");
  /////////////////////////////////////////////////////////////////////////////
// WinMain
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
 // Create a picture window  
 CPicture* pPicture = CreateWnd <CPicture (hInstance,
            szClass, 
            WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
            szTitle);
  
 // Main message loop
 MSG msg;
   while(GetMessage(&msg, NULL, 0, 0)) 
 {
  TranslateMessage(&msg);
  DispatchMessage(&msg);
 }
   return 0;
}  |  
  
 
 
I wrote a full-blown MDI application with several views, a toolbar, and accelerator support
using an approach similar to this. I have created a small sample application for download that
does use this base class, please check it out! It contains extensions for implementing dialogs as well.
 
  Usual waivers regarding responsibility for use of this code apply!
 
  Keith Tingle 
ktingle@bellsouth.net
 | 
 
 
 
 
The zip file viewer built into the Developer Toolbox made use
of the zlib library, as well as the zlibdll source additions.
 
 
 |