#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg = {0};
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"minwindowsapp";
if( FAILED(RegisterClass(&wc)) )
return 1;
if(FAILED(CreateWindow(wc.lpszClassName,
L"Minimal Windows Application",
WS_OVERLAPPEDWINDOW|WS_VISIBLE,
0,0,640,480,0,0,hInstance,NULL)))
return 2;
while( GetMessage( &msg, NULL, 0, 0 ) > 0 )
{
DispatchMessage( &msg );
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
This section describes and provides extra information about the code of our program.
#include <windows.h>
This enables access to Microsoft Windows specific data types and
functions that make life easy when programming with apps for Windows. (MSG, WNDCLASSEX, etc.)
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
This is a Forward Declaration of a Window Procedure. The Window Procedure is crudely simplified, an 'event handler' of sorts. It receives events, which in Windows, are called 'messages'.
Every window in Windows has a Window Procedure. This will be covered in
depth more when we reach the implementation of this declaration.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
This is the 'entry' point of the program, specifically, a graphical Windows-based program. Console programs use the simpler main(...).
The four parameters you see are automatically received from the
Microsoft Windows operating system when your application is started. The
first parameter(hInstance, meaning Instance Handle) being the most important. It functions as a unique 'identifier'(these are often called handles
in Win32 programming) for this particular instance of your program you
have started. Every program running on Windows has one and it allows you
to create multiple instances of your programs window without having to
worry it already existing since they both use their own unique instance
'handles'.
More information about WinMain: Click here
More information about hInstance: Click here
MSG msg = {0};
This is a data structure that contains message information for
example when a user clicks a mouse button on top of your window etc.
This is required for a normal Win32 application main loop which you will
see later in the code. Its initialized here to conform to my favourite
C89 coding standard which requires that all variables are declared at
the start of the scope. Remember, C89 is your friend, because you can use all C89 in C++. More information about the MSG data structure by clicking here.
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = L"minwindowsapp";
if( FAILED(RegisterClass(&wc)) )
return 1;
The Window Class. This is an important concept to grasp in Windows programming.
A window class must be successfully registered
to the Windows operating system before a window can be created. We will
create our own custom window class and try to register it to the
operating system. Trying to register a window class to the operating
system is not something that always succeeds, because the operating
system simply might not have enough memory available for any new window
classes anymore. If this is the case it will return a zero and not a
sensible unique identifier for our class, which is why the call should
always be error checked.
The minimal member variables of the WNDCLASS struct that are required for a window are as follows:
lpfnWndProc
Contains a function pointer to which function this windows messages should go to for processing and handling.
hInstance
The
unique instance handle we received from the operating system into our
first entry point parameter when our program started, will be bound here
into the window class data structure.
hbrBackground
Without
defining this, we would have no background to our window meaning it the
background of the window would become whatever was under the window
when it was created, and when moved, it would cause all kinds of
graphical errors to appear on your window.
lpszClassName
This
is the human readable name of the window class we are about to create.
This name works as a reference when telling windows what kind 'style' of
a window we want to create and what function receives its window
messages (WndProc in this case). We could then for example create multiple windows using a class named "myredwindowclass" which all had red backgrounds if the class was already registered to the operating system.
IMPORTANT NOTE: Why do you put an L infront of a normal string? Click here.
if(FAILED(CreateWindow(wc.lpszClassName,
L"Minimal Windows Application",
WS_OVERLAPPEDWINDOW|WS_VISIBLE,
0,
0,
640,
480,
0,
0,
hInstance,
NULL)))
return 2;
After the necessary 'raindancing' above the CreateWindow snippet is complete, we are finally ready to attempt the creation of a window. If this fails, our program will exit with the return code of 2. If the function succeeds, the return value is a handle of the type HWND, to your new window, which you can use for example, like this: ShowWindow(handle,SW_MINIMIZE); which will minimize your window to the taskbar :)
The parameter list is as follows:
lpClassName
Here we need to assign CreateWindow a valid, already registered window
class name. A sweet example of already registered class names are
common controls, like the edit controls etc. where you type your text in
Windows. As those are usually always registered already, and everything visible in windows is a window, even buttons!, you can try replacing the wc.lpszClassName with this: L"EDIT"
and you will see how changing the windows class affects the window. You
can now select and deselect text and your whole window has turned into a
text control! :) Click the following hyperlink and scroll down to the part where EDIT is also presented, and feel free to try out more common controls! MSDN About Common Controls
lpWindowName
This is pretty self-explanatory, you can decide yourself what kind of a name you give to your window.(more details)
x
The initial X position of the window, or some would call it the initial horizontal position. CW_USEDEFAULT could be used here(more details)
y
The initial Y position of the window.(more details)
nWidth
The initial width of the window. (more details)
nHeight
The initial height of the window.(more details)
hWndParent
Here could be a 'handle' to the parent or owner window of this window,
this is nearing advanced topics so lets leave this for now.(more details)
hMenu
This
is a little bit more advanced tool for this tutorial, leave it to null
for now. Its good for you to know that this has a deep and useful
connection with those resource files that you might have seen when creating the default template win32 app with visual studio.
hInstance
Nothing new here.
lpParam
This parameter is used in advanced windows programming also, for example: abstracting functionality of WndProc etc.
Note: Generally there is a call to TranslateMessage before DispatchMessage, but it is intentionally left out for this tutorial to provide you with the distilled functions to learn and master instead of everything at once. TranslateMessage produces WM_CHAR messages only for keys that are mapped to ASCII characters by the keyboard driver. See the this msdn link for more information: TranslateMessage
while( GetMessage( &msg, NULL, 0, 0 ) > 0 )
{
DispatchMessage( &msg );
}
return 0;
}
The Main Loop, also known as, the Message Loop
The programs in the Microsoft Windows operating system
communicate with each other using messages, which in some systems are
called 'events'. Something happens somewhere, like a mouse is clicked on
a windows top right X icon, a message is sent throughout the operating
system that 'hey, that window just died, hide it and start freeing up
the memory it was eating, remove its classname from the registered
class names list etc.'.
Here we check for any messages for our window, and we send them along until our window receives a posted message. DispatchMessage does the sending.
This is not the place for reacting to received messages, the WndProc WindowProcedure is the place where that should happen.
When all messages have been processed and we've received a posted message that is 0 or below 0 by its value (like the PostQuitMessage(0) command produces), we return 0 for a sign of successfull/natural program termination.
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CLOSE:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Capturing Basic Keyboard Input
Navigate to this row of code:
switch(message)
{
and add the following code:
case WM_KEYUP:
{
switch(wParam)
{
case VK_ESCAPE:
{
PostQuitMessage(0);
}
break;
}
break;
}
break;
and when you recompile and run with F5, you can press Escape and you window will close automatically!
I really recommend using MSDN as a source for the Windows API and reading about the wonderful virtual keys that we can utilize and also about the WM_KEYDOWN to complement the WM_KEYUP that we've seen. Heres are example links to MSDN that describe more indepth what virtual keys are about and the keyup window message:
Click here for Virtual-Key Codes on MSDN
Click here for WM_KEYUP Window Message on MSDN
Capturing Basic Mouse Input
The mouse input functions a lot like the keyboard input, but I'll show you a useful trick I've learned along the way to get the useful information out of mouse movement a bit easier than every other tutorial on the block. We'll capture mouse coordinates and print the results into your Visual Studio 'Debug Output' window and show a message saying Hello World when the window is clicked with the left mouse button.
Just add this code where your WndProc function begins from:
WCHAR mouseCoordinates[4] = {0};
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_MOUSEMOVE:
wsprintf(mouseCoordinates, L"X:%d Y:%d\n", HIWORD(lParam), LOWORD(lParam));
OutputDebugString(mouseCoordinates);
break;
case WM_LBUTTONUP:
{
MessageBox(0,L"Hello World!",0,0);
}
break;
This modification I leave for you to explore forwards, there is a wondrous amount of useful functionality within the Microsoft Windows operating system and I hope I have sparked an interest into delving deeper into it, successfully and without frustrations :)