Author : Oluseyi Sonaiya
Page : << Previous 2
it->second;
m_MsgHandlers.insert(std::pair<long,tyMessageHandler>(message, handler));
return m;
}
Alright, so it wasn't so dramatic. The RegisterMessageHandler method inserts a message handler into the message map and returns the previous message handler, if there was one. That about wraps it up for message handling (I say about because I'll revisit one of the methods described above later). Now let's turn to integrating the Window with the application message pump.
The Window Class and the Application Message Pump
A typical Windows message pump looks something like this:
MSG msg;
while(GetMessage(&msg, hwnd, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// other procedures
}
Microsoft actually advises against this form of message loop because GetMessage has a tri-state return value and the above may cause an attempt at execution even when there was an error.
We do it anyway.
This article is getting long and I'm getting tired, so I'll dump the code and then break it down.
// Window::OnDestroy, revisited
long Window::OnDestroy(Window &wnd, HWND hwnd, long param0, long param1)
{
PostQuitMessage(0);
wnd->SetExit(true);
return 0;
}
// Window::HandleMessage ties everything together
bool Window::HandleMessages()
{
static MSG msg;
if(!m_hwnd)
throw std::runtime_error(std::string("Window not yet created"));
if((m_UsePeekMessage)
? ::PeekMessage(&msg, m_hwnd, 0, 0, PM_REMOVE)
: ::GetMessage(&msg, m_hwnd, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else
{
if(IsExit())
{
SetExitCode((long)msg.lParam);
return false;
}
if(m_UseWaitMessage)
WaitMessage();
}
return true;
}
There's a bunch of functions not introduced here. Obviously, the constructor and methods like Create and ShowWindow; I leave this as an exercise for the inexperienced reader and a chore for the expert. There are also the boolean variables m_UseWaitMessage, m_UsePeekMessage and m_Exit; the exit code for the Window (to pass to the application if the Window is the main window); and the static method to register the windowclass. The code above is fairly self-explanatory. As is evident, I use exceptions to avoid having to pass and compare return values for application-terminating errors. I also use the m_UsePeekMessage and m_UseWaitMessage as discriminants between using GetMessage and PeekMessage, and whether or not to use WaitMessage respectively.
That's it. And here's a simple example of my implementation in use:
Window *g_wnd;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /* hPrevInstance */,
LPSTR lpCmdLine, int nShowCmd)
{
try
{
g_wnd = new Window(hInstance);
g_wnd->Create(NULL, WS_OVERLAPPEDWINDOW|WS_VISIBLE);
if(!g_wnd)
throw std::runtime_error(std::string("Initialization Failed: Window::Create"));
g_wnd->UsePeekMessage();
g_wnd->UseWaitMessage(false);
g_wnd->ShowWindow(nShowCmd);
while(true)
{
if(!g_wnd->HandleMessages())
break;
}
}
catch(std::runtime_error &e)
{
::MessageBox(NULL, e.what(), "Runtime Error",
MB_OK|MB_ICONERROR|MB_DEFBUTTON1|MB_TASKMODAL|MB_TOPMOST);
return -1;
}
catch(std::logic_error &e)
{
::MessageBox(NULL, e.what(), "Logic Error",
MB_OK|MB_ICONERROR|MB_DEFBUTTON1|MB_TASKMODAL|MB_TOPMOST);
return -1;
}
catch(...)
{
::MessageBox(NULL, "Unhandled Exception", "Unknown Error",
MB_OK|MB_ICONERROR|MB_DEFBUTTON1|MB_TASKMODAL|MB_TOPMOST);
return -1;
}
return g_wnd->ExitCode();
}
Hopefully this article has been useful as a solution, but more importantly has inspired you to even better implementations which I hope you share with us all.
Cheers!
Page : << Previous 2