Jun 10, 2013 11:25 AM
(This post was last modified: Jun 10, 2013 11:26 AM by Inso.)
Introduction to the Win32 API
![[Image: MCGSCNZ.gif]](https://i.imgur.com/MCGSCNZ.gif)
![[Image: MCGSCNZ.gif]](https://i.imgur.com/MCGSCNZ.gif)
Hello and welcome to my tutorial on the Windows Application Programming Interface for C++. So what is it? The Windows API is a way for programmers to create graphical user interfaces (GUI) on the Windows operating system (OS). It allows the programmer to have a simpler way to do this rather than have to handle compatibility with different hardware and various other aspects of development between machines.
So why use the WinAPI? Aren't there easier packages to do this?
This is true, various frameworks such as MFC and .NET exist for rapid development of windows forms. Most of these frameworks work as wrappers for the Win32 API, where all of the code used would be wrapped within premade classes that cuts down on the amount of code that must be wrote.
The Win32 API should still be learned though as it can be helpful in the event of an error with one of these frameworks. With no understanding of how these frameworks really work, it will be difficult or more time consuming to fix any problems.
As this is my first tutorial on the Win32 API, I'm only going to show the Windows version of the popular Hello World program and explain it step by step. Because a bit more work would go into using the WinAPI, the Hello World program will be much bigger than expected.
This is a simple "Hello World" program using regular C++:
PHP Code
<?php
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!";
return 0;
}
This is a nice short program. In contrast, the Win32 requires much more code just for a simple window. Charles Petzold (who has wrote many books on programming with the Windows API) has been quoted as saying "The original hello world program in the Windows 1.0 SDK was a bit of a scandal. HELLO.C was about 150 lines long, and the HELLO.RC resource script had another 20 or so more lines. (...) Veteran C programmers often curled up in horror or laughter when encountering the Windows hello-world program".
Okay, so now onto our program. The first thing to do before anything else is done, is include the Windows header file, as this is the file where we get all the Windows functions, data structures, etc. This is done using something called a "preprocessor directive". This is a command that the compiler reads and responds to, in this case it bundles the windows header file along with your source into the program. Preprocessor directives are preceeded by a hash (#) symbol. Unlike other statements in C++, it is not ended by a semicolon (;).
PHP Code
<?php
#include <Windows.h>
The next thing to do is create our main function. Console based programs in C++ will use an actual main() function, but when programming with Win32 you use either WinMain() or wWinMain() as Windows.h will provide its own version of the main function. The only difference between the two is wWinMain uses unicode and thus wide characters, whereas WinMain used ASCII character codes.
In this tutorial I will use the wWinMain() function. It has the following prototype:
PHP Code
<?php
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow);
hInstance is the handle to the windows instance. prevInstance is also handle to a windows instance, but it should always be 0 as it is a parameter only used on 16 bit Windows. cmdLine is the arguments given to the program as a unicode string if the program was run from the command prompt. cmdShow is a flag that tells Windows if the program should start maximised, minimised or normal.
Here is our empty wWinMain function:
PHP Code
<?php
#include <Windows.h>
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow) {
return static_cast<int>(msg.wParam);
}
Because our program won't actually be using prevInstance or cmdLine, two additional functions can be called inside wWinMain which stops the compiler throwing errors at you for not using arguments.
PHP Code
<?php
#include <Windows.h>
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow) {
UNREFERENCED_PARAMETER(prevInstance);
UNREFERENCED_PARAMETER(cmdLine);
return static_cast<int>(msg.wParam);
}
Okay, now the next step is to create a windows class. This is done using WNDCLASSEX, a type defintion of the data structure tagWNDCLASSEX. The MSDN documentation defines it as follows:
PHP Code
<?php
typedef struct tagWNDCLASSEX {
UINT cbSize; //Size in bytes of the structure
UINT style; //Class style flags
WNDPROC lpfnWndProc; //A pointer to the window procedure
int cbClsExtra; //Number of extra bytes to allocate the window-class structure
int cbWndExtra; //The number of extra bytes to allocate the window instance
HINSTANCE hInstance; //A handle to the window procedure for the class
HICON hIcon; //A handle to the class icon
HCURSOR hCursor; //A handle to the class cursor
HBRUSH hbrBackground; //A handle to the class background brush
LPCTSTR lpszMenuName; Pointer to string that specifies the resource name of the class menu
LPCTSTR lpszClassName; //A pointer to a string specifying the class name
HICON hIconSm; A handle to a small icon for the window class
} WNDCLASSEX, *PWNDCLASSEX;
To create your new window class, put the following code in your wWinMain function, after the UNREFERENCED_PARAMETER() function and before the return statement.
PHP Code
<?php
WNDCLASSEX wndClass = {0};
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = "MyTutorialWindow";
In this snippet of code, we have created an instance of WNDCLASSEX and called it wndClass, then we initiate all of its values to 0 (0 is equivalent to null in the Windows API, somewhere in Windows.h there is a line reading #define NULL 0).
The size of the structure is set to the size of the WNDCLASSEX structure. Next it is given several style flags, in this case CS_HREDRAW and CS_VREDRAW. Flags are always separated by the bitwise OR symbol. CS_HREDRAW and CS_VREDRAW both redraw the entire window if a change is made to the horizontal or vertical client area respectively. A list of style flags can be found here.
Next lpfnWndProc was set to WndProc, a function that we will be creating later. The hInstance is set to the application instance containing the window procedure for the class. hCursor is set using the LoadCursor function, which takes an HINSTANCE (optional) and LPCTSTR argument which is the name for the cursor to be used. To get a list of all cursor types available, use this link. this function will return a variable of type HCURSOR, which is the handle to the cursor. If the function fails it returns a NULL (0) value.
hbrBackground is the handle to the background brush for this class. It may be a colour value or a handle to a brush. If it is a colour value, it must use one of the following colour values:
- COLOR_ACTIVEBORDER
- COLOR_ACTIVECAPTION
- COLOR_APPWORKSPACE
- COLOR_BACKGROUND
- COLOR_BTNFACE
- COLOR_BTNSHADOW
- COLOR_BTNTEXT
- COLOR_CAPTIONTEXT
- COLOR_GRAYTEXT
- COLOR_HIGHLIGHT
- COLOR_HIGHLIGHTTEXT
- COLOR_INACTIVEBORDER
- COLOR_INACTIVECAPTION
- COLOR_MENU
- COLOR_MENUTEXT
- COLOR_SCROLLBAR
- COLOR_WINDOW
- COLOR_WINDOWFRAME
- COLOR_WINDOWTEXT
Each of these values is equivalent to an integer. The reason we must use +1 is because the first value is equivalent to 0, so we add 1 so windows doesn't think we are supplying a NULL brush. We have set the menu name to null as we don't have one and we have called our class "MyTutorialWindow". Remember this name, as it will be used again later.
Next we must register this class. Add the following snippet of code after before the return statement.
PHP Code
<?php
if(!RegisterClassEx(&wndClass)) {
return -1;
}
This if conditional check uses the RegisterClassEx function as it's condition. If RegisterClassEx successfully registers the class it will return true. If it fails it will return false. This statement uses the NOT operator (!), so it checks if RegisterClassEx returns false. If it does, the wWinMain function returns -1 and will terminate.
The next step is to create our window and the handle to it. Add the following code before the return statement.
PHP Code
<?php
HWND hwnd = CreateWindow(
"MyTutorialWindow",
"Hello, World!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
300,
NULL,
NULL,
hInstance,
NULL
);
We have created a handle to a window called hwnd, which has been initialised using the CreateWindow() function. This takes 11 parameters. Although this is a function call, you may have noticed I have separated the arguments on different lines. C++ allows the use of whitespace like this, so it should be taken advantage of to improve readability of your code. In the MSDN document, CreateWindow is defined as follows:
PHP Code
<?php
HWND WINAPI CreateWindow(
LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu,
HINSTANCE hInstance,
LPVOID lpParam
);
It has a return value of HWND (Window Handle). It takes the argument of lpClassName (the class name we created earlier), lpWindowName (the text that will appear in the title label), the style flags (a full list can be found here), the initial x and y coordinates (in this case we used the default, CW_USEDEFAULT), the dimensions of the window, the handle of the parent window (ours has no parent so it is NULL), the handle to any menu associated with the window, an instance to the module associated with the window, and a pointer to a value passed through CREATESTRUCT (this isn't necessary for our window so it has also been set to NULL).
After creating a window it is good practice to check if CreateWindow() failed. To do this, add the following snippet.
PHP Code
<?php
if(!hwnd) {
return -1;
}
If this function is successful it will return an HWND for hwnd, but if it fails hwnd will be NULL which is equivalent to 0. This conditional check sees if hwnd is NULL, if it is the wWinMain function returns -1 and the program terminates. To display our window we now called the ShowWindow() function. This takes 2 arguments, the window handle of the window we want to display, and what state the window should open in.
PHP Code
<?php
ShowWindow(hwnd, cmdShow);
cmdShow will be given by the wWinMain function, it is one of the arguments we gave when writing it. After the first time the window is called and you decide to call ShowWindow again to change the state, the following values can be used instead of cmdShow:
- SW_HIDE: 0, hides the window and activates another window
- SW_SHOWNORMAL: 1, show the window in its normal state
- SW_SHOWMINIMIZED: 2, activates the window and minimises it
- SW_SHOWMAXIMIZED: 3, maximises the window
- SW_SHOWNOACTIVATE: 4, similar to SW_SHOWNORMAL but the window is not activated
- SW_SHOW: 5, activates the window and shows it in its current size and position
- SW_MINIMIZE: 6, similar to SW_SHOWMINIMIZED but the next window in the Z order is activated
- SW_SHOWMINNOACTIVE: 7, similar to SW_SJOWMINIMIZED but the window is not activated
- SW_SHOWNA: 8, similar to SW_SHOW but the window is not activated
- SW_RESTORE: 9, activates the window and displays it in its original size and position
- SW_SHOWDEFAULT: 10, sets the window to a default size and position
- SW_FORCEMINIMIZE: 11, minimises the window even if the thread that owns the window is not responding
To see these calls in action, you could change your ShowWindow call to the following:
PHP Code
<?php
ShowWindow(hwnd, cmdShow);
Sleep(2000);
ShowWindow(hwnd, SW_SHOWMAXIMIZED);
This will wait for 2 seconds, then maximise the window. Don't try this until the entire window is made though, as there are still some parts to add before the window will even work. While following the rest of this tutorial, keep only the original ShowWindow() call, you can try out the second one and the Sleep() afterwards.
Next I'm going to introduce the MSG structure. It is defined in the MSDN documentation as follows:
PHP Code
<?php
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG, *LPMSG;
hwnd is the handle to the window that has a windows procedure that receives messages (you will learn more about this later), message is the message identifier, wParam and lParam contains additional information about this message, time is the time the message was sent, and pt is the posiiton of the cursor at the time of the message being sent.
Now it's time to populate our MSG structure, add the following:
PHP Code
<?php
MSG msg = { 0 };
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
We first create an instance of the data structure, called msg and we set all of its member variables to 0. We have created a while loop that continues as long as msg.message is not WM_QUIT. WM_QUIT is the message used to quit a program. For each iteration of the loop we check to see if PeekMessage returns true. If it returns NULL there are no messages in the "message queue" (the queue where message are kept until they can be processed). PeekMessage() takes arguments of the address of our MSG structure, the handle to a window where messages should be received, the first and last in the range of messages to be examined, and a flag specifiying how messages are handled. In this case it is PM_REMOVE (Removed from queue after being processed by PeekMessage). Other flags that could be used (may be read about in more detail here) instead are:
- PM_NOREMOVE: Not removed from queue
- PM_REMOVE: Removed from queue
- PM_NOYIELD: Prevents threads being released that are waiting for the caller to go idle
All messages are automatically processed, but if you only want one or more types of messages to be processed you can also use the following flags:
- PM_QS_INPUT: Mouse and keyboard messages
- PM_QS_PAINT: Paint messages
- PM_QS_POSTMESSAGE: All posted messages
- PM_QS_SENDMESSAGE: All sent messages
These flags (like the window style flags) are separated by the bitwise OR operator (|).
TranslateMessage() will translate messages into character messages which are put into the message queue. DispatchMessage() will take messages from the queue and sends it to the window procedure, which is the next function we are going to create. Just to recap, your current wWinMain function should be as follows:
PHP Code
<?php
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPWSTR cmdLine, int cmdShow) {
UNREFERENCED_PARAMETER(prevInstance);
UNREFERENCED_PARAMETER(cmdLine);
WNDCLASSEX wndClass = { 0 };
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = hInstance;
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = "MyTutorialWindow";
if(!RegisterClassEx(&wndClass)) {
return -1;
}
HWND hwnd = CreateWindow(
"MyTutorialWindow",
"Hello, World!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
500,
300,
NULL,
NULL,
hInstance,
NULL
);
if(!hwnd) {
return -1;
}
ShowWindow(hwnd, cmdShow);
MSG msg = { 0 };
while(msg.message != WM_QUIT) {
if(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return static_cast<int>(msg.wParam);
}
Now onto our windows procedure function. First, we need to write the prototype for our function, before the wWinMain function definition and after the #include directive write the following:
PHP Code
<?php
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
What this does is create the prototype for our WndProc (Windows Procedure). It takes 4 arguments; the handle to the window, the message identifier being sent, the first message parameter, and the second message parameter.
Now below the wWinMain function definition, create our WndProc function:
PHP Code
<?php
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
return 0;
}
Now it's time to fill our WndProc function. First, we create a PAINTSTRUCT data structure that will hold information used to paint on the client window. After this we create a handle to the device context which can either be of type display, printer, memory, or information. The device context is used to support drawing or data retrieval on different types of devices.
PHP Code
<?php
PAINTSTRUCT paintStruct;
HDC hDC;
Now we have to create a switch which uses cases defined by the message identifiers:
PHP Code
<?php
switch(message) {
case WM_PAINT:
break;
case WM_DESTROY:
break;
default:
break;
}
This switch takes the message identifier passed to the WndProc function and does different things depending on this message identifier. In our switch we have included the WM_PAINT identifier and the WM_DESTROY identifier, but we have also included a default case in the event of being passed a message identifier that doesn't have a specific set of instructions attributed to it.
When populating this switch, each case should be in a block enclosed by braces, this prevents some errors which are caused when braces are left out (I'm not sure why though). For example:
PHP Code
<?php
switch(variable) {
case VALUE:
{
MyStatements...
}
break;
}
So first, in the WM_PAINT case, add the following:
PHP Code
<?php
{
hDC = BeginPaint(hwnd, &paintStruct);
EndPaint(hwnd, &paintStruct);
}
BeginPaint takes a handle to the window being repainted and the address of our paintStruct structure. It uses these values and returns a device context handle but if it fails it will return NULL. EndPaint takes the handle to the window that has been repainted, and the address of paintStruct which should hold data passed to it from BeginPaint.
Now add the following to the WM_DESTROY case:
PHP Code
<?php
{
PostQuitMessage(0);
}
When the WM_DESTROY message identifer is sent, it will call PostQuitMessage. PostQuitMessage will take an argument that specifies the exit code (0 tends to mean everything executed correctly). The exit code is used as the wParam parameter of the WM_QUIT message. Because WM_QUIT is not associated with a window (it is a call for the entire application to quit) it does not need a case to be created in the window procedure. It is instead generated when PostQuitMessage is called.
Now add this to the default case:
PHP Code
<?php
return DefWindowProc(hwnd, message, wParam, lParam);
DefWindowProc takes 4 arguments, the handle to the window procedure that received the message, the message identifier, and two parameters for additional message information. It is used to handle any messages that haven't been handled by our switch cases in the WndProc function.
If you haven't caught on yet, the basic flow for a Windows program will be as follows:
- Program starts and your WinMain/wWinMain function is called
- Create your window class
- Register this class
- Create a window
- Initiate the message loop
- Check if the message queue contains anything
- If it does, call the windows procedure while passing the handle to whatever window is using this callback procedure
- Use the information in this message to decide what to do
- Act on this message
- Continue looping until WM_QUIT is posted as a message
- Quit the application
And now your window is complete! This is a basic window that will say "Hello, World!" in the title bar. The entire source code can be seen in the spoiler below:
Spoiler (Click to View)



