Win32 API Basics

Inside WinMain

//--------------------------------------------------------------------------------------
// Entry point to the program. Initializes everything and goes into a message processing
// loop. Idle time is used to render the scene.
//--------------------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
    WNDCLASSEX wcex;
    HWND hwnd;
    MSG Msg;

    // Register the Window
    wcex.cbSize        = sizeof(WNDCLASSEX);
    wcex.style         = 0;
    wcex.lpfnWndProc   = WndProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = hInstance;
    wcex.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName  = NULL;
    wcex.lpszClassName = g_szClassName;
    wcex.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wcex))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "Window Title",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    // The Message Loop
    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

Register Class and Create Window

WINDCLASSEX

typedef struct tagWNDCLASSEXA {
  UINT      cbSize;  // structure size
  UINT      style;
  WNDPROC   lpfnWndProc;  // pointer to window procedure
  // extra bytes
  int       cbClsExtra; // class structure
  int       cbWndExtra; // window instance
  HINSTANCE hInstance;  // handle to the instance that contains winproc() for class
  HICON     hIcon;
  HCURSOR   hCursor;
  HBRUSH    hbrBackground;
  LPCSTR    lpszMenuName;  // resource name of class menu
  LPCSTR    lpszClassName; // window class name
  HICON     hIconSm; // small icon
} WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;

The WINDCLASSEX structure will contain information about the window's fields i.e. the pointer to the Window Procedure which controls the window, window and class extra allocated memory, small and large icons for the window, the window’s background color, window styles, the window’s cursor, the menu resource name, its own name, the instance’s handle (to read/write this resource) and the size of the class itself.

An instance of WINDCLASSEX will be used with RegisterClassEx to register a window and for subsequent calls in CreateWindow.

hInstance serves as a resource handle for the current instance of the application. It is needed to examine or modify a system resource e.g. when creating the window.

Note

It is useful for creating as many of the same window (with same fields) necessary, by having to only to register a particular WINDCLASSEX object once.

Would be better to separate registering and creating the window and place inside a InitWindow function, which is called within WinMain. See this link!

TODO : ADD LINK ^^^

Creating the Window

CreateWindowEx

Passing g_szClassName(the name of the WINDCLASSEX object) into CreateWindowEx tells the system to use g_szClassName's associated attributes/fields to create a window (as in, g_szClassName points to which window identity to create).

Returns HWND, a Windows data type that is a handle to a window object.

Show the Window

ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);

After creating the window it is necessary to call the ShowWindow function. This function specifies the state of a specific window (through the window handler i.e. hwnd).

The state is determined by the value of nCmdShow. For example, when nCmdShow has the integer value 3, this maximizes the specific window.

The UpdateWindow function updates the client area of the specified window by sending a WM_PAINT message to the window if the window's update region is not empty. The function sends a WM_PAINT message directly to the window procedure of the specified window, bypassing the application queue. If the update region is empty, no message is sent.

The Message Loop

GetMessage

Retrieves the next available system/application generated message (e.g. may contain information about mouse movement, or keyboard input) from the front of the application's (calling thread's) message queue - FIFO order.

Copies the message to structure of type MSG.

These messages are placed on the message queue (specific to a thread of a window) by the PostMessage function.

Returns a non-zero values unless WM_QUIT is encountered. If an error is occurs, GetMessage will return -1.

Note

A BOOL is defined as a unsigned int. True would equate to 1 while false would be 0.

PeekMessage

Similar to GetMessage.

Both are used to check for a message on the queue that matches the filter criteria i.e. parameters wMsgFilterMin and wMsgFilterMax of type UNIT.

GetMessage does not return until a message matching the filter criteria as specified in the parameters is placed in the queue.

Whereas, PeekMessage returns immediately regardless of whether a message is in the queue.

Reference:
- Examining a Message Queue

Note

Notice that whenever HWND is present/used, it is to specify a specific window.

TranslateMessage

Does additional processing on the received messages. It is necessary if the thread is to receive character input from keyboard.

Translates virtual-key messages i.e. WM_KEYDOWN and WM_KEYUP generated from the system into a character message i.e. WM_CHAR and places it back into message queue.

DispatchMessage

Releases/sends a message to the window's (the one that originally sent that message i.e. as specified by HWND) window procedure as specified in MSG.

Therefore, only one message loop is needed since DispatchMessage will always dispatch the message to the appropriate window.

My Message Loop Analogy:

It is almost like a filtering process involving GetMessage the receiver, TranslateMessage the mediator, then finally DispatchMessage the sender, all within the application layer which sends off the message to the application window layer.

The Message Loop is like the heart of the application.

Window Messages (Notifications)

wParam and lParam are fields of the MSG structure. Which is passed to the window procedure after being dispatched from the message loop.

Both specify additional information about the message. The exact meaning depends on the values of the message member.

For example, WM_CREATE is defined as:

#define WM_CREATE                      0x0001

The identifier of a message is passed to the window procedure as the UINT parameter.

Depending on the purpose of the message, some messages will use one of the two parameters (either wParam or lParam) of WndProc, both or none. This applies to DefWindowProc as well.

For example, WM_CLOSE uses neither wParam or lParam. Hence, it is 0 for both.

typedef UINT_PTR WPARAM;
typedef LONG_PTR LPARAM;

UINT_PTR is an unsigned INT_PTR

#if defined(_WIN64)
 typedef unsigned __int64 UINT_PTR;
#else
 typedef unsigned int UINT_PTR;
#endif

Returns a LRESULT type.

typedef LONG_PTR LRESULT;

LONG_PTR is a signed long type for pointer precision.

#if defined(_WIN64)
 typedef __int64 LONG_PTR;
#else
 typedef long LONG_PTR;
#endif  

Reference:
- Naming Convention of Types such as LRESULT and What They Mean
- Windows Data Types

PostMessage

Places (posts) a message in the message queue associated with the thread that created the specified window.

Returns true (non-zero) if succeeds and without waiting for the thread to process the message.

For example, the same effect as clicking on the close button ❎, would be to call PostMessage(hwnd, WM_CLOSE, 0, 0).

SendMessage

Similar to PostMessage, both are used to send messages to the message queue.

However, PostMessage only puts the message in the message queue and then immediately returns.

While the SendMessage function calls the window procedure for the specified window and does not return until the window procedure has processed the message.

In other words, SendMessage does not send the message to the message queue.

To Terminate the Message Loop

Briefly:
An application can end its own loop by using the PostQuitMessage function, typically in response to the WM_DESTROY message in the window procedure of the application's main window.

In other words:
The message loop continues to retrieve messages from the thread's message queue and to dispatch them to the appropriate windows. The message loop ends when the GetMessage function removes the WM_QUIT message from the message queue.

Explained:
When the window procedure receives a WM_CLOSE message this would in turn call the DestroyWindow function. This tells the window that the message is to destroy its child windows and then itself.

This triggers a WM_DESTROY message to be dispatched to the window procedure. As a result, PostQuitMessage will be called which sends a WM_QUIT that never makes it to the window procedure as this causes the GetMessage while loop to not run, which means that DispatchMessage doesn’t get called. The purpose WM_QUIT is essentially to stop GetMessage or return false.

Reference: - Message Loop Microsoft Docs
- Understanding the Message Loop

The Window Procedure

//--------------------------------------------------------------------------------------
// Called every time the application receives a message
//--------------------------------------------------------------------------------------
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch( message )
    {
        case WM_PAINT:
            hdc = BeginPaint( hWnd, &ps );
            EndPaint( hWnd, &ps );
            break;

        case WM_DESTROY:
            PostQuitMessage( 0 );
            break;

        default:
            return DefWindowProc( hWnd, message, wParam, lParam );
    }

    return 0;
}

A function that receives and processes all messages sent to a particular window as specified by HWND.

Returns type LRESULT which indicates the success or failure of the function.

CALLBACK is an identifier that specifies WndProc as a callback function. This means that the OS (Windows) will be calling this function outside of the code space of the program. WndProc is never explicitly called in the application code.

See this Callback Functions for more information, there will be mention of Dynamic Link Library (DLL) functions.

Every window class has a window procedure, and every window created with that class uses that same window procedure to respond to messages.

DefWindowProc handles/processes default actions (or actions not handled by WndProc are passed to it) and returns a message result.

My WndProc Analogy:

The Window Procedure is like the brain of the application.

Reference:
- Window Procedure Microsoft Docs

Why Function Names in Win32 end with A and W

Functions with A use ANSI strings as input and output while functions with W use Unicode strings.

Reference:
- Unicode and ANSI Functions
- Stackoverflow Discussion on fucntionsA and functionsW

References Overall

A Simple Window

DirectX11 Sample Browser Tutorial 1