A window in Windows API
last modified October 18, 2023
A window is a rectangular area of the screen where the application displays output and receives input from the user. Everything is a window in Windows. At least from the programmer's point of view. A main window, a button, a static text even an icon; all are windows. A static text is only a special kind of a window, so is the desktop area.
The wWinMain function
Every Windows UI application must have at least two functions: the WinMain
function and the window procedure. The WinMain function is the entry
point to a Windows UI application. It initialises the application, shows the
application window on the screen, and enters the main loop. In our examples, we
use the wWinMain
function prototype, which is used for creating
Unicode UI programs.
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
The hInstance
is a handle of an instance. It is a 32-bit number
identifying the instance of our program within the OS environment. This number
is given by Windows when the program starts executing. The
hPrevInstance
parameter is always NULL
; it is a legacy
from the 16-bit Windows. Windows programs can also be started from the command
line. The parameters given are stored in pCmdLine
parameter. The
nCmdShow
value specifies how the window will be displayed:
minimised, maximised, or hidden.
The wWinMain
function terminates when it receives the
WM_QUIT
message.
Registering a window class
Before we can create a window, we must register its class within the Windows.
Many controls have their window classes already registered. So when we create a
button or a static text, we do not need to register a window class for them. To
register a window class, we must create and fill a WNDCLASS
structure. We set the window style, extra allocation bytes, window class name,
handle of the program instance, background brush, optional menu name, window
procedure, handle of the cursor, and an icon. Then the
RegisterClassW
function is called.
Creating a window
The window is created by calling the CreateWindowW
function.
HWND CreateWindowW(LPCWSTR lpClassName, LPCWSTR lpWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam);
The lpClassName
uniquely identifies the window. It is the name
under which we registered the window. The lpWindowName
is the
window name. Its effect depends on the context—it can be title of the window in
parent windows or a label in child windows like button or static text. Windows
can be created using several styles. For this, we have the dwStyle
parameter. The x
, y
specify the initial horizontal
and vertical position of the window. The nWidth
and
nHeight
specify the window width and height.
The hWndParent
is a handle to the parent window. For windows that
do not have parents, we use NULL
. For a parent window the
hMenu
is an optional handle to the menu, for a child window, it is
a control identifier. The hInstance
is a handle to the program
instance. The lpParam
is the last parameter, it is an optional
value passed to the window during the WM_CREATE
message. The
CreateWindowW
function returns a handle to the newly created window.
Messages
The WinMain
function creates a message loop. It is an
endless cycle which runs during the life of the application. Message loop is a
programming construct that waits for and dispatches events or messages in a
program. Windows communicate using messages. A message is an integer
value that identifies a specific event—a button click, resizing of the window,
or closing of an application.
There can be multiple messages created in one moment. The messages cannot be
processed all at the same time; therefore they are stored in a
message queue. The message enters the message queue and waits until it
is processed. The GetMessage
function retrieves the message from
the message queue. The DispatchMessage
function dispatches a
message to a window procedure. If the application obtains character input, we
include the TranslateMessage
function in the loop.
Window procedure
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
Every window has an associated window procedure. It is a function that receives
messages. The hwnd
is a handle to the window that is going to
receive the message. The uMsg
is the message. The
wParam
and lParam
parameters provide additional
message information. The values of these parameters depend on the message type.
The messages come from the user or from the operating system. We react to a
message or we call the default window procedure to provide default processing.
Most messages are sent to the default window procedure. The default window
procedure is called DefWindowProcW
. It is called with the same
parameters as the normal window procedure.
A simple window
The following example shows a skeletal Windows application.
#include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { MSG msg; HWND hwnd; WNDCLASSW wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.lpszClassName = L"Window"; wc.hInstance = hInstance; wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE); wc.lpszMenuName = NULL; wc.lpfnWndProc = WndProc; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); RegisterClassW(&wc); hwnd = CreateWindowW(wc.lpszClassName, L"Window", WS_OVERLAPPEDWINDOW | WS_VISIBLE, 100, 100, 350, 250, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); } return (int) msg.wParam; } LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProcW(hwnd, msg, wParam, lParam); }
We are going to explain the example code step by step.
#include <windows.h>
This is a header file for the C programming language. It contains all function
declarations in the API, all common macros and all the data types. The Windows
API is added to the C programming project by linking the necessary
libraries—kernel32.lib
,
user32.lib
, gdi32.lib
—and by including the
<windows.h>
header file.
wc.style = CS_HREDRAW | CS_VREDRAW;
We set the window style here. The CS_HREDRAW
and
CS_VREDRAW
flags mean that whenever there is a movement or
size adjustment of the height or width of the window, the entire window
is redrawn.
wc.cbClsExtra = 0; wc.cbWndExtra = 0;
In our example, we do not use the additional bytes. So we set the members to zero. The most common use of those two attributes is window subclassing.
wc.lpszClassName = L"Window";
Window is a class name for this particular window type. We use this class
name when creating the window. The L
character precedes wide
strings.
wc.hInstance = hInstance;
We set the instance of our program.
wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
Here we set the background brush. It is the colour that is used to paint the client area of the window.
wc.lpszMenuName = NULL;
In our example, we do not create a menu.
wc.lpfnWndProc = WndProc;
We provide the window procedure for the window class.
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
We set the cursor for our application. We load the cursor from system resources
by using the LoadCursor
function. IDC_ARROW
is a value
for a standard arrow cursor.
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
We set the icon for our application. The icon is retrieved from system resources
with the LoadIcon
function. IDI_APPLICATION
is a value
for a default application icon.
RegisterClassW(&wc);
We register the window class with the system.
ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd);
These two lines show the window on the screen. The nCmdShow
specifies how we display the window on the screen.
while (GetMessage(&msg, NULL, 0, 0)) { DispatchMessage(&msg); }
This is the message loop. We receive messages from the message queue using
the GetMessage
function and dispatch them to the window
procedure using the DispatchMessage
function.
return (int) msg.wParam;
At the end of the application, the exit code is returned to the system.
switch(msg) { case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProcW(hwnd, msg, wParam, lParam);
In the window procedure, we react to the WM_DESTROY
message.
The PostQuitMessage
sends the WM_QUIT
message to
the message queue. All other messages are sent to the default processing
using the DefWindowProcW
function.
In this part of the Windows API tutorial, we have created a basic window.