In this tutorial, we will create a basic but functional window class using C++. This tutorial assumes you are using Microsoft Visual Studio and also assumes a basic knowledge of the environment and how to manipulate projects.
The object oriented nature of C++ creates an ideal foundation for creating something such as a window, where we want to hide most of the internal Windows code and expose only useful information such as the window's handle.
Begin by creating a new Visual Studio solution. We want to make a Win32 application and not a Console application.
Setting up the Solution

 |
When choosing the Application Settings, be sure to select Windows application and check the box "Empty Project". This disables pre-compiled headers and other features that we aren't interested in for the sake of this tutorial.
After accepting these settings, add a source code file to the new solution file called Main.cpp. This is where we will add our entry point to the Windows application (as the int main() method would in a console application) |
Before we begin, navigate to the Project menu and select Properties. In the property pages, expand "Configuration Properties", then click 'General'. In the field called "Character Set", change the value to "Use Multi-Byte Character Set". This will fix problems relating to unicode strings.
Creating a new Class
For now, we won't touch Main.cpp. First, we need to make our window class. Create a new C++ class by right clicking the project in the solution explorer and navigating to Add, and then to Class. Select "C++ Class" from the list of templates and click Add to continue. In the class wizard which follows, enter CBasicWindow as the class name and press Finish to create it.
This creates two new files in our solution; BasicWindow.h and BasicWindow.cpp.
Open BasicWindow.h. The first thing we need to do is to tell this unit that we are going to use the windowing functionality from the Win32 API. We do this by including a Windows header at the top of BasicWindow.h, like this:
#pragma once #define WIN32_LEAN_AND_MEAN #include <windows.h>
...
|
In this code we've placed a special #define symbol called WIN32_LEAN_AND_MEAN. This isn't an absolute necessity but does reduce the size of your application considerably by only including the essential tools from the Windows library header.
So, what next? Now we can start to add code for the window, but before we dive into writing code this is a good time to define the variables that we'll need to initialise a window. We will only need to store our window's handle. Add a private variable to the class CBasicWindow called 'handle', of type 'HWND'.
class CBasicWindow { private: HWND handle;
... |
Writing the Code
Open BasicWindow.cpp and locate the constructor for CBasicWindow. In here we will write all of the code for creating and showing the window on screen.
The first job is to define a WNDCLASS. This is a structure which allows us to define partly how we'd like the window to behave. Many of the members of this structure are not of great interest and can be set to zero, for this reason we will immediately zero the entire structure and then assign values to the fields we do want. Here is the constructor so far:
CBasicWindow::CBasicWindow( void ) : handle(0) { WNDCLASS wndClass; ZeroMemory( &wndClass, sizeof(wndClass) ); } |
Note: the initialiser applied to our handle variable. We want this variable to start life at zero, the constructor's header is where we do this.
The fields we need to define in this structure are as follows:
| wndClass.style |
= CS_HREDRAW | CS_VREDRAW;
Ensures that the window is repainted entirely if the window is resized either horizontally or vertically.
|
| wndClass.lpfnWndProc |
= WndProc;
We will examine this shortly.
|
| wndClass.hInstance |
= GetModuleHandle(0);
We use GetModuleHandle() here to retrieve the handle to the thread on which the application is executing.
|
| wndClass.hbrBackground |
= (HBRUSH)(COLOR_BTNFACE + 1);
We want the window to be the standard grey colour normally associated with Windows. The value 1 is always added to the colour here.
|
| wndClass.lpszClassName |
= "Basic Window";
This is the name we will give to our window class.
|
After defining these fields, our constructor now looks like this:
CBasicWindow::CBasicWindow( void ) : handle(0)
{
// prepare the class structure
WNDCLASS wndClass;
ZeroMemory( &wndClass, sizeof(wndClass) );
// define the window wndClass.style = CS_HREDRAW | CS_VREDRAW;
wndClass.lpfnWndProc = WndProc;
wndClass.hInstance = GetModuleHandle(0);
wndClass.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
wndClass.lpszClassName = "Basic Window";
} |
With the window defined, we can create it. The code to do this consists of two parts. First we register the class we just defined, then we create a window instance using the class. Here is an example of the code which performs these tasks:
// register the window class if ( !RegisterClass( &wndClass ) ) return;
// create the window
handle = CreateWindow( "Basic Window", "Blank Window", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, GetModuleHandle(0), 0 );
|
There's a pretty horrible line of code here! On examination, however, it makes a lot more sense. We are creating the window using our Basic Window class, the titlebar should read Blank Window, it should have all of the standard buttons (such as maximize, minimize, close), and be at a default position and size. The final four parameters aren't important and we will not discuss them here.
Assuming the code above runs fine, our handle variable should now contain a handle to a real window. We should make sure of this by adding a quick check;
Note: Although it has been omitted for the sake of this tutorial, you should consider adding a better error catching system to your own code. Returning from the constructor is not always the most ideal solution.
We're almost done. The window can now be shown by calling a function called ShowWindow() - providing our window's handle and what we want to do exactly;
ShowWindow( handle, SW_SHOW );
|
Defining the Window Procedure
Finally; remember WndProc from earlier? We need to define this. To keep things quick, we'll just define a very basic WndProc that handles things in a default way. Add the following to the window class in the header:
private: static LRESULT CALLBACK WndProc( HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam );
|
Add its implementation to the source code file, like this:
LRESULT CALLBACK CBasicWindow::WndProc( HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam ) { switch ( uMsg ) { case WM_DESTROY: PostQuitMessage(0); return true; break; default: return DefWindowProc( hwnd, uMsg, wParam, lParam ); break; } } |
This is an ugly function. In fact, it's a special callback method expected by Windows. Whenever a message is delivered to a window owned by your application, this function is expected to handle those messages in the relevant way. For the sake of this example, we have only handled the message sent when the user clicks the window's close button, for everything else we have used DefWindowProc to handle things in a default way.
Opening the Window
That concludes the code required for the window class. Save your work and now move to the empty Main.cpp file we created at the beginning of this tutorial.
We need to include our window class before we can use it;
The function we will define for our Windows application is called WinMain. This name is set in stone and cannot be changed, since Windows looks for this function when your application starts. Define it as follows:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
// create a new window
CBasicWindow win;
// enter a basic message loop to keep the window open
MSG msg; while ( GetMessage( &msg, 0, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); }
return 0;
} |
We've create a window and entered a message loop to keep the application open (if we didn't, the application would terminate before we have chance to see the fruits of our labour!). Don't become too concerned about the code for the message loop, its job is simple - to keep processing messages until there is no message to process (this happens when the application is closing).
Compile and run your application to see the results

This concludes the tutorial.
|