Upcoming Events
Unite 2010
11/10 - 11/12 @ Montréal, Canada

GDC China
12/5 - 12/7 @ Shanghai, China

Asia Game Show 2010
12/24 - 12/27  

GDC 2011
2/28 - 3/4 @ San Francisco, CA

More events...
Quick Stats
64 people currently visiting GDNet.
2406 articles in the reference section.

Help us fight cancer!
Join SETI Team GDNet!
Link to us Events 4 Gamers
Intel sponsors gamedev.net search:

Contents
 Design Principles
 The Window Class
 The Window Class
 and the Application
 Message Pump


 Printable version
 Discuss this article

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!