«

»

Jul
24
2012

Simple Main Window Class

Here’s another fairly trivial code snippet. I’ve stumbled across some borked attempts at initializing and maintaining rendering windows for games lately. Most failed to properly respond to window messages, either ignoring WM_CLOSE outright or letting DefWindowProc() call DestroyWindow() when WM_CLOSE was received, thereby not giving the rest of the game’s code any time to cleanly shut down before the window handle becomes invalid.

So I’ll provide a clean and well-behaved window class here. It doesn’t use any global variables – in fact, you could create any number of windows from any number of threads. WM_CLOSE simply causes the class’ WasCloseRequested() method to return true, so by polling this method you can first shut down graphics and input devices and then destroy the window in an orderly fashion.

For your convenience I also added some helper methods: one resizes the window in a way that ensures the client area will actually end up with the exact pixel size requested. Another will center the window on the screen without messing up if the user has extended his desktop over multiple monitors.

Usage example:

 

/// <summary>Main entry point for the application</summary>
/// <param name="instanceHandle">Instance handle of the new process</param>
/// <param name="previousInstanceHandle">Instance handle of the previous process</param>
/// <param name="commandLine">arguments provided to the application on the command line</param>
/// <param name="showWindow">Desired initial state of the application's window</param>
/// <returns>Zero on success</returns>
int WINAPI WinMain(
  HINSTANCE instanceHandle, HINSTANCE previousInstanceHandle,
  LPSTR commandLine, int showWindow
) {

  // This creates a new window, using the default window size and position.
  // It starts invisible, so you can tweak the window before showing it.
  Window mainWindow(instanceHandle, L"My Game");

  // You can resize the window and it will end up with its drawable region
  // having the exact size you requested
  mainWindow.ResizeViewRectangle(800, 600);
  
  // Centering the window can be done with a single call and it will for once
  // not mess up if your game is run on a workstation with multiple monitors.
  mainWindow.CenterOnPrimaryDisplay();
  
  // After you're done tweaking the window, you show it
  mainWindow.Show();
  
  // Run the message loop. When the user tries to close the window by clicking
  // on 'X' or pressing Alt+F4, your game is informed via WasCloseRequested()
  // and can perform an orderly shutdown.
  while(!mainWindow.WasCloseRequested()) {
    MSG message;
    while(::PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {
      if(message.message == WM_QUIT) {
        return message.wParam;
      }

      ::TranslateMessage(&message);
      ::DispatchMessage(&message);
    }
  }
  
  // As the window goes out of scope, it gets destroyed and everything will
  // be cleaned up.
  return 0;

}  

 

Download

Download

Nuclex.SimpleMainWindow.Example.7z (14.2 KiB)

Includes Visual C++ 2010 project files and an example application.

7 comments

  1. Darren Evans says:

    Awesome. Thanks for this and tall the other tutorials on your blog.

    I’m slowly learning Win32 game programming and this discussion and sample code looks to be the perfect starting point for my dabbling. I’m still a bit of a programmer noob so I’m struggling to fully grok your code at this point but at least I can plug in my demo code into it without fully understanding how it works until I get more C++ knowledge under my belt :)

  2. Cygon says:

    Thanks!

    The only fancy stuff should be the two classes in Window.cpp. The WindowClassUnregisterer follows the RAII pattern (and the Dismiss() method is inspired by Andrei Alexandrescu’s ScopeGuard). The PointerAppender was originally just an if(sizeof(void *) == 4) {} but on /W4 Visual C++ complained that the if will always evaluate to true, so I used a template specialized on the size of a pointer instead.

    It’s all just good, modern C++, never trying to be fancy, though :)

  3. Darren Evans says:

    In reading some books on game programming I kept coming across all that boiler-plate Win32 window and message loop code examples and immediately thought to encapsulate it into a class as an exercise to see if all this ‘object-oriented’ I keep reading about is sinking in. I’m still a bit rooted in the old procedural way of thinking from my Atari ST days :)

    I soon encountered that problem of the window procedure method not playing nice in any derived classes though and found myself researching into the problem. I found quite a few sites discussing the issue and am currently trying to wrap my head around the problem with the help of the discussion @ http://tinyurl.com/d72pgbr.

    I’m only just getting comfortable with the C++ language syntax and object-oriented way of designing things though so I’m not entirely ‘getting the problem’ yet. I did have an immediate gut-reaction to all the ‘Win32 window wrapper’ class tricks though and that was – “this doesn’t seem thread-safe”. I may be naively wrong here though :)

    Then I found your implementation of the Simple Main Window Class. It immediately ‘spoke’ to me because you were using modern C++ 11 language features. I’m a late starter remember and I’m coming to this with C++ 11 RTM out the door, so it’s my default learning tool (out with the old, in with the new etc). You also stated it was thread-safe, whereas the other ‘window wrapper class’ discussions seem quite dated and do not explicitly state this and I’m too inexperienced to determine if this is so or not. My naive, gut-feeling tells me they’re not.

    About your ‘pointer appender’ templates too. All discussions of templates I see in books simply use examples where some notation is replaced in the template-generated code by whatever type you speclalise it with. Your usage in the appender class didn’t seem to be following this pattern and was just being given a number to specialise it and I couldn’t quite follow what was happening under the hood. I had an inkling it was something to do with the number of bytes in 32 and 64 bit pointers though. I also get the impression that you’re using this technique to generate unique window class string names to pass into the WNDCLASS structure? Is this correct? Also, is the ability to specialise the appender class to 4 or 8 bytes to support situations where an application is to be compiled as 32-bit or 64-bit? It does seem like a very elegant way to accomplish things.

    Hopefully it won’t be long until I start internalising all the C++ language (and native Win32) elements and get to the point where I can look at other peoples code and see what all these code constructs are accomplishing with minimal effort :)

    Thanks for sharing the rationale behind WindowClassUnregisterer. I recently watched Bjarne Stroustrup’s talk on the RAII idiom on Channel 9′s Going Native talks so your usage makes more sense now. I didn’t know about Alexandrescu’s ScopeGuard though so I’ll be Googling that next. I do have his ‘Modern C++ Design) book as a reference but the topics are a bit beyond me at the moment.

  4. Cygon says:

    Lots of code is not thread-safe, even when it claims it is :)

    You’re right about the PointerAppender, it converts pointers into hex numbers and appends them to a string in order to generate unique window class names for each instance of my class.

    Depending on whether sizeof(void *) evaluates to 4 (32 bit) or 8 (64 bit), the matching specialization is used, calling either ultow() or ui64tow(). For any other pointer sizes it would fall back to the non-specialized template, thereby causing a compilation error and forcing the programmer to add a code path for his pointer size.

    I would have greatly preferred a plain if to all that code, but aforementioned compiler warning prevented me from taking the easy way out :p

  5. DissidentRage says:

    Don’t call it a class if it’s not a class. What you have here is a generic Windows program written entirely in the main method – thanks for being one of the million people erroneously naming your static Windows program a class and contributing to my 6 hour search for an ACTUAL window class.

  6. Cygon says:

    Let me guess, you looked for 2 seconds at the example code showing how to use this main window class and felt it appropriate to shout at me. Gee, thanks.

    There’s a download link under the code. It’s probably the cleanest and most well designed implementation of a window class you’ve come across in your 6 hour search, but this being the internet, you likely won’t be back to find out.

  7. Jose Ladislao Lainez Ortega says:

    Why don’t use SDL2? Creating a Windows is 2 lines of code and has all the event management too. Also it is multi-platform, so your game can be potentially ported to Linux and Mac.

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Please copy the string dvzbTf to the field below:

Social Widgets powered by AB-WebLog.com.