Working with the Win32 API
Main Window Requirements
- Window Procedure Function
- Main Window Function
- Window Class (not a c++ class)
- Message Loop
- Resource file (optional)
The windows API uses a LOT of preprocessor instructions to modify function definitions that can be very confusing at first. Also most of the interactions with the API will be through Macros rather than functions.
First things first
Text encoding should be UNICODE as that is the standard these days. To enable unicode you will need to add two define statements:
#define UNICODE
#define _UNICODE
Without these define statements, either in the code or as a compiler flag the wrong functions might be called. When using an IDE put them in the code, and make sure there are no automatic defines assumed by the IDE so as not to show confusing results with code paths.
Windows Procedure Function
The window procedure needs to be defined, or at least declared prior to the main window function. The signature is:
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM)
LRESULT
is a window API typedef
CALLBACK
expands to __stdcall and is absolutely required for this function to work
For the main window procedure, you should pass on any unprocessed messages to the default windows procedure:
return DefWindowProc(hand, msg, wParam, lParam);
Main Window Function
The main window function is a macro and the inclusion of the header file <windows.h>
causes the compiler to use a different entry point (not main) and will prepare and call the WinMain
function with the listed parameters.
Int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow)
APIENTRY
expads to __stdcall and is required
hInstance
is a handle to the module (.exe)
hPrevInstance
is a hold over from the win16 api and is no longer used
lpCmdLine
is a string pointer containing the command line args
nCmdShow
is an int containing command line info regarding how the window should be show on start, useful for shortcut configurations
Window Class
The window class is a STRUCT
and needs to be filled out completely prior to being registered with the system. Incorrect details will cause the class registration to fail…
Use WNDCLASSEX
(extended)
See example program at the bottom.
Message Loop
The message loop is blocking and runs for the entire time the window executable is running.
Messages are obtained from the system, and then translated and distributed to the running application. Note the while loop for this, the logical comparison > 0 is important here, as negative values here would indicate an error so don’t use != 0 or leave it off.
Resource File
Using a resource file will allow the use of a manifest and also greatly simplify loading resources. See useful website references for details.
The file should be resource.rc
It needs to be compiled with the program objects.
To get an object compiled from a resource file you need to use a program called winders
:
windres -i resource.rc -o resource.o
Include resources in the program like this:
MAKEINTRESOURCE(IDI_APPICON)
Or depending on the use case just access the custom resource directly
IDI_APPICON
IDI_APPICON
in this case is a custom defined resource and will be in the resource.h file
Example Program
This is an example of a minimal program from which you could build off:
#define UNICODE
#define _UNICODE
#include <windows.h>
const wchar_t className[] = TEXT("WindowClass");
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR lpCmdLine, INT nCmdShow)
{
WNDCLASSEX wc;
HWND hWnd;
MSG msg;
wc.cbClsExtra = 0;
wc.cbSize = sizeof(WNDCLASSEX);
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WindowProc;
wc.lpszClassName = className;
wc.lpszMenuName = NULL;
wc.style = 0;
// Register the window class
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, TEXT("Could not register the window class"), TEXT("Error"), MB_OK | MB_ICONERROR);
return 1;
}
// Create the window
hWnd = CreateWindowEx(
0,
className,
TEXT("Window Title"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 400, 300,
NULL, NULL, hInstance, NULL);
if (!hWnd)
{
MessageBox(NULL, TEXT("Could not create window"), TEXT("Error"), MB_OK | MB_ICONERROR);
return 1;
}
// Show the window
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// Handle the message loop
while (GetMessage(&msg, hWnd, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (INT)msg.lParam;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
I found a lot of useful resources while figuring out how to work with the Win32 API:
Official Microsoft Documentation
Some GUI Examples
Minimizing C++ Win32 App To System Tray
Building Win32 GUI apps with MinGW
The Forgers Win32 API Programming Tutorial