Home  Contents

First steps

Let's make our hands dirty by programming. Easy examples. Various topics. Just coding.

Simple program

Here is the most simple program. It will pop up a small dialog box.

#include <windows.h>

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int CmdShow)
{

    MessageBox(NULL, TEXT("First Program"), TEXT("First"), MB_OK );

    return 0;
}
 #include <windows.h>

We include the basic function declarations, constants, data types and structures. All these we will need in programming.

 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int CmdShow)

The entry to every application is the WinMain() function.

 MessageBox(NULL, TEXT("First Program"), TEXT("First"), MB_OK);

The MessageBox() function displays a simple message box. The first parameter is the owner window. In our case, the dialog box has no owner. The next two parameters provide the message text and the caption. The last parameter defines the message dialog type. We have a dialog box with one OK button.


Simple message box

Centering the window

In the next code example, we will center the window on the screen.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CenterWindow(HWND);

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
				LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Center" );
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClass(&wc);
  CreateWindow( wc.lpszClassName, TEXT("Center"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 250, 150, 0, 0, hInstance, 0);  

  while( GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
  switch(msg)  
  {
      case WM_CREATE:
      {
          CenterWindow(hwnd);
          return 0;
      }

      case WM_DESTROY:
      {
          PostQuitMessage(0);
          return 0;
      }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

void CenterWindow( HWND hwnd )
{
    RECT rc ;
    
    GetWindowRect ( hwnd, &rc ) ;
    
    SetWindowPos( hwnd, 0, 
        (GetSystemMetrics(SM_CXSCREEN) - rc.right)/2,
        (GetSystemMetrics(SM_CYSCREEN) - rc.bottom)/2,
         0, 0, SWP_NOZORDER|SWP_NOSIZE );
}
 case WM_CREATE:
 {
     CenterWindow(hwnd);
     return 0;
 }

We call the user defined CenterWindow() function during the WM_CREATE message.

More Windows

To create a window, we must first register it with the windows os. Then we create it by calling the CreateWindow() function. Each window has a window procedure. It is a function that is called by the OS, when user interact with the window. In the following window, we create three windows. One parent window and two child windows.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK PanelProc(HWND, UINT, WPARAM, LPARAM);
void RegisterRedPanel(void);
void RegisterBluePanel(void);


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Windows" );
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClass(&wc);
  CreateWindow( wc.lpszClassName, TEXT("Windows"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 250, 180, 0, 0, hInstance, 0);  

  while( GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{

    
  switch(msg)  
  {
    case WM_CREATE:
    {

	RegisterRedPanel();
	CreateWindow(TEXT("RedPanel"), NULL, 
		WS_CHILD | WS_VISIBLE,
		20, 20, 80, 80,
		hwnd, (HMENU) 1, NULL, NULL);

	RegisterBluePanel();
	CreateWindow(TEXT("BluePanel"), NULL, 
		WS_CHILD | WS_VISIBLE,
		120, 20, 80, 80,
		hwnd, (HMENU) 2, NULL, NULL);

	break;
    }

    case WM_DESTROY:
    {
        PostQuitMessage(0);
        break; 
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

LRESULT CALLBACK PanelProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{

  switch(msg)  
  {
    case WM_LBUTTONUP:
    {
        Beep(50, 40);
        break;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}


void RegisterRedPanel(void) {

  HBRUSH hbrush = CreateSolidBrush(RGB(255, 0, 0));

  WNDCLASS rwc = {0};
  rwc.lpszClassName = TEXT( "RedPanel" );
  rwc.hbrBackground = hbrush;
  rwc.lpfnWndProc   = PanelProc ;
  rwc.hCursor       = LoadCursor(0,IDC_ARROW);
  RegisterClass(&rwc);
  
}

void RegisterBluePanel(void) {

  HBRUSH hbrush = CreateSolidBrush(RGB(0, 0, 255));

  WNDCLASS rwc = {0};
  rwc.lpszClassName = TEXT( "BluePanel" );
  rwc.hbrBackground = hbrush;
  rwc.lpfnWndProc   = PanelProc ;
  rwc.hCursor       = LoadCursor(0,IDC_ARROW);
  RegisterClass(&rwc);
  
}

The two child windows have blue and red backgrounds.

  HBRUSH hbrush = CreateSolidBrush(RGB(255, 0, 0));
  ...
  rwc.hbrBackground = hbrush;

To create a coloured window background, we create a custom solid brush by calling the CreateSolidBrush() function. To specify a colour, we use the RGB macro. As we know, any color can be created by combining red, green a blue colours. Then we set the hbrBackground parameter of the window class structure to this newly created brush.


Figure: Windows
  RegisterRedPanel();
  CreateWindow(TEXT("RedPanel"), NULL, 
	WS_CHILD | WS_VISIBLE,
	20, 20, 80, 80,
	hwnd, (HMENU) 1, NULL, NULL);

When we register a window, we must create it before we register another window.

Both of our child windows share the PanelProc window procedure. This procedure is called by the windows os, when we interact with it.

  case WM_LBUTTONUP:
  {
      Beep(50, 40);
      break;
  }

We interact with our child windows, when we click on them. By left clicking on the child window, the Windows OS calls the child window procedure and sends a WM_LBUTTONUP message. In our example, we call the Beep() function. If we left click on the background of the two child windows, we hear a beep sound. If we click outside, we don't hear anything.

The escape key

It is a common feature to close a window by pressing the escape key. We will show, how we can do it using windows api.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Escape" );
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClass(&wc);
  CreateWindow( wc.lpszClassName, TEXT("Escape"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 250, 180, 0, 0, hInstance, 0);  

  while( GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
    
  switch(msg)  
  {
    case WM_KEYDOWN:
	 if (wParam == VK_ESCAPE) {
	     int ret = MessageBox(NULL, TEXT("Are you sure to quit?"), TEXT("Message"), MB_OKCANCEL);
	     if ( ret == IDOK) {
	         SendMessage(hwnd, WM_CLOSE, 0, 0);
	     }
	 }
	 break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break; 
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

It is a good practice to ask if we really want to close the window. If we have a clock or a calculator, than it does not matter. But if we have a text editor or a drawing application, it matters. We might accidentally press the escape key and loose all our modifications.

 case WM_KEYDOWN:
     if (wParam == VK_ESCAPE) {
         int ret = MessageBox(NULL, TEXT("Are you sure to quit?"), TEXT("Message"), MB_OKCANCEL);
         if ( ret == IDOK) {
	      SendMessage(hwnd, WM_CLOSE, 0, 0);
	  }
     }
     break;

If we press a key, the window procedure receives a WM_KEYDOWN message. The wParam parameter has a key code. We can close a window by sending a WM_CLOSE message.

Moving a window

Let's move the window on the screen. When we move the window, the window procedure receives the WM_MOVE message. In our example we display the current window position on the screen.

Watch out! In this example do not use the unicode build. It will not work. I want to keep the examples as easy as possible. We will be covering the UNICODE later on. In Visual C++ 2005 Express Edition, click on the project, select properties. Select Configuration properties, General. See Project Defaults, Character set. The default value is Use Unicode Character Set. Select Not Set and click apply.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void CreateLabels(HWND);

HWND hwndSta1;
HWND hwndSta2;

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			LPSTR lpCmdLine, int nCmdShow )
{
  HWND hwnd;
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = "Escape";
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClass(&wc);
  hwnd = CreateWindow( wc.lpszClassName, "Moving",
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                150, 150, 250, 180, 0, 0, hInstance, 0);  
		
  CreateLabels(hwnd);		

  while( GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{

  char buf[10];
  RECT rect;

  switch(msg)  
  {
    case WM_MOVE:
	GetWindowRect(hwnd, &rect);

	_itoa(rect.left, buf, 10);
	SetWindowText(hwndSta1, buf);

	_itoa(rect.top, buf, 10);
	SetWindowText(hwndSta2, buf);

	break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break; 
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

void CreateLabels(HWND hwnd){

  CreateWindow("static", "x: ",
      WS_CHILD | WS_VISIBLE,
      10, 10, 25, 25, 
      hwnd, (HMENU) 1, NULL, NULL);
				
  hwndSta1 = CreateWindow("static", "150",
      WS_CHILD | WS_VISIBLE,
      40, 10, 55, 25, 
      hwnd, (HMENU) 2, NULL, NULL);

  CreateWindow("static", "y: ",
      WS_CHILD | WS_VISIBLE,
      10, 30, 25, 25, 
      hwnd, (HMENU) 3, NULL, NULL);

  hwndSta2 = CreateWindow("static", "150",
      WS_CHILD | WS_VISIBLE,
      40, 30, 55, 25, 
      hwnd, (HMENU) 4, NULL, NULL);
}

Normally, I would create the static text controls during the WM_CREATE message. This was not possible. Because the window is being moved during the creation. This way I received error messages. I accessed windows that did not exist yet. That's why I put the creation of the static text windows in a separate function, which is called immediately after the main window is created.

void CreateLabels(HWND hwnd){

  CreateWindow("static", "x: ",
      WS_CHILD | WS_VISIBLE,
      10, 10, 25, 25, 
      hwnd, (HMENU) 1, NULL, NULL);
				
  hwndSta1 = CreateWindow("static", "150",
      WS_CHILD | WS_VISIBLE,
      40, 10, 55, 25, 
      hwnd, (HMENU) 2, NULL, NULL);

  CreateWindow("static", "y: ",
      WS_CHILD | WS_VISIBLE,
      10, 30, 25, 25, 
      hwnd, (HMENU) 3, NULL, NULL);

  hwndSta2 = CreateWindow("static", "150",
      WS_CHILD | WS_VISIBLE,
      40, 30, 55, 25, 
      hwnd, (HMENU) 4, NULL, NULL);
}

There are four static text controls. Two of them change during the life of the application. So we need only two handles.

  case WM_MOVE:
    GetWindowRect(hwnd, &rect);

    _itoa(rect.left, buf, 10);
    SetWindowText(hwndSta1, buf);

    _itoa(rect.top, buf, 10);
    SetWindowText(hwndSta2, buf);
    break;

To get the window coordinates, we call the GetWindowRect() function. The coordinate is a number. We must convert the number to characters. To accomplish this, we use the _itoa() function call.


Figure: moving a window

Flashing a window

Sometimes when an important event happens, the title bar or/and the taskbar button start to flash. The flashing is the change of the title bar from inactive status to active status and vice versa. This is a common feature in Miranda IM, when we receive a new message.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
			LPSTR lpCmdLine, int nCmdShow )
{
  MSG  msg ;    
  WNDCLASS wc = {0};
  wc.lpszClassName = TEXT( "Flash" );
  wc.hInstance     = hInstance ;
  wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
  wc.lpfnWndProc   = WndProc ;
  wc.hCursor       = LoadCursor(0,IDC_ARROW);
  
  RegisterClass(&wc);
  CreateWindow( wc.lpszClassName, TEXT("Flash"),
                WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                100, 100, 250, 180, 0, 0, hInstance, 0);  

  while( GetMessage(&msg, NULL, 0, 0)) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return (int) msg.wParam;
}

LRESULT CALLBACK WndProc( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
  FLASHWINFO fwi;

  switch(msg)  
  {
      case WM_CREATE:
          CreateWindow(TEXT("Button"), TEXT("Flash"),
		  WS_CHILD | WS_VISIBLE,
		  10, 10, 80, 25, 
		  hwnd, (HMENU) 1, NULL, NULL);
          break;

      case WM_COMMAND:
				
          fwi.cbSize = sizeof(fwi);
          fwi.dwFlags = FLASHW_ALL;
          fwi.dwTimeout = 0;
          fwi.hwnd = hwnd;
          fwi.uCount = 4;

          FlashWindowEx(&fwi);
          break;

      case WM_DESTROY:
          PostQuitMessage(0);
          break; 
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

In order to flash a window, we must do two steps. Create and fill a FLASHWINFO structure and call the FlashWindowEx() function.

 fwi.dwFlags = FLASHW_ALL;

We have set the FLASHW_ALL flag. This will flash both the title bar and the taskbar button. To flash only the titlebar, we use FLASHW_CAPTION. To flash the taskbar button, we can use the FLASHW_TRAY flag.

 fwi.dwTimeout = 0;

The dwTimeout member is the rate at which the window is to be flashed, in milliseconds. If dwTimeout is zero, the function uses the default cursor blink rate.

 fwi.hwnd = hwnd;
 fwi.uCount = 4;

Here we set which window to flash and how many times. In our case, we will flash the main window four times.

 FlashWindowEx(&fwi);

This function call actually starts the flashing.