Overview of Win32 Apps

Before we touch DirectX, we need a window. Here's a tour of how Win32 desktop apps are structured — entry points, message loops, and the event-driven model.

Early Draft Work in progress — GitHub links coming soon.

Every native Windows graphics application — whether it uses DirectX, OpenGL, or Vulkan — sits on top of the Win32 API. Before we can render a single pixel, we need to understand the host environment that owns our window, dispatches input, and routes events between the OS and our code.

You do not need to know the whole Win32 API to follow along. For this series, the goal is much smaller: understand just enough of the windowing layer to create a window, keep it alive, react to resize and input events, and give DirectX a place to present rendered frames.

Here is the practical reason this matters: when the user resizes your window, Windows sends your app a WM_SIZE message. Later, your DX12 code will respond by resizing its swap chain back buffers. When the user closes the window, Windows sends close and destroy messages. Your app needs to turn those into a clean shutdown. Win32 is the layer where those moments enter your program.

What You Should Know First

This article assumes you are comfortable with basic C++: functions, structs, pointers or references, and switch statements. You do not need prior Windows programming experience. If terms like HWND, HINSTANCE, or WndProc look strange, that is expected — we will define them as we go.

You also do not need to understand every corner of Windows desktop programming. We will skip menus, dialogs, GDI drawing, accelerators, COM, DPI scaling, threading, and most of the thousands of messages Win32 supports. Those are real topics, but they are not required to open a window for a graphics program.

The Big Picture

A Win32 desktop app is built around three things:

  1. An entry point (WinMain) where execution starts.
  2. A window, which is a kernel object the OS owns and your app holds a handle to (HWND).
  3. A message loop that pulls events out of a thread-local queue and dispatches them to a function you write (the window procedure).

Unlike a console program that runs top-to-bottom and exits, a Win32 app spends almost its entire life inside the message loop, reacting to events.

The rough lifecycle looks like this:

WinMain starts

Register a window class

Create a window

Show the window

Run the message loop

WndProc handles messages

Quit when the window is destroyed

If you remember only one idea from this page, remember this: a Windows app is not a single straight line of code. It is a small setup phase followed by a loop that waits for messages from the operating system.

Another way to divide it:

A Small Win32 Glossary

Win32 names can feel cryptic at first because many of them come from older C-style APIs. Here are the terms you will see constantly:

WinMain vs main

Console apps use int main(...). GUI apps use:

int WINAPI WinMain(
    HINSTANCE hInstance,        // handle to this app's loaded module
    HINSTANCE hPrevInstance,    // legacy, always nullptr on modern Windows
    LPSTR     lpCmdLine,        // command line as ANSI
    int       nCmdShow);        // initial window show state

hInstance is the most important parameter — it’s how the OS identifies your running module, and you’ll pass it to nearly every window-creation API.

The all-caps words in the signature are old Windows calling conventions and typedefs:

You do not need to memorize these right now. Treat them as part of the required shape of Win32 functions.

The Event-Driven Model

Win32 is push-based. The OS doesn’t call your code directly when the user clicks; it posts a message (a small struct with a code and two parameters) into your thread’s message queue. Your message loop pulls those messages out one at a time and routes each to the right window’s procedure.

In plain English, your app does not constantly ask, “Did the user click? Did the user resize? Did the user type?” Instead, Windows leaves messages in a queue, and your app reads them one at a time:

Win32 message pump

Messages move through your app one packet at a time

queue WM_MOUSEMOVEWM_SIZEWM_KEYDOWN
WM_MOUSEMOVE1Windows posts2Queue waits3Loop reads4WndProc runs
Windows posts Input, resize, paint, and close events become messages. The cursor moved over the window.

The message loop is the code that performs that read-and-dispatch work:

MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0) > 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

GetMessage waits until a message is available. TranslateMessage does some keyboard-message translation, and DispatchMessage sends the message to the correct window procedure. In a real-time renderer, we will usually use PeekMessage instead so the app can keep rendering even when there is no input. The next article shows that version.

A window procedure is just a function with a fixed signature:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_DESTROY:
            PostQuitMessage(0);
            return 0;
        // ... handle other messages
    }
    return DefWindowProc(hwnd, msg, wParam, lParam);
}

The big switch statement is the heart of every Win32 app. DefWindowProc is the default handler — anything you don’t care about, you forward to it so Windows can do the right thing (close the window on Alt+F4, repaint when needed, etc.).

One useful way to read WndProc is: “For the messages I understand, do my custom behavior. For everything else, let Windows handle it normally.”

Common Messages You’ll See

A few messages come up constantly when building a graphics app:

Where DirectX Fits In

DX12 doesn’t render to the screen directly. It renders to off-screen buffers called the back buffers of a swap chain. The swap chain is connected to your Win32 window through its HWND.

The flow looks like this:

Your DX12 commands render into a back buffer

The swap chain owns that back buffer

You call Present()

Windows' desktop compositor receives the finished image

The image appears inside your window on the monitor

So the Win32 window is the bridge between your GPU work and the user’s screen. DirectX produces images, but Win32 gives those images a real place to appear.

Things That Feel Weird at First

If this is your first Win32 program, a few details may feel backwards:

Once those ideas click, Win32 becomes much less mysterious. You set up a window, keep pumping messages, handle the few events your graphics app cares about, and let Windows handle the rest.

Quick Checkpoint

You are ready for the next article if these ideas make sense:

What’s Next

Now that you understand the structure, the next post walks through actually creating one — registering a window class, calling CreateWindowEx, and writing a working message loop. From there we’ll be ready to bring DX12 into the picture.