This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| 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

Download Associated File: STLMsgRouting.zip (642,903 bytes)

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.