February 2013

Volume 28 Number 02

Windows with C++ - Creating Desktop Apps with Visual C++ 2012

By Kenny Kerr | February 2013

Kenny KerrWith all the hype over Windows 8 and what are now known as Windows Store apps, I’ve received some questions about the relevance of desktop apps and whether Standard C++ is still a viable choice going forward. These questions are sometimes hard to answer, but what I can tell you is that the Visual C++ 2012 compiler is more committed than ever to Standard C++ and it remains the best toolchain, in my humble opinion, for building great desktop apps for Windows whether you’re targeting Windows 7, Windows 8 or even Windows XP.

A follow-up question I inevitably receive is how best to approach desktop app development on Windows and where to begin. Well, in this month’s column, I’m going to explore the fundamentals of creating desktop apps with Visual C++. When I was first introduced to Windows programming by Jeff Prosise (bit.ly/WmoRuR), Microsoft Foundation Classes (MFC) was a promising new way to build apps. While MFC is still available, it really is showing its age, and a need for modern and flexible alternatives has driven programmers to search for new approaches. This issue has been compounded by a shift away from USER and GDI (msdn.com/library/ms724515) resources and toward Direct3D as the primary foundation by which content is rendered on the screen.

For years I’ve been promoting the Active Template Library (ATL) and its extension, the Windows Template Library (WTL), as great choices for building apps. However, even these libraries are now showing signs of aging. With the shift away from USER and GDI resources, there’s even less reason to use them. So where to begin? With the Windows API, of course. I’ll show you that creating a desktop window without any library at all isn’t actually as daunting as it might seem at first. I’ll then show you how you can give it a bit more of a C++ flavor, if you so desire, with a little help from ATL and WTL. ATL and WTL make a lot more sense once you have a good idea of how it all works behind the templates and macros.

The Windows API

The trouble with using the Windows API to create a desktop window is that there are myriad ways you could go about writing it—far too many choices, really. Still, there’s a straightforward way to create a window, and it starts with the master include file for Windows:

#include <windows.h>

You can then define the standard entry point for apps:

int __stdcall wWinMain(HINSTANCE module, HINSTANCE, PWSTR, int)

If you’re writing a console app, then you can just continue to use the standard C++ main entry point function, but I’ll assume that you don’t want a console box popping up every time your app starts. The wWinMain function is steeped in history. The __stdcall calling convention clarifies matters on the confusing x86 architecture, which provides a handful of calling conventions. If you’re targeting x64 or ARM, then it doesn’t matter because the Visual C++ compiler only implements a single calling convention on those architectures—but it doesn’t hurt, either.

The two HINSTANCE parameters are particularly shrouded in history. In the 16-bit days of Windows, the second HINSTANCE was the handle to any previous instance of the app. This allowed an app to communicate with any previous instance of itself or even to switch back to the previous instance if the user had accidentally started it again. Today, this second parameter is always a nullptr. You may also have noticed that I named the first parameter “module” rather than “instance.” Again, in 16-bit Windows, instances and modules were two separate things. All apps would share the module containing code segments but would be given unique instances containing the data segments. The current and previous HINSTANCE parameters should now make more sense. 32-bit Windows introduced separate address spaces and along with that the necessity for each process to map its own instance/module, now one and the same. Today, this is just the base address of the executable. The Visual C++ linker actually exposes this address through a pseudo variable, which you can access by declaring it as follows:

extern "C" IMAGE_DOS_HEADER __ImageBase;

The address of __ImageBase will be the same value as the HINSTANCE parameter. This is in fact the way that the C Run-Time Library (CRT) gets the address of the module to pass to your wWinMain function in the first place. It’s a convenient shortcut if you don’t want to pass this wWinMain parameter around your app. Keep in mind, though, that this variable points to the current module whether it’s a DLL or an executable and is thus useful for loading module-specific resources unambiguously.

The next parameter provides any command-line arguments, and the last parameter is a value that should be passed to the ShowWindow function for the app’s main window, assuming you’re initially calling ShowWindow. The irony is that it will almost always be ignored. This goes back to the way in which an app is launched via CreateProcess and friends to allow a shortcut—or some other app—to define whether an app’s main window is initially minimized, maximized or shown normally.

Inside the wWinMain function, the app needs to register a window class. The window class is described by a WNDCLASS structure and registered with the RegisterClass function. This registration is stored in a table using a pair made up of the module pointer and class name, allowing the CreateWindow function to look up the class information when it’s time to create the window:

WNDCLASS wc = {};
wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
wc.hInstance = module;
wc.lpszClassName = L"window";
wc.lpfnWndProc = []
 (HWND window, UINT message, WPARAM wparam, 
    LPARAM lparam) -> LRESULT
{
  ...
};
VERIFY(RegisterClass(&wc));

To keep the examples brief, I’ll just use the common VERIFY macro as a placeholder to indicate where you’ll need to add some error handling to manage any failures reported by the various API functions. Just consider these as placeholders for your preferred error-handling policy.

The earlier code is the minimum that’s required to describe a standard window. The WNDCLASS structure is initialized with an empty pair of curly brackets. This ensures that all the structure’s members are initialized to zero or nullptr. The only members that must be set are hCursor to indicate which mouse pointer, or cursor, to use when the mouse is over the window; hInstance and lpszClassName to identify the window class within the process; and lpfnWndProc to point to the window procedure that will process messages sent to the window. In this case, I’m using a lambda expression to keep everything inline, so to speak. I’ll get back to the window procedure in a moment. The next step is to create the window:

VERIFY(CreateWindow(wc.lpszClassName, L"Title",
  WS_OVERLAPPEDWINDOW | WS_VISIBLE,
  CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
  nullptr, nullptr, module, nullptr));

The CreateWindow function expects quite a few parameters, but most of them are just defaults. The first and second-to-last parameters, as I mentioned, together represent the key that the RegisterClass function creates to let CreateWindow find the window class information. The second parameter indicates the text that will be displayed in the window’s title bar. The third indicates the window’s style. The WS_OVERLAPPEDWINDOW constant is a commonly used style describing a regular top-level window with a title bar with buttons, resizable borders and so on. Combining this with the WS_VISIBLE constant instructs CreateWindow to go ahead and show the window. If you omit WS_VISIBLE, then you’ll need to call the ShowWindow function before your window will make its debut on the desktop.

The next four parameters indicate the window’s initial position and size, and the CW_USEDEFAULT constant used in each case just tells Windows to choose appropriate defaults. The next two parameters provide the handle to the window’s parent window and menu, respectively (and neither are needed). The final parameter provides the option of passing a pointer-sized value to the window procedure during creation. If all goes well, a window appears on the desktop and a window handle is returned. If things go south, then nullptr is returned instead and the GetLastError function may be called to find out why. With all the talk about the hardships of using the Windows API, it turns out that creating a window is actually quite simple and boils down to this:

WNDCLASS wc = { ... };
RegisterClass(&wc);
CreateWindow( ... );

Once the window appears, it’s important that your app starts dispatching messages as soon as possible—otherwise your app will appear unresponsive. Windows is fundamentally an event-driven, message-based OS. This is particularly true of the desktop. While Windows creates and manages the queue of messages, it’s the app’s responsibility to dequeue and dispatch them, because messages are sent to a window’s thread rather than directly to the window. This provides a great deal of flexibility, but a simple message loop need not be complicated, as shown here:

MSG message;
BOOL result;
while (result = GetMessage(&message, 0, 0, 0))
{
  if (-1 != result)
  {
    DispatchMessage(&message);
  }
}

Perhaps not surprisingly, this seemingly simple message loop is often implemented incorrectly. This stems from the fact that the GetMessage function is prototyped to return a BOOL value, but in fact, this is really just an int. GetMessage dequeues, or retrieves, a message from the calling thread’s message queue. This may be for any window or no window at all, but in our case, the thread is only pumping messages for a single window. If the WM_QUIT message is dequeued, then GetMessage will return zero, indicating that the window has disappeared and is done processing messages and that the app should terminate. If something goes terribly wrong, then GetMessage might return -1 and you can again call GetLastError to get more information. Otherwise, any nonzero return value from GetMessage indicates that a message was dequeued and is ready to be dispatched to the window. Naturally, this is the purpose of the DispatchMessage function. Of course, there are many variants to the message loop, and having the ability to construct your own affords you many choices for how your app will behave, what input it will accept and how it will be translated. Apart from the MSG pointer, the remaining parameters to GetMessage can be used to optionally filter messages.

The window procedure will start receiving messages before the CreateWindow function even returns, so it had better be ready and waiting. But what does that look like? A window requires a message map or table. This could literally be a chain of if-else statements or a big switch statement inside the window procedure. This does, however, quickly become unwieldy, and much effort has been spent in different libraries and frameworks to try to manage this somehow. In reality, it doesn’t have to be anything fancy, and a simple static table will suffice in many cases. First, it helps to know what a window message consists of. Most importantly, there’s a constant—such as WM_PAINT or WM_SIZE—that uniquely identifies the message. Two arguments, so to speak, are provided for every message, and these are called WPARAM and LPARAM, respectively. Depending on the message, these might not provide any information. Finally, Windows expects the handling of certain messages to return a value, and this is called the LRESULT. Most messages that your app handles, however, won’t return a value and should instead return zero.

Given this definition, we can build a simple table for message handling using these types as building blocks:

typedef LRESULT (* message_callback)(HWND, WPARAM, LPARAM);
struct message_handler
{
  UINT message;
  message_callback handler;
};

At a minimum, we can then create a static table of message handlers, as shown in Figure 1.

Figure 1 A Static Table of Message Handlers

static message_handler s_handlers[] =
{
  {
    WM_PAINT, [] (HWND window, WPARAM, LPARAM) -> LRESULT
    {
      PAINTSTRUCT ps;
      VERIFY(BeginPaint(window, &ps));
      // Dress up some pixels here!
      EndPaint(window, &ps);
      return 0;
    }
  },
  {
    WM_DESTROY, [] (HWND, WPARAM, LPARAM) -> LRESULT
    {
      PostQuitMessage(0);
      return 0;
    }
  }
};

The WM_PAINT message arrives when the window needs painting. This happens far less often than it did in earlier versions of Windows thanks to advances in rendering and composition of the desktop. The BeginPaint and EndPaint functions are relics of the GDI but are still needed even if you’re drawing with an entirely different rendering engine. This is because they tell Windows that you’re done painting by validating the window’s drawing surface. Without these calls, Windows wouldn’t consider the WM_PAINT message answered and your window would receive a steady stream of WM_PAINT messages unnecessarily.

The WM_DESTROY message arrives after the window has disappeared, letting you know that the window is being destroyed. This is usually an indicator that the app should terminate, but the GetMessage function inside the message loop is still waiting for the WM_QUIT message. Queuing this message is the job of the PostQuitMessage function. Its single parameter accepts a value that’s passed along via WM_QUIT’s WPARAM, as a way to return different exit codes when terminating the app.

The final piece of the puzzle is to implement the actual window procedure. I omitted the body of the lambda that I used to prepare the WNDCLASS structure previously, but given what you now know, it shouldn’t be hard to figure out what it might look like:

wc.lpfnWndProc =
  [] (HWND window, UINT message,
      WPARAM wparam, LPARAM lparam) -> LRESULT
{
  for (auto h = s_handlers; h != s_handlers +
    _countof(s_handlers); ++h)
  {
    if (message == h->message)
    {
      return h->handler(window, wparam, lparam);
    }
  }
  return DefWindowProc(window, message, wparam, lparam);
};

The for loop looks for a matching handler. Fortunately, Windows provides default handling for messages that you choose not to process yourself. This is the job of the DefWindowProc function.

And that’s it—if you’ve gotten this far, you’ve successfully created a desktop window using the Windows API!

The ATL Way

The trouble with these Windows API functions is that they were designed long before C++ became the smash hit that it is today, and thus weren’t designed to easily accommodate an object-oriented view of the world. Still, with enough clever coding, this C-style API can be transformed into something a little more suited to the average C++ programmer. ATL provides a library of class templates and macros that do just that, so if you need to manage more than a handful of window classes or still rely on USER and GDI resources for your window’s implementation, there’s really no reason not to use ATL. The window from the previous section can be expressed with ATL as shown in Figure 2.

Figure 2 Expressing a Window in ATL

class Window : public CWindowImpl<Window, CWindow,
  CWinTraits<WS_OVERLAPPEDWINDOW | WS_VISIBLE>>
{
  BEGIN_MSG_MAP(Window)
    MESSAGE_HANDLER(WM_PAINT, PaintHandler)
    MESSAGE_HANDLER(WM_DESTROY, DestroyHandler)
  END_MSG_MAP()
  LRESULT PaintHandler(UINT, WPARAM, LPARAM, BOOL &)
  {
    PAINTSTRUCT ps;
    VERIFY(BeginPaint(&ps));
    // Dress up some pixels here!
    EndPaint(&ps);
    return 0;
  }
  LRESULT DestroyHandler(UINT, WPARAM, LPARAM, BOOL &)
  {
    PostQuitMessage(0);
    return 0;
  }
};

The CWindowImpl class provides the necessary routing of messages. CWindow is a base class that provides a great many member function wrappers, mainly so you don’t need to provide the window handle explicitly on every function call. You can see this in action with the BeginPaint and EndPaint function calls in this example. The CWinTraits template provides the window style constants that will be used during creation.

The macros harken back to MFC and work with CWindowImpl to match incoming messages to the appropriate member functions for handling. Each handler is provided with the message constant as its first argument. This can be useful if you need to handle a variety of messages with a single member function. The final parameter defaults to TRUE and lets the handler decide at run time whether it actually wants to process the message or let Windows—or even some other handler—take care of it. These macros, along with CWindowImpl, are quite powerful and let you handle reflected messages, chain message maps together and so on.

To create the window, you must use the Create member function that your window inherits from CWindowImpl, and this in turn will call the good old RegisterClass and CreateWindow functions on your behalf:

Window window;
VERIFY(window.Create(nullptr, 0, L"Title"));

At this point, the thread again needs to quickly begin dispatching messages, and the Windows API message loop from the previous section will suffice. The ATL approach certainly comes in handy if you need to manage multiple windows on a single thread, but with a single top-level window, it’s much the same as the Windows API approach from the previous section.

WTL: An Extra Dose of ATL

While ATL was designed primarily to simplify the development of COM servers and only provides a simple—yet extremely effective—window-handling model, WTL consists of a slew of additional class templates and macros specifically designed to support the creation of more-complex windows based on USER and GDI resources. WTL is now available on SourceForge (wtl.sourceforge.net), but for a new app using a modern rendering engine, it doesn’t provide a great deal of value. Still, there are a handful of useful helpers. From the WTL atlapp.h header, you can use its message loop implementation to replace the hand-rolled version I described earlier:

CMessageLoop loop;
loop.Run();

Although it’s simple to drop into your app and use, WTL packs a lot of power if you have sophisticated message filtering and routing needs. WTL also provides atlcrack.h with macros designed to replace the generic MESSAGE_HANDLER macro provided by ATL. These are merely conveniences, but they do make it easier to get up and running with a new message because they take care of cracking open the message, so to speak, and avoid any guesswork in figuring out how to interpret WPARAM and LPARAM. A good example is WM_SIZE, which packs the window’s new client area as the low- and high-order words of its LPARAM. With ATL, this might look as follows:

BEGIN_MSG_MAP(Window)
  ...
  MESSAGE_HANDLER(WM_SIZE, SizeHandler)
END_MSG_MAP()
LRESULT SizeHandler(UINT, WPARAM, 
  LPARAM lparam, BOOL &)
{
  auto width = LOWORD(lparam);
  auto height = HIWORD(lparam);
  // Handle the new size here ...
  return 0;
}

With the help of WTL, this is a little simpler:

BEGIN_MSG_MAP(Window)
  ...
  MSG_WM_SIZE(SizeHandler)
END_MSG_MAP()
void SizeHandler(UINT, SIZE size)
{
  auto width = size.cx;
  auto height = size.cy;
  // Handle the new size here ...
}

Notice the new MSG_WM_SIZE macro that replaced the generic MESSAGE_HANDLER macro in the original message map. The member function handling the message is also simpler. As you can see, there aren’t any unnecessary parameters or a return value. The first parameter is just the WPARAM, which you can inspect if you need to know what caused the change in size.

The beauty of ATL and WTL is that they’re just provided as a set of header files that you can include at your discretion. You use what you need and ignore the rest. However, as I’ve shown you here, you can get quite far without relying on any of these libraries and just write your app using the Windows API. Join me next time, when I’ll show you a modern approach for actually rendering the pixels in your app’s window.


Kenny Kerris a computer programmer based in Canada, an author for Pluralsight and a Microsoft MVP. He blogs at kennykerr.ca and you can follow him on Twitter at twitter.com/kennykerr.

Thanks to the following technical expert for reviewing this article: Worachai Chaoweeraprasit