WndProc - The Window Callback Function

The WndProc is the function you write to receive all input directed at your window. You will have already told Windows to call this function with messages by supplying a pointer to this function in the class structure (it is a callback function). See the WinMain section for details of how to do this.

Advanced Note: because we supplied a pointer to a function to Windows the function cannot be a class member function unless it is defined as static, this is why the C code required by the Windows API is somewhat clumsy to use with object oriented programming.

WndProc definition

This is your function but you must define the parameters in the way Windows expects, you can call it anything you want but you must define it with parameters like this:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

  • LRESULT resolves to long and CALLBACK resolves to _stdcall, so your function should return a long.
  • hWnd - this is the unique handle of the window. Remember that many windows could be created from the same window class definition you previously registered. So if you created two windows from the class each will have a different handle but each will call this function with messages. Generally you just create one window from your window class.
  • message - this is the message your window has received, more on this in a minute.
  • wParam and lParam - each message sent to your window can have a number of extra values, e.g. if the message is that the mouse has been moved over your window then the extra parameters include the x, y screen position of the mouse.

Advanced Note: WPARAM is an unsigned int and LPARAM is a long. On a PC an int is 32 bits (4 bytes) and the wParam parameter often holds two values packed into one. A word is 16 bits so 2 words will fit in an int. To unpack the values there are two provided macros you can use: LOWORD(x) returns the low word packed into the int and HIWORD(x) returns the high word.

Messages

Whenever anything happens to your window, Windows will call this function telling you what has happened. The message parameter contains the message sent. You don't want to have to handle all the messages but just the ones you are interested in so any messages you decide not to handle should be passed back to Windows to handle in a default way. You do this by calling: DefWindowProc(...)

For the messages you do want to handle a switch statement can be used e.g.:

switch (message)
{
        //case WM_COMMAND:
          // handle menu selections etc.
        //break;
        //case WM_PAINT:
          // draw our window - note: you must paint something here or not trap it!
        //break;
        case WM_DESTROY:
             PostQuitMessage(0);
        break;
        default:
            // We do not want to handle this message so pass back to Windows
            // to handle it in a default way
            return DefWindowProc(hWnd, message, wParam, lParam);
}

return 0;

Note: you must return DefWindowProc if you do not handle a message or your Window will not appear. Also do not trap a message if you are not going to do anything with it e.g. do not trap WM_PAINT and then not do any painting as the window will not be redrawn.

The WM_DESTROY message must be handled, this message is sent if the user has closed the window. You should post a message telling windows to destroy the window as shown above.

The WM_COMMAND message is sent to your window when a menu item is selected or an accelerator key pressed. The low word of the wParam contains the id of the menu item (as you defined it in the resource editor).

The WM_PAINT message is sent when your window needs redrawing, this can happen when the window is created, when another window is removed from over the top of our window, when the window is maximized etc. As I mentioned earlier you can only draw to your window when Windows tells you to, however if you want to change what is displayed without waiting you can tell Windows that your window is dirty and needs a redraw by using this function:

InvalidateRect(hWnd,NULL,TRUE);

The first parameter is the handle of the window, the second is the rectangular are of the window that needs redrawing or NULL to indicate the whole window. The third parameter is TRUE if you want the current contents to be erased first.

Note: if you handle the WM_PAINT message you must actually do some drawing otherwise the window will not be refreshed. So if you do not want to do anything do not trap the message.

Note: there are loads and loads of types of messages that can be sent to your window, look in the MSDN help for details, I will just describe a few of the most useful here:

Message: WM_COMMAND

Sent when a menu item or accelerator key is pressed. The low word or wParam is the id of the menu item. So if you had defined in the resource editor a menu with two items, save and load and given them the ids IDM_SAVE and IDM_LOAD you might write:

case WM_COMMAND:
{

      switch(LOWORD(wParam))
      {
          case IDM_SAVE:
               SaveProject()
           break;
           case IDM_LOAD:
               LoadProject()
           break;
           default:
               break;
       }
}

 

Message: WM_KEYDOWN

This message is sent to your window when a key is pressed. Note that there is also a WM_KEYUP message. The wParam contains a virtual key code and the lParam specifies some extra information like the repeat count, extended-key flag, context code, previous key-state flag etc.

case WM_KEYDOWN:
{
     switch(wParam)
     {
         case 'W':
              // w key pressed
         break;
         case VK_RIGHT:
             // Right arrow pressed
         break;
         default:
             break;
     }
}

There are many virtual key codes available. As you can see above you can type the character (upper case) for letters and numbers but for special keys you need to use a code like VK_RIGHT, VK_LEFT, VK_HOME etc. These are all defined in Winuser.h.

It is not recommended to use WM_KEYDOWN for text entry as the keyboard layout depends a lot on the locality etc. instead you should use WM_CHAR.

Message: WM_MOUSEMOVE

This message is sent to your window when the mouse is moved over its surface area. wParam indicates if a specific key or mouse button is held down. The low word of lParam is the x position of the mouse and the high word is the y position. So to retrieve the position and button states:

case WM_MOUSEMOVE:
{

    // Retrieve mouse screen position
    int x=(short)LOWORD(lParam);
    int y=(short)HIWORD(lParam);

    // Check to see if the left button is held down:
    bool leftButtonDown=wParam & MK_LBUTTON;

    // Check if right button down:
    bool rightButtonDown=wParam & MK_RBUTTON;

}

In your game you may want to control a camera rotation dependant on the change in the mouse position. To do this you need to remember the previous mouse position and use the difference to map onto camera rotations. If you are running your game in a window you can get into problems when the mouse leaves the window area as you stop receiving mouse messages. To solve this you can capture the mouse, this means only your window receives messages from the mouse. Obviously you do not want to do this all the time or the user would not be able to work outside of your program so one way to do it is to capture the mouse only when the left button is first held down and then release it when the button is lifted.

To capture the mouse:

SetCapture(hWnd);

To release the mouse:

ReleaseCapture();

Messages: WM_LBUTTONDOWN, WM_RBUTTONDOWN

These are sent if the left button or right button on the mouse has been pressed.

Messages: WM_LBUTTONUP, WM_RBUTTONUP

These are sent if the left button or right button on the mouse has been released.

So that's it for WndProc. To summarise: it is the function called by Windows whenever a message is sent to your window - something has happened. There are loads of possible messages that you can handle or pass back to Windows if you don't want to. The lParam and wParam parameters hold additional information dependant on the type of message. Some of the most common ones are shown above but look in the MSDN help for a full list.

Back to Win32 API page



© 2004-2016 Keith Ditchburn