(Enable Javascript for nicer formatting. Will make this server-side in the future.)

Table of Contents

Windows: Important Notes

Ansi and Unicode

All functions/structures which take string parameters/have string fields have an A and a W version. For example,

WNDCLASS
WNDCLASSA
WNDCLASSW

A is for Ansi encoding, while W is for Unicode encoding. If neither an A or a W is specified, the macro will decide which one to use depending on how your program is compiled. For these tutorials, I will be using the Ansi version of the functions and structures.

Hungarian Notation

Microsoft uses Hungarian notation used when naming structures or parameters for functions. You can view them here.

Windows: The Main Function

Prerequisites

For every application you write for Windows, you will most likely want to include the Windows header.

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

Simple enough! Although not required, you can put that define before the include. It prevents plenty of headers, many of which you probably do not need, from being included in your file. This can speed up the compilation process.

The Function

We all know that every great program starts within a main function. For a Win32 application, this function must be defined as:

int WinMain(
  // The handle to the current instance of the application. You will use this handle to create a window for your game.
  HINSTANCE hInstance,
  // The handle to the previous instance. This will always be 0 for modern applications.
  HINSTANCE hPrevInstance,
  // The long pointer to the string of characters which were passed in from the command line.
  LPSTR lpCmdLine,
  // A very obscure parameter. If you right click an executable and click properties,
  // you will notice there is a property called "Run." That is what determines this value.
  int nShowCmd
);

Trying It Out

Let's test what we have done so far. A nice, simple function is the MessageBox function. Reading the documentation, we can fill this out as follows:

MessageBox(0, "Text", "Title", MB_OK|MB_ICONINFORMATION);

Place this in your main function, run your program, and hopefully you will see the dialog box pop up!

So far, our code looks like this:

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
  MessageBox(0, "Text", "Title", MB_OK|MB_ICONINFORMATION);
}

Have some fun with the parameters of this function. Once you're done, move onto the next section.

Windows: Creating a Window

There are a couple of steps to create a window, all not too complex.

WNDCLASS

You first need to fill out a WNDCLASS structure. An example of how one could fill it out is:

WNDCLASS wc = {0};

// You can find this passed into your WinMain function.
wc.hInstance = hInstance;
// We will get to this field next.
wc.lpfnWndProc = 0;
// A unique identifier to your window class. Name this whatever you'd like.
wc.lpszClassName = "MyGameWindow";

Window Proc

You will have noticed we defied lpfnWndProc as 0, though in practice you never want to do this. This is a pointer to the function which will handle all window messages, such as moving, resizing, maximising, keyboard input and much more. This function must defined as:

LRESULT WindowProc(
  // The handle to the window to which this message corresponds to.
  HWND hwnd,
  // The message ID.
  UINT uMsg,
  // A variable parameter, its value dependant on the message.
  WPARAM wParam,
  // Another variable parameter, its value also dependant on the message.
  LPARAM lParam
);

If we look at the number of messages there are, it's clear that there are way too many for us to implement! Luckily, we have a magical function which we can use to handle messages. This is the DefWindowProcA function. We can input the values which are passed in from our definition of the WindowProc function into this function.

Do not forget to return the value of the function! Otherwise, the window will appear to be unresponsive.

Now that we have our stuff set up, we can use our function as a pointer in our WNDCLASS structure:

LRESULT win32_windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
  return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

// ...

wc.lpfnWndProc = win32_windowProc;

It is possible to simply pass in a pointer to the DefWindowProc function as the lpfnWndProc field, though this is limiting as in the future we will want to handle some of the messages which are passed.

Registering a WNDCLASS

Now, we must register the window class. It is simply done with this function:

// Pass in a pointer of our WNDCLASS structure.
RegisterClassA(&wc);

Actually Creating The Window

Finally, we have everything set up to create the window! This is done with the CreateWindowExA function:

HWND window = CreateWindowExA(
  // No need for extra styles.
  0,
  // Use the class name from your window class!
  wc.lpszClassName,
  // The title of the window
  "My game",
  // How should the window look? This value is a combination of a bunch of values to create the default window look.
  // We also want the window to be visible on creation.
  WS_OVERLAPPEDWINDOW|WS_VISIBLE,
  // 4 default parameters: x position, y position, width, and height of the window.
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  CW_USEDEFAULT,
  // We have no parent window.
  0,
  // We don't want a menu (i.e. the File, Edit, Format, View, Help bar in Notepad)
  0,
  // The instance of our program.
  hInstance,
  // A value to be passed into the lParam parameter of our window proc function with a message ID of WM_CREATE. We don't need this.
  0
);

We're almost there! The final thing we need is a loop to handle all window messages from the message queue.

while (1) {
  MSG message;
  while (PeekMessageA(&message, window, 0, 0, PM_REMOVE)) {
    TranslateMessage(&message);
    DispatchMessage(&message);
  }
}

TODO msg link and PeekMessageA

The MSG structure allows you to store the current window message to be handled. PeekMessageA gets the next message in the window message queue without holding up the thread. GetMessageA does a similar job, however it holds the thread until a new message is received, if there are none already available.

TODO links

TranslateMessage and DispatchMessage

If you've done everything right, you will see the most beautiful white window. (Or, if you're running windows 7, probably black?)