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
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
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 finallyDispatchMessage
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