Windows Tutorial 2
Creating a basic Window
This Windows tutorial explores how to create a basic window using the Win32 API. In later tutorials be will build upon the code used in this tutorial to create more advanced applications.Prerequisites
Project type: WindowsInclude files: windows.h
Library files: N/A
Basic window creation
Unfortunatley, creating a basic window using the Win32 API is not as simple as just executing a single function, due to the flexibility in how we can create a window.The main steps involved are;
Register a window class Create the window Show the window Setup a callback procedureThe window class defines the overall 'look and feel' of the window that we want to create. For example we can set the background color, the style of mouse pointer, menus that we might be using, etc..
To create a window class an fill out its attributes is done like so;
WNDCLASSEX wClass;
ZeroMemory(&wClass,sizeof(WNDCLASSEX));
wClass.cbClsExtra=NULL;
wClass.cbSize=sizeof(WNDCLASSEX);
wClass.cbWndExtra=NULL;
wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
wClass.hIcon=NULL;
wClass.hIconSm=NULL;
wClass.hInstance=hInst;
wClass.lpfnWndProc=(WNDPROC)WinProc;
wClass.lpszClassName="Window Class";
wClass.lpszMenuName=NULL;
wClass.style=CS_HREDRAW|CS_VREDRAW;
It looks like a lot but, it is not as bad as it looks. But, I should explain what we have just done.
Firstly we created an instance of a window class using WNDCLASSEX and then cleared all of the parameters to zero.
Quite a few of the parameters did not even require setting up. But, I make use of the intellisense feature bundled with Visual Studio to make sure that I haven't forgotten about anything.
| cbClsExtra | Additional parameters |
| cbSize | Specifies the size of the window class |
| cbWndExtra | Additional parameters |
| hbrBackground | Sets background color for the window |
| hCursor | The cursor that will appear in the window |
| hIcon | Icon for the window |
| hIconSm | Small icon for the window |
| hInstance | Handle to the application instance |
| lpfnWndProc | The callback procedure (more on that later) |
| lpszClassName | The unique name of the window class |
| lpszMenuName | Used for menus |
| style | The look and feel of the window |
There are plenty of parameters that can be used in creating a window class. Way too many to mention in this tutorial, in the spirit of keeping things 'basic' at this stage. More information can be found on MSDN which is linked at the bottom of the tutorial.
One thing that may be handy to know at this stage is the different cursor types that are available, specified in the 'hCursor' parameter. You can choose what type of cursor is displayed when the mouse hovers over your window.
The different cursor types are;
No that we have filled out or WNDCLASSEX structure, we then need to register the window class.
RegisterClassEx(&wClass);
Having now created a window class it is probably worth adding some error checking in case you want to get adventurous and play with some of the parameters, to see what happens. When experimenting, errors can and will occur. So, to save tearing all of your hair out, it is worth taking advantage of error checking so you will have a fair indication as to what has gone wrong.
We can tell if the window class has failed if RegisterClassEx() returns 0. Fortunatley, there is another handy function, namely GetLastError().
GetLastError() will return an error code, which can then be used to track down the cause of the error. A list of possible error codes is linked at the end of this tutorial.
The error handling code we are going to use for this tutorial is as follows;
if(!RegisterClassEx(&wClass))
{
int nResult=GetLastError();
MessageBox(NULL,
"Window class creation failed",
"Window Class Failed",
MB_ICONERROR);
}
If a failure occurs for some reason, we will get a message box that tells us what has gone amiss.
Once we are happy that our window class if working as intended we can then move on to the creation of our actual window.
HWND hWnd=CreateWindowEx(NULL,
"Window Class",
"Windows application",
WS_OVERLAPPEDWINDOW,
200,
200,
640,
480,
NULL,
NULL,
hInst,
NULL);
Once again, if the window creation fails for some reason, the call will return 0. So, we can use the same error handling as we did before with the RegisterClassEx() call.
I highly recommend using error checking as it is very easy to put a wrong parameter in somewhere and have the call fail. It happens to the most experienced of programmers, believe me.
Now that we have called CreateWindowEx() and handled any errors, we can display the Window.
ShowWindow(hWnd,nShowCmd);
If we try to run the program from this point, no window will be displayed? Why, you ask? We have left out our final, vital, step. The windows 'callback procedure'.
Firstly we create a 'main loop' for our program.
MSG msg;
ZeroMemory(&msg,sizeof(MSG));
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Here we create a message handler. Our instance of MSG gets filled with the GetMessage() call. TranslateMessage() translates virtual key messages to character messages. And finally DispatchMessage() sends any messages to the callback procedure, where we can then handle the messages that are being received appropriately.
The callback procedure handles all of the windows messages that will be thrown at your application. Things like windows resizing, clicking menus and buttons, keyboard input, etc..
Every time someone puts any sort of input into your application, the callback procedure jumps into action. From there, depending on what input was receive, your program will respond in some way.
The callback procedure prototype looks like this;
LRESULT CALLBACK WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
The first parameter is the handle to the window that we want to monitor, the second is the message that we are currently receiving, the third and fourth parameters are, essentially, additional information for the received message. For example, it is no good to know that we have just received a key press if we don't know what key was pushed, is it?
The callback procedure being used for the purposes of this tutorial is as follows;
LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
break;
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
It is fairly straight forward. At this stage the only Windows message we are listening for is WM_DESTROY. WM_DESTROY is sent to the application if the program was closed using the X on the top right of the window.
Now, if we compile and run our newly created Window application You should be greeted by a blank window that can now be used as the basis for just about any windows program you want to make.
Next tutorial we will explore how to populate our newly created window with buttons, edit boxes, etc..
The Full Code
#include <windows.h>
LRESULT CALLBACK WinProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInst,LPSTR lpCmdLine,int nShowCmd)
{
WNDCLASSEX wClass;
ZeroMemory(&wClass,sizeof(WNDCLASSEX));
wClass.cbClsExtra=NULL;
wClass.cbSize=sizeof(WNDCLASSEX);
wClass.cbWndExtra=NULL;
wClass.hbrBackground=(HBRUSH)COLOR_WINDOW;
wClass.hCursor=LoadCursor(NULL,IDC_ARROW);
wClass.hIcon=NULL;
wClass.hIconSm=NULL;
wClass.hInstance=hInst;
wClass.lpfnWndProc=(WNDPROC)WinProc;
wClass.lpszClassName="Window Class";
wClass.lpszMenuName=NULL;
wClass.style=CS_HREDRAW|CS_VREDRAW;
if(!RegisterClassEx(&wClass))
{
int nResult=GetLastError();
MessageBox(NULL,
"Window class creation failed",
"Window Class Failed",
MB_ICONERROR);
}
HWND hWnd=CreateWindowEx(NULL,
"Window Class",
"Windows application",
WS_OVERLAPPEDWINDOW,
200,
200,
640,
480,
NULL,
NULL,
hInst,
NULL);
if(!hWnd)
{
int nResult=GetLastError();
MessageBox(NULL,
"Window creation failed",
"Window Creation Failed",
MB_ICONERROR);
}
ShowWindow(hWnd,nShowCmd);
MSG msg;
ZeroMemory(&msg,sizeof(MSG));
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WinProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
switch(msg)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}
break;
}
return DefWindowProc(hWnd,msg,wParam,lParam);
}
Things to try
Try experimenting with cursor types and window styles.Additional information
For additional information we have provided the following links.Microsoft (MSDN) - More about WNDCLASSEX
Microsoft (MSDN) - Windows system error codes