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: Windows
Include 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 procedure
The 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.

cbClsExtraAdditional parameters
cbSizeSpecifies the size of the window class
cbWndExtraAdditional parameters
hbrBackgroundSets background color for the window
hCursorThe cursor that will appear in the window
hIconIcon for the window
hIconSmSmall icon for the window
hInstanceHandle to the application instance
lpfnWndProcThe callback procedure (more on that later)
lpszClassNameThe unique name of the window class
lpszMenuNameUsed for menus
styleThe 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;

IDC_APPSTARTING  IDC_APPSTARTING
IDC_SIZEALL  IDC_SIZEALL
IDC_ARROW  IDC_ARROW
IDC_CROSS  IDC_CROSS
IDC_HAND  IDC_HAND
IDC_HELP  IDC_HELP
IDC_IBEAM  IDC_IBEAM
IDC_NO  IDC_NO
IDC_SIZENESW  IDC_SIZENESW
IDC_SIZENS  IDC_SIZENS
IDC_SIZESWSE  IDC_SIZENWSE
IDC_SIZEWE  IDC_SIZEWE
IDC_UPARROW  IDC_UPARROW
IDC_SIZEWE  IDC_SIZEWE

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


Next tutorial

Tutorial 3 - Creating window assets