rss
logo

I provide consulting and custom development for Natural Language Processing, Information Extraction and Search solutions.Self Picture


 learn more   get in touch 

Logo - I Build Search
Jul 26
2008

Lesson 2 – Creating a basic window digg

In this lesson you will be introduced to the event-driven programming model. You will learn how Windows uses messages to communicate with applications, how event based programming works, what callback functions are, and while doing this create a basic windows application.

2:1

Before you begin

If you do not have the platform SDK help files (WinAPI docs), download the win32.hlp file. Though old, it is a valuable resource. Or you can download the Platform SDK form msdn.

Begin

Add the following code to main.cpp:

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/* DrawLite - Windows Programming Tutorial
 * by Pravin Paratey (March 08, 2007)
 *
 * Source released under
 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
 * http://creativecommons.org/licenses/by-nc-nd/3.0/
 */
 
#include <windows.h>
 
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
 
int WINAPI
    WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
    HWND hwnd;
    WNDCLASSEX wcx;
 
    wcx.cbSize = sizeof(WNDCLASSEX); // Must always be sizeof(WNDCLASSEX)
    wcx.style = CS_DBLCLKS; // Class styles
    wcx.lpfnWndProc = MainWndProc; // Pointer to callback procedure
    wcx.cbClsExtra = 0; // Extra bytes to allocate following the wndclassex structure
    wcx.cbWndExtra = 0; // Extra bytes to allocate following an instance of the structure
    wcx.hInstance = hInst; // Instance of the application
    wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Class Icon
    wcx.hCursor = LoadCursor(NULL, IDC_ARROW); // Class cursor
    wcx.hbrBackground = (HBRUSH) (COLOR_WINDOW); // Background brush
    wcx.lpszMenuName = NULL; // Menu Resource
    wcx.lpszClassName = "DrawLite"; // Name of this class
    wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // Small icon for this class
 
    // Register this window class with MS-Windows
    if (!RegisterClassEx(&wcx))
        return 0;
 
    hwnd = CreateWindowEx(0, //Extended window style
            "DrawLite", // Window class name
            "Lesson 2 - A simple win32 application", // Window title
            WS_OVERLAPPEDWINDOW, // Window style
            CW_USEDEFAULT, CW_USEDEFAULT, // (x,y) pos of the window
            500, 400, // Width and height of the window
            HWND_DESKTOP, // HWND of the parent window (can be null also)
            NULL, // Handle to menu
            hInst, // Handle to application instance
            NULL); // Pointer to window creation data
 
    // Check if window creation was successful
    if (!hwnd)
        return 0;
    // Make the window visible
    ShowWindow(hwnd,SW_SHOW);
 
    // Process messages coming to this window
    while (GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
 
    return msg.wParam;
}
 
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_DESTROY: // User closed the window
            PostQuitMessage(0);
            break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

Phew. That was long! Press F9 to compile and run. You have a basic window on your screen!

Breaking it up

Event driven programming model

2:5

Every time windows has to communicate with your application, it sends messages to your application. Once all initializations have been done and the window shown on screen, all your application has to do is poll for windows messages.

The lines up to 54 create and show the window and the lines 57-61 poll for messages.

The GetMessage() function gets the next message to be processed from the message queue. GetMessage() returns a non-zero value for every message other than WM_QUIT. This means that the while loop continues until it is time to quit.

TranslateMessage() translates virtual key messages to character messages.

DispatchMessage() dispatches the message to a window procedure. This means that for messages coming to our window, the MainWndProc() is called by Windows through DispatchMessage().

How does Windows know which function to call? Well, we tell Windows during WNDCLASSEX initialization [line 24].

WNDCLASSEX structure

Every window that you create has an associated WNDCLASSEX structure. The WNDCLASSEX structure provides all the information necessary for Windows to do perform window related functions like drawing its icon, cursor, menu, calling the callback function which will receive messages and so on.

The WNDCLASSEX structure is defined as,

typedef struct _WNDCLASSEX {
    UINT    cbSize; 	// This must always be set to sizeof(WNDCLASSEX)
    UINT    style; 	// This specifies the class styles. Take a look at
			// your SDK documentation for the values this member can take.
    WNDPROC lpfnWndProc; // Pointer to the WndProc which will handle this windows messages.
    int     cbClsExtra; // Number of extra bytes to allocate at the end of the WNDCLASSEX structure.
    int     cbWndExtra; // Number of extra bytes to allocate at the end of the window instance.
    HANDLE  hInstance;  // Identifies the instance that the window procedure of this class is within.
    HICON   hIcon; 	// Handle to the icon associated with windows of this class.
    HCURSOR hCursor; 	// Handle to the cursor for windows of this class.
    HBRUSH  hbrBackground; // Identifies the class background brush.
    LPCTSTR lpszMenuName; // Identifies the menu for windows of this class.
    LPCTSTR lpszClassName; // Pointer to a NULL terminated string or an atom specifying the class of this structure.
    HICON   hIconSm; // Handle to the small icon associated with this class.
} WNDCLASSEX;

Registering your window class

After you’ve created your window class, you need to tell Windows about it. This is done by registering the class with windows. The function call is RegisterClassEx(..). Once this is done, you can create instances of this window by calling CreateWindowEx(..) with the proper arguments.

Creating the window

A window is created by calling the CreateWindowEx(..) defined as,

HWND CreateWindowEx(
    DWORD dwExStyle,		// extended window style
    LPCTSTR lpClassName,	// pointer to registered class name
    LPCTSTR lpWindowName,	// pointer to window name
    DWORD dwStyle,		// window style
    int x,			// horizontal position of window
    int y,			// vertical position of window
    int nWidth,			// window width
    int nHeight,		// window height
    HWND hWndParent,		// handle to parent or owner window
    HMENU hMenu,		// handle to menu, or child-window identifier
    HINSTANCE hInstance,	// handle to application instance
    LPVOID lpParam 		// pointer to window-creation data
   );

Lines 39-48 create the window. If the creation was successful a non-zero handle is returned by CreateWindowEx after which ShowWindow() shows the window on the screen.

Tip: Refer to your documentation

It is a good idea to keep referring to these functions in your SDK docs while reading this tutorial.

2:2

Callback functions

A callback function is the one that receives the messages sent to your application. This is where you do something about the message. We provide a pointer to this function while defining the window class [line 18].

Callback functions have to be defined as,

LRESULT CALLBACK
function-name(	HWND hwnd,	// Handle of window which received this message
		UINT msg, 	// The message
		WPARAM wParam,	// Extra information
		LPARAM lParam	// Extra information
		);
HWND hwnd
The handle of the window is specified so that you know which window to act upon. This is necessary because you may have created more than one instance of the window.
UINT msg
This contains the message sent.
WPARAM wParam and WPARAM lParam
wParam and lParam are used to pass extra info about the message. For example a WM_LBUTTONDOWN (left mouse button down) message will have the x and y co-ordinates as the upper and lower word of lParam and wParam will tell if any modifier keys (ctrl, alt, shift) have been pressed.

MainWndProc

66
67
68
69
70
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	...

The switch statement lets us select which message was sent. There are over 200 messages that windows can send your application. To read about them, just search for WM_ in your SDK docs.

2:3
WM_DESTROY
69
70
71
72
73
...
	case WM_DESTROY: // User closed the window
		PostQuitMessage(0);
		break;
...

The WM_DESTROY message is sent to your application when the user teminates the application either by clicking the X at the upper right corner, pressing Alt+F4, or quits the application by other means.

PostQuitMessage() causes GetMessage(..) [line 57] to return false and thus breaking out of the while loop and exiting the application. The argument to PostQuitMessage is the return value to the system.

DefWindowProc(..)

What about the other 200 or so messages? Surely you, the programmer, aren’t going to write code for all the 200 messages. Fortunately, Windows provides the DefWindowProc(..) function which handles all the messages. For the purposes of displaying a simple window, your MainWndProc could very well have consisted of,

LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

What this means is that every time you want to do something about a message, add the case switch for the message and write the code which does something about it. All messages that you don’t want to handle should be passed to the DefWindowProc(). This is what we have done in our code.

72
73
74
75
	...
		default: // Call the default window handler
			return DefWindowProc(hwnd,msg,wParam,lParam);
	...

C++ is about classes

Before we go any further, let’s organize that code into a classes. This helps us manage our code better. Add a new file to the project and name it MainWindow.h. Add the following code to it:

MainWindow.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* DrawLite - Windows Programming Tutorial
 * by Pravin Paratey (March 08, 2007)
 *
 * Source released under
 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
 * http://creativecommons.org/licenses/by-nc-nd/3.0/
 */
 
#include <windows.h>
 
class MainWindow
{
public:
    MainWindow(HINSTANCE hInstance);
    ~MainWindow();
    static LRESULT CALLBACK MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
    bool Run(int nCmdShow);
 
private:
    WNDCLASSEX m_wndClass;
    static HINSTANCE m_hInstance;
    HWND m_hwnd;
    static char m_szClassName[];
};

Next, add another file called MainWindow.cpp with the following code:

MainWindow.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/* DrawLite - Windows Programming Tutorial
 * by Pravin Paratey (March 08, 2007)
 *
 * Source released under
 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
 * http://creativecommons.org/licenses/by-nc-nd/3.0/
 */
 
#include <windows.h>
#include "MainWindow.h"
 
char MainWindow::m_szClassName[] = "DrawLite";
 
MainWindow::MainWindow(HINSTANCE hInstance)
{
    m_hInstance = hInstance; // Save Instance handle
 
    m_wndClass.cbSize = sizeof(WNDCLASSEX); // Must always be sizeof(WNDCLASSEX)
    m_wndClass.style = CS_DBLCLKS; // Class styles
    m_wndClass.lpfnWndProc = MainWndProc; // Pointer to callback procedure
    m_wndClass.cbClsExtra = 0; // Extra bytes to allocate following the wndclassex structure
    m_wndClass.cbWndExtra = 0; // Extra bytes to allocate following an instance of the structure
    m_wndClass.hInstance = hInstance; // Instance of the application
    m_wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // Class Icon
    m_wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); // Class cursor
    m_wndClass.hbrBackground = (HBRUSH) (COLOR_WINDOW); // Background brush
    m_wndClass.lpszMenuName = NULL; // Menu Resource
    m_wndClass.lpszClassName = m_szClassName; // Name of this class
    m_wndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); // Small icon for this class
}
 
MainWindow::~MainWindow()
{
}
 
LRESULT CALLBACK MainWindow::MainWndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_DESTROY:
        PostQuitMessage (0);
        break;
    default:
        return DefWindowProc (hwnd, msg, wParam, lParam);
    }
 
    return 0;
}
 
bool MainWindow::Run(int nCmdShow)
{
    if(!RegisterClassEx(&m_wndClass))
        return false;
    m_hwnd = CreateWindowEx(
            0,
            m_szClassName,
            "Draw Lite",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            500,
            400,
            NULL,
            NULL,
            m_hInstance,
            NULL
            );
    if(!m_hwnd)
        return false;
    ShowWindow(m_hwnd, nCmdShow);
    return true;
}

Next change main.cpp to look like so:

main.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* DrawLite - Windows Programming Tutorial
 * by Pravin Paratey (March 08, 2007)
 *
 * Source released under
 * Creative Commons Attribution-Noncommercial-No Derivative Works 3.0
 * http://creativecommons.org/licenses/by-nc-nd/3.0/
 */
 
#include <windows.h>
#include "MainWindow.h"
 
int WINAPI
    WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lpCmdLine, int nCmdShow)
{
    MSG msg;
 
    MainWindow *winMain = new MainWindow(hInst);
    if(!winMain->Run(nCmdShow))
    {
        delete winMain;
        return 1; // error
    }
 
    // Run the message loop. It will run until GetMessage() returns 0
    while (GetMessage (&msg, NULL, 0, 0))
    {
        // Translate virtual-key messages into character messages
        TranslateMessage(&msg);
        // Send message to WindowProcedure
        DispatchMessage(&msg);
    }
 
    delete winMain;
 
    return msg.wParam;
}

Adding Functionality

Lets pop up a message box which will display the co-ordinates of the point where the left mouse button was pressed. To do this you will have to handle the WM_LBUTTONDOWN message.

Add this code to MainWindow.cpp at line 44

MainWindow.cpp
41
42
43
44
45
46
47
48
49
50
51
52
53
	...
	PostQuitMessage(0);
		break;
	case WM_LBUTTONDOWN:
		char str[256];
		POINT pt;
		pt.x = LOWORD(lParam);
		pt.y = HIWORD(lParam);
		wsprintf(str,"Co-ordinates are\nX=%i and Y=%i",pt.x,pt.y);
		MessageBox(hwnd, str, "Left Button Clicked", MB_OK);
		break;
	default:
	...

Press F9. This is what you should see when you click anywhere inside the window.

2:3

Exercise

Try this exercise. Pop up a message every time a key is pressed on the keyboard. Hint: Handle the WM_CHAR message. Remember to refer to your SDK docs.

If you could manage that, give yourself a pat on the back. You now understand the basics of event-driven programming – the mechanism which Windows uses to communicate with your application. You have crossed one of the more difficult hurdles in learning windows programming.

Don’t worry if you could not do the exercise or if things are still a bit hazy. These concepts will be used in every single lesson after this and it will soon become second nature to you.

11 Responses (rss) (trackback)

#1

samundra

August 10th, 2008 at 2:21 pm

Hello Pravin,

Am very much pleased to go with your tutorial, as it provided me a lot of kick-start. Am even more pleased to see that you have made corrections to your code, where you forgot to mention that char str[256], POINT pt.

Thanks,
Samundra (Nepal)

#2

huphos

September 28th, 2008 at 12:39 am

Thanks for all your hard work.
The first listing of main.cpp in lesson 2 will not compile as there’s no “MainWindow.h”

#3

kislorod

November 16th, 2008 at 9:28 am

huphos

+1

#4

bungy_serbia

December 12th, 2008 at 1:14 am

Just comment that line huphos.
// #include "MainWindow.h"

#5

Pravin Paratey

March 28th, 2009 at 12:10 am

Samundra: Thank you for your wishes!

hupos, kislorod, bungy_serbia: Updated it in the tutorial thanks!

#6

Amit

May 11th, 2009 at 12:06 pm

Nice site. Really well written. But you’ve not explained callbacks properly. Also, do all callbacks need to be defined in the same manner? Are there any other places where we use callbacks in Windows programming? And lastly, what exactly is that CALLBACK typedef?

Keep writing.
Amit

#7

Franck

August 14th, 2009 at 3:50 am

really … i’ve been trying just to find books to help me understand windowing programming with c++ since i know how to program api with java, objective c , c# and VB but nothing…

thanks a lot

#8

chris negus

October 16th, 2009 at 9:53 pm

if you get:
error C2664: ‘CWnd::MessageBoxW’ : cannot convert parameter 1 from ‘const char [...]‘ to ‘LPCTSTR’

then:
Change your project configuration to use multibyte strings. Press ALT+F7 to open the properties, and navigate to Configuration Properties > General. Switch Character Set to “Use Multi-Byte Character Set”.

http://social.msdn.microsoft.com/forums/en-US/vclanguage/thread/c1b08c0a-a803-41c3-ac8c-84eba3be1ddb/

#9

red

October 19th, 2009 at 5:54 pm

Your tutorials are awesome, I have looked at a few books on this topic, but its tutorials like this that make it a bit easier to swallow for a total newbie like myself.

With that said….
Could you provide a unicode example of this? I’m trying to do this example with TCHAR and I can’t get it to work.

#10

larry

January 9th, 2010 at 12:34 am

This is awesome. At least I have idea of how to use wprintf.

#11

franck

February 16th, 2010 at 2:01 am

can you please add a code of how to add a button to an empty c++ window …

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Latest Articles

Feb
19

Join a list of integers in Python

How do you run a string join on a list of integers in Python? After googling for about 10 mins, I gave up and did this. I am sure there is a better way of doing it! [Read More]
Jan
21

Writing a spider in 10 mins using Scrapy

I came across Scrapy a few days back and have grown to really love it. This tutorial will illustrate how you can write a simple spider using Scrapy to scrape data off Paul Smith. All this in 10 minutes. [Read More]

Featured Projects

Deebot

Deebot

Deeb0t is an IRC chat bot capable of making meaningful conversation with other users. It also responds to commands issued by its owner.

[Read More]

Indic to English Transliterator

Indic to English Transliterator

Transliteration is the process of converting a word from one language to another while retaining its phonetic characteristics. This application lets you convert a word from any major Indian language (currently supports Hindi, Marathi, Sanskrit and Bengali) to English.

[Read More]

This page and its contents are copyright © 2010, Pravin Paratey.