Classy Windows

How do you create a top-level window on the desktop? For some programmers it’s obvious: use MFC’s CWnd or one of its derived classes such as CFrameWnd or CDialog. I gave up on MFC a long time ago. It’s not bad but it’s certainly not what anyone would consider modern C++. Many programmers moved from MFC to ATL with its CWindowImpl class template. This is certainly modern stuff compared to MFC and there’s nothing wrong with ATL’s windowing classes. It’s lightweight and exceedingly flexible. Still, it adopted MFC’s love of macros and this offends the sensibilities of some who prefer their C++ with a minimum of preprocessing.

I don’t prescribe one technique to rule them all. I actually use three different approaches depending on the circumstance. If I’m teaching and need to describe something that just happens to need a window but is otherwise unrelated then I might just use the Windows API directly. If I’m creating a window with lots of controls then I might use ATL’s CWindowImpl so that I can more easily subclass a window and reflect messages to the appropriate class. These two approaches are radically different. The Windows API accommodates a C style of programming whereas ATL is all about classes. Lately I’ve needed a class for convenience but haven’t needed any of the bells and whistles that ATL provides. For that I wrote a simpler window class template that simply takes care of class-ifying the window procedure and that’s about it.

As I often get asked what approach should be taken I’m going to quickly illustrate all three approaches here and you can choose the one that best suits your needs on any given day. Nothing about this is particularly new but you might find it a helpful refresher on how to get a window on the desktop with C++.

The Windows API

The first approach is the most fundamental. First, register a window class by filling in a WNDCLASS structure and calling the RegisterClass function. Next, create a window based on this class with the CreateWindow or CreateWindowEx function. Finally, pump window messages with the GetMessage and DispatchMessage functions. Here’s a simple example.

int __stdcall wWinMain(HINSTANCE module,
                       HINSTANCE,
                       PWSTR,
                       int)
{
    WNDCLASS wc = {};
    wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
    wc.hInstance = module;
    wc.lpszClassName = L”window”;
    wc.style = CS_HREDRAW | CS_VREDRAW;

    wc.lpfnWndProc = [] (HWND   const window,
                         UINT   const message,
                         WPARAM const wparam,
                         LPARAM const lparam) -> LRESULT
    {
        if (WM_PAINT == message)
        {
            // Render . . .
            VERIFY(ValidateRect(window, nullptr));
            return 0;
        }

        if (WM_DESTROY == message)
        {
            PostQuitMessage(0);
            return 0;
        }

        return DefWindowProc(window,
                             message,
                             wparam,
                             lparam);
    };

    RegisterClass(&wc);

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

    MSG message;

    while (GetMessage(&message,
                      nullptr,
                      0,
                      0))
    {
        DispatchMessage(&message);
    }
}

It’s pretty simple. I’m using a lambda for the window procedure but that’s optional. The point is that there’s nothing classy about this. It’s all just good old C-style programming from top to bottom. Sometimes this is all you need. I find it certainly helps to keep things simple and focused.

ATL’s CWindowImpl

The second approach of using ATL’s CWindowImpl is a little simpler in that you don’t have to fill in a WNDCLASS structure or call CreateWindow directly. But I don’t really regard those as benefits. Inevitably, I want to customize my window’s appearance and behavior and that’s precisely where I’d start. ATL does allow those details to be overridden but it’s a bit less obvious how it’s done. Still, the code is pretty simple and the result is a window class whose instances represent individual windows. The message handlers are member functions and this certainly helps with program structure. ATL also provides a message loop abstraction but you’re not required to use it. Here’s a simple example.

struct Window : CWindowImpl<Window,
                            CWindow,
                            CWinTraits<WS_OVERLAPPEDWINDOW | WS_VISIBLE>>
{
    DECLARE_WND_CLASS_EX(nullptr, 0, -1);

    BEGIN_MSG_MAP(c)
        MESSAGE_HANDLER(WM_PAINT, PaintHandler)
        MESSAGE_HANDLER(WM_DESTROY, DestroyHandler)
    END_MSG_MAP()

    LRESULT PaintHandler(UINT, WPARAM, LPARAM, BOOL &)
    {
        // Render . . .
        VERIFY(ValidateRect(nullptr));
        return 0;
    }

    LRESULT DestroyHandler(UINT, WPARAM, LPARAM, BOOL &)
    {
        PostQuitMessage(0);
        return 0;
    }
};

int __stdcall wWinMain(HINSTANCE,
                       HINSTANCE,
                       PWSTR,
                       int)
{
    Window window;

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

    MSG message;

    while (GetMessage(&message,
                      nullptr,
                      0,
                      0))
    {
        DispatchMessage(&message);
    }
}

Again, it’s pretty simple, but a lot is hidden behind various macros. It’s a good approach if you’re building a complex window based on USER32 controls as there is a lot of help for wiring them all up. If you’re building a modern DirectX app then there’s less value in this approach.

A Simple Window Class Template

The final approach involves a simple window class template that I wrote from scratch. There’s nothing particularly exciting here but it solves a pressing problem. How do I marry a window procedure with an instance of a class? Well, if you’ve ever attempted this and thought you might take a peek at what ATL does you may have been left a little discouraged. ATL employs something called a thunk that involves assembly language to basically rewrite the window procedure to accommodate a “this” pointer. This is certainly very efficient but its messy and very error prone.

Instead of assembly language trickery I can simply store the “this” pointer along with the window structure such that I can simply ask for the pointer given a window handle. This is achieved with the SetWindowLongPtr and GetWindowLongPtr functions.

The challenge is figuring out when to store it and when to retrieve it. I tend to use the WM_NCCREATE message, which is one of the earliest messages to arrive, and carries an app-specific value provided to the CreateWindow or CreateWindowEx functions. Here’s a simple example.

template <typename T>
struct Window
{
    HWND m_window = nullptr;

    static T * GetThisFromHandle(HWND window)
    {
        return reinterpret_cast<T *>(GetWindowLongPtr(window,
                                                      GWLP_USERDATA));
    }

    static LRESULT __stdcall WndProc(HWND   const window,
                                     UINT   const message,
                                     WPARAM const wparam,
                                     LPARAM const lparam)
    {
        ASSERT(window);

        if (WM_NCCREATE == message)
        {
            CREATESTRUCT * cs = reinterpret_cast<CREATESTRUCT *>(lparam);
            T * that = static_cast<T *>(cs->lpCreateParams);

            ASSERT(that);
            ASSERT(!that->m_window);

            that->m_window = window;

            SetWindowLongPtr(window,
                             GWLP_USERDATA,
                             reinterpret_cast<LONG_PTR>(that));
        }
        else if (T * that = GetThisFromHandle(window))
        {
            return that->MessageHandler(message,
                                        wparam,
                                        lparam);
        }

        return DefWindowProc(window,
                             message,
                             wparam,
                             lparam);
    }

    LRESULT MessageHandler(UINT   const message,
                           WPARAM const wparam,
                           LPARAM const lparam)
    {
        if (WM_DESTROY == message)
        {
            PostQuitMessage(0);
            return 0;
        }

        return DefWindowProc(m_window,
                             message,
                             wparam,
                             lparam);
    }
};

A class template is not strictly necessary but I prefer to avoid virtual functions whenever possible. Using a class template allows me to hide this boilerplate code in a base class and use compile-time polymorphism to direct messages to derived classes. A derived class can “override” the MessageHandler without using a virtual function. A derived class is also responsible for actually registering and creating the window. I find this far more convenient as I can control the specifics of window registration and creation. It also resides in the class itself and is plain to see. Here’s an example of a concrete window class based on the window class template above.

struct SampleWindow : Window<SampleWindow>
{
    SampleWindow()
    {
        WNDCLASS wc = {};

        wc.hCursor       = LoadCursor(nullptr, IDC_ARROW);
        wc.hInstance     = reinterpret_cast<HINSTANCE>(&__ImageBase);
        wc.lpszClassName = L”SampleWindow”;
        wc.style         = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc   = WndProc;

        RegisterClass(&wc);

        ASSERT(!m_window);

        VERIFY(CreateWindow(wc.lpszClassName,
                            L”Window Title”,
                            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            CW_USEDEFAULT, CW_USEDEFAULT,
                            nullptr,
                            nullptr,
                            wc.hInstance,
                            this));

        ASSERT(m_window);
    }

    LRESULT MessageHandler(UINT message,
                           WPARAM const wparam,
                           LPARAM const lparam)
    {
        if (WM_PAINT == message)
        {
            PaintHandler();
            return 0;
        }

        return __super::MessageHandler(message,
                                       wparam,
                                       lparam);
    }
    
    void PaintHandler()
    {
        // Render . . .
    }
};

Notice how the m_window inherited member is not initialized before calling CreateWindow but is magically initialized once the call returns. This is a form of reentrancy where the window procedure that you’ve just registered is called a number of times before the CreateWindow function even returns. The window class template takes care of wiring this up in its static window procedure so that the derived window class doesn’t need to think about this too much.

Notice also how the sample window handles WM_PAINT but leaves the base class to handle WM_DESTROY as well as any other messages. Just be careful when deriving further classes. If you decide to go down that route then you’d be better off moving the window creation out of the constructor, otherwise message handlers may be called in derived classes before their constructors have completed. This is essentially the same problem as calling virtual functions from a constructor. Fortunately the solution is simple. Just move the window creation out of the constructor as needed. Anyway, for the example above the program’s main function is nice and simple.

int __stdcall wWinMain(HINSTANCE,
                       HINSTANCE,
                       PWSTR,
                       int)
{
    SampleWindow window;
    MSG message;

    while (GetMessage(&message,
                      nullptr,
                      0,
                      0))
    {
        DispatchMessage(&message);
    }
}

And that’s all there is to it. Three different approaches. Pick the one that best meets the needs of your app.

Now that you have a good idea how to create a desktop window perhaps you’ll want to render your app with DirectX.

Read the next installment in this series: Making a Window DPI-Aware

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s