|
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.
|