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.