Windows Programming
Windows Programming
In a graphical Windows-based application, a window is a rectangular area of the screen where the application displays
output and receives input from the user. Therefore, one of the first tasks of a graphical Windows-based application is to
create a window.
A window shares the screen with other windows, including those from other applications. Only one window at a time
can receive input form the user (the window which has focus). The user can use the mouse, keyboard, or other input
device to interact with this window and the application that owns it.
About Windows
1. The Desktop Window
When you start the system, it automatically creates the desktop window. The desktop window is a system-defined
window that paints the background of the screen and serves as the base for all windows displayed by all applications.
The desktop window uses a bitmap to paint the background of the screen. The pattern created by the bitmap is called
the desktop wallpaper. By default, the desktop window uses the bitmap from a .bmp file specified in the registry as the
desktop wallpaper.
A system configuration application, such as a Control Panel item, changes the desktop wallpaper by using the
SystemParametersInfo() function with the wAction parameter set to SPI_SETDESKWALLPAPER and the
lpvParam parameter specifying a bitmap file name. SystemParametersInfo() then loads the bitmap from the
specified file, uses the bitmap to paint the background of the screen, and enters the new file name in the registry.
2. Application Windows
Every graphical Windows-based application creates at least one window called the main window, that serves as the
primary interface between the user and the application. Most applications also create other windows, either directly or
indirectly, to perform tasks related to the main window. Each window plays a part in displaying output and receiving
input form the user.
When you start an application, the system also associates a taskbar button with the application. The taskbar button
contains the program icon and title. When the application is active, its taskbar button is displayed in the pushed state.
a title bar
a menu bar
the window menu (formerly known as the system menu)
the minimize button
the maximize button
the restore button
the close button
a sizing border
a client area
a horizontal scroll bar
a vertical scroll bar
An application's main window typically includes all of these components. The following illustration shows these
components in a typical main window:
3. Client Area
The client area is the part of a window where the application displays output, such as text or graphics. For example, a
desktop publishing application displays the current page of a document in the client area. The application must provide a
function, called a window procedure, to process input to the window and display output in the client area.
4. Nonclient Area
The title bar, menu bar, window (or system) menu, minimize and maximize buttons, sizing border, and scroll bars, are
referred to collectively as the window's nonclient area. The system manages most aspects of the nonclient area. The
application manages the appearance and behavior of its client area.
When you click the maximize button, the system enlarges the window to the size of the screen and positions the
window, so it covers the entire desktop, minus the taskbar. At the same time, the system replaces the maximize
button with the restore button.
When you click the restore button, the system restores the window to its previous size and position.
When you click the minimize button, the system reduces the window to the size of its taskbar button, positions
the window over the taskbar button, and displays the taskbar button in its normal state. To restore the
application to its previous size and position, click its taskbar button.
When you click the close button, the apllication exits.
A control is a window that an application uses to obtain a specific piece of information from the user, such as the name
of a file to open, or the desired point size of a text selection. Applications use controls to obtain information needed to
control a particular feature of an application. For example, a word-processing application typically provides a control to
let the user turn word wrapping on and off.
Controls are always used in conjunction with another window - typically, a dialog box. A dialog box is a window that
contains one or more controls. An application uses a dialog box to prompt the user for input needed to complete a
command. For example, an application that includes a command to open a file would display a dialog box that includes
controls in which the user specifies a path and a file name. Dialog boxes do not typically use the same set of window
components as does a main window. Most have a title bar, a window menu, a border (non-sizing), and a client area, but
they typically do not have a menu bar, minimize and maximize buttons, or scroll bars.
A message box is a special dialog box that displays a note, caution, or warning to the user. For example, a message box
can inform the user of a problem the application has encountered while performing a task.
6. Window Attributes
An application must provide the following information when creating a window (with the exception of the window
handle, which the creation function returns to uniquely identify the new window):
class name
window name
window style
extended window style
position
size
parent or owner window handle
menu handle or child-window identifier
application instance handle
creation data
window handle
6.1 The Class Name
Every window belongs to a window class. An application must register a window class before creating any windows of
that class. The window class defines most aspects of a window's appearance and behavior. The chief component of a
window class is the window procedure, a function that receives and processes all input and requeste sent to the window.
The system provides the input and requests in the form of messages.
To change the window name after creating the window, use the SetWindowText() function. This function uses the
GetWindowTextLength() and GetWindowText() functions to retrieve the current window-name string from
the window
The system and, to some extent, the window procedure for the class, interpret the window styles.
Some window styles apply to all windows, but most apply to windows of specific window classes. The general window
styles are represented by constants that begin with the WS_ prefix. They can be combined with the OR operator to form
different types of windows, including main windows, dialog boxes, and child windows. The class-specific window styles
define the appearance and behavior of windows belonging to the predefined control classes. For example, the
SCROLLBAR class specifies a scroll bar control, but the SBS_HORZ and SBS_VERT styles determine whether a
horizontal or vertical scroll bar control is created.
6.5 Position
A window's position is defined as the coordinates of its upper left corner:
These coordinates, sometimes called window coordinates, are always relative to the upper left corner of the
screen.
For a child window, these coordinates are relative to the upper left corner of the parent window's client area.
For example, a top-level window having the coordinates (10,10) is placed 10 pixels to the right of the upper left corner of
the screen and 10 pixels down from it. A child window having the coordinates (10, 10) is placed 10 pixels to the right of
the upper left corner of its parent window's client area and 10 pixels down form it.
The WindowFromPoint() function retrieves a handle to the window occupying a particular point on the screen.
Similarly, the ChildWindowFromPoint() and ChildWindowFromPointEx() functions retrieve a handle to the
child window occupying a particular point in the parent window's client area. Although
ChildWindowFromPointEx() can ignore invisible, disabled, and transparent child windows,
ChildWindowFromPoint() cannot.
6.6 Size
A window's size (width and height) is given in pixels. A window can have zero width or height. If an application sets a
window's width and height to zero, the system sets the size to the default minimum window size. To discover the
default minimum window size, an application uses the GetSystemMetrics() function with the SM_CXMIN and
SM_CYMIN flags.
A window can own, or be owned by, another window. An owned window always appears in front of its owner window, is
hidden when its owner window is minimized, and is destroyed when its owner window is destroyed.
Every window, except a child window, can have a menu. An application can include a menu by providing a menu handle
either when registering the window's class or when creating the window.
An application can use the FindWindow() function to discover whether a window with the specified class name or
window name exists in the system. If such a window exists, FindWindow() returns a handle to the window. To limit
the search to the child windows of a particular application, use the FindWindowEx() function.
The IsWindow() function determines whether a window handle identifies a valid existing window.
There are special constants that can replace a window handle in certain functions. For example, an application can use
HWND_BROADCAST in the SendMessage() and SendMessageTimeout() functions, or HWND_DESKTOP in the
MapWindowPoints() function.
7. Window Creation
To create application windows, use the CreateWindow() or CreateWindowEx() function. You must provide the
information required to define the window attributes. CreateWindowEx() has a parameter, dwExStyle, that
CreateWindow() does not have. Otherwise, the functions are identical. In fact, CreateWindow() simply calls
CreateWindowEx() with the dwExStyle parameter set to zero.
It should be noted that there are additional functions for creating special-purpose windows such as dialog boxes and
message boxes (such as DialogBox(), CreateDialog() and MessageBox()).
Your WinMain() function can also limit your application to a single instance. Create a named mutex using the
CreateMutex() function. If GetLastError() returns ERROR_ALREADY_EXISTS, another instance of your
application exists (it created the mutext) and you should exit WinMain().
The system does not automatically display the main window after creating it. Instead, an application must use the
ShowWindow() function to display the main window. After creating the main window, the application's WinMain()
function calls ShowWindow(), passing it two parameters: a handle to the main window and a flag specifying whether
the main window should be minimized or maximized when it is first displayed. Normally, the flag can be set to any of the
constants beginning with the SW_ prefix. However, when ShowWindow() is called to display the application's main
window, the flag must be set to SW_SHOWDEFAULT. This flag tells the system to display the window as directed by the
program that started the application.
If a window class was registered with the Unicode version of RegisterClass(), the window receives only Unicode
messages. To determine whether a window uses the Unicode character set or not, call IsWindowUnicode().
WM_NCCREATE - after creating the window's nonclient area (before creating the window's client area)
WM_CREATE - after creating the window's client area
The window procedures receives both messages before the system displays the window. Both messages include a
pointer to a CREATESTRUCT structure that contains all the information specified in the CreateWindowEx()
function. Typically, the window procedure performs initialization tasks upon receiving these messages.
When creating a child window, the system sends the WM_PARENTNOTIFY message to the parent window after sending
the WM_NCCREATE and WM_CREATE messages. It also sends other messages while creating a window. The number
and order of these messages depend on the window class and style and on the function used to create the window.
The GetWindowThreadProcessId() function returns the identifier of the thread that created a particular window.
To set the show state of a window created by another thread, use the ShowWindowAsync() function.
Window Features
1. Window Types
This section describes the following types of windows:
Overlapped windows
Pop-up windows
Child windows
Layered windows
Message-only windows
You create a pop-up window by specifying the WS_POPUP style in CreateWindowEx(). To include a title bar, specify
the WS_CAPTION style. Use the WS_POPUPWINDOW style to create a pop-up window that has a border and a window
menu. The WS_CAPTION style must be combined with the WS_POPUPWINDOW style to make the window menu visible.
A child window must have a parent window. The parent window can be:
An overlapped window
A pop-up window
A child window
You specify the parent window when you call CreateWindowEx(). If you specify the WS_CHILD style in
CreateWindowEx() but do not specify a parent window, the system does not create the window.
A child window has a client area but no other features, unless they are explicitly requested. An application can request a
title bar, a window menu, minimize and maximize buttons, a border, and scroll bars for a child window, but a child
window cannot have a menu. If the application specifies a menu handle, either when it registers the child's window class
or creates the child window, the menu handle is ignored. If no border style is specified, the system creates a borderless
window. An application can use borderless child windows to divide a parent window's client area while keeping the
divisions invisible to the user.
No part of a child window ever appears outside the borders of its parent window. If an application creates a child
window that is larger than the parent window or positions a child window so that some or all of the child window
extends beyond the borders of the parent, the system clips the child window. That is, the portion outside the parent
window's client area is not displayed.
Actions that affect the parent window can also affect the child window, as follows:
A child window can overlap other child windows in the same client area. A child window that shares the same parent
window as one or more other child windows is called a sibling window. Sibling windows can draw in each other's client
area, unless one of the child window has the WS_CLIPSIBLINGS style. If a child window does have this style any
portion of its sibling window that lies within the child window is clipped.
If a window has either the WS_CLIPCHIDREN or WS_CLIPSIBLINGS style, a slight loss in performance occurs. Each
window takes up system resources, so an application should not use child windows indiscriminately. For best
performance, an application that needs to logically divide its main window should do so in the window procedure of the
main window rather than by using child windows.
The parent window relinquishes a portion of its client area to a child window, and the child window receives all input
from this area. The window class need not be the same for each of the child windows of the parent window. This means
that an application can fill a parent window with child windows that look different and carry out different tasks. For
example, a dialog box can contain many types of controls, each one a child window that accepts different types of data
from the user.
A child window has only one parent window, but a parent can have any number of child windows. Each child window, in
turn, can have child windows. In this chain of windows, each child window is called a descendant window of the original
parent window. An application uses the IsChild() function to discover whether a given window is a child window or
a descendant window of a given parent window.
The EnumChildWindows() function enumerates the child windows of a parent window. Then,
EnumChildWindows() passes the handle to each child window to an application-defined callback function.
Descendant windows of the given parent window are also enumerated.
A child window can have a unique integer identifier. Child window identifiers are important when working with control
windows. An application directs a control's activity by sending it messages. The application uses the control's child
window identifier to direct the messages to the control. In addition, a control sends notification messages to its parent
window. A notification message includes the control's child window identifier, which the parent uses to identify which
control sent the message. An application specifies the child-window identifier for other types of child windows by setting
the hMenu parameter of the CreateWindowEx() function to a value rather than a menu handle.
To create a layered window, specify the WS_EX_LAYERED extended window style when calling the
CreateWindowEx() function, or call the SetWindowLong() function to set WS_EX_LAYERED after the window
has been created. After the CreateWindowEx() function call, the layered window will not become visible until the
SetLayeredWindowAttributes() or UpdateLayeredWindow() function has been called for this window.
It should be noted that beginning with Windows 8, WS_EX_LAYERED can be used with child windows and top-level
windows. Previous Windows versions support WS_EX_LAYERED only for top-level windows.
To set the opacity level or the transparency color key for a given layered window, call
SetLayeredWindowAttributes(). After the call, the system may still ask the window to paint when the window
is shown or resized. However, because the system stores the image of a layered window, the system will not ask the
window to paint if parts of it are revealed as a result of relative window moves on the desktop. Legacy applications do
not need to restructure their painting code if they want to add translucency or transparency effects for a window,
because the system redirects the painting of windows that called SetLayeredWindowAttributes() into off-
screen memory and recomposes it to achieve the desired effect.
For faster and more efficient animation or if per-pixel alpha is needed, call UpdateLayeredWindow().
UpdateLayeredWindow() should be used primarily when the application must directly supply the shape and
content of a layered window, without using the redirection mechanism the system provides through
SetLayeredWindowAttributes(). In addition, using UpdateLayeredWindow() directly uses memory more
efficiently, because the system does not need the additional memory required for storing the image of the redirected
window. For maximum efficiency in animating windows, call UpdateLayeredWindow() to change the position and
the size of a layered window. Please note that after SetLayeredWindowAttributes() has been called,
subsequent UpdateLayeredWindow() calls will fail until the layering style bit is cleared and set again.
Hit testing of a layered window is based on the shape and transparency of the window. This means that the areas of the
window that are color-keyed or whose alpha value is zero will let the mouse messages through. However, if the layered
window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and
the mouse events will be passed to other windows underneath the layered window.
To create a message-only windows, specify the HWND_MESSAGE constant or a handle to an existing message-only
window in the hWndParent parameter of the CreateWindowEx() function. You can also change an existing
window to a message-only window by specifying HWND_MESSAGE in the hWndNewParent parameter of the
SetParent() function.
To find message-only windows, specify HWND_MESSAGE in the hWndParent parameter of the FindWindowsEx()
function. In addition, FindWindowsEx() searches message-only windows as well as top-level windows if both the
hWndParent and hWndChildAfter parameters are NULL.
2. Window Relationships
There are many ways that a window can relate to the user or another window. A window may be an owned window,
foreground window, or background window. A window also has a z-order relative to other windows.
Each thread has a priority level that determines the amount of CPU time the thread receives. Although an application
can set the priority level of its threads, normally the foreground thread has a slightly higher priority level than the
background threads. Because it has a higher priority, the foreground thread receives more CPU time than the
background threads. The foreground thread has a normal base priority of 9. A background thread has a normal base
priority of 7.
The user sets the foreground window by clicking a window, or by using the ALT + TAB or ALT + ESC key combination. To
retrieve a handle to the foreground window, use the GetForegroundWindow() function. To check if your
application window is the foreground window, compare the handle returned by GetForegroundWindow() to that
of your application window.
The system restricts which processes can set the foreground window. A process can set the foreground window only if
one of the following conditions is true:
A process that can set the foreground window can enable another process to set the foreground window by calling the
AllowSetForegroundWindow() function, or by calling the BroadcastSystemMessage() function with the
BSF_ALLOWSFW flag. The foreground process can disable calls to SetForegroundWindow() by calling the
LockSetForegroundWindow() function.
Only an overlapped or pop-up window can be an owner window. A child window cannot be an owner window. An
application creates an owned window by specifying the owner's window handle as the hWndParent parameter of
CreateWindowEx() when it creates a window with the WS_OVERLAPPED or WS_POPUP style. The hWndParent
parameter must identify an overlapped or pop-up window. If hWndParent identifies a child window, the system
assigns ownership to the top-level parent window of the child window. After creating an owned window, an application
cannot transfer ownership of the window to another window.
Dialog boxes and message boxes are owned windows by default. An application specifies the owner window when
calling a function that creates a dialog box or message box.
An application can use the GetWindow() function with the GW_OWNER flag to retrieve a handle to a window's owner.
2.3 Z-Order
The z-order of a window indicates the window's position in a stack of overlapping windows. This window stack is
oriented along an imaginary axis, the z-axis, extending outward from the screen. The window at the top of the z-order
overlaps all other windows. The window at the bottom of the z-order is overlapped by all other windows.
The system maintains the z-order in a single list. It adds windows to the z-order based on whether they are topmost
windows, top-level windows, or child windows. A topmost window overlaps all other non-topmost windows, regardless
of whether it is the active or foreground window. A topmost window has the WS_EX_TOPMOST style. All topmost
windows appear in the z-order before any non-topmost windows. A child window is grouped with its parent in z-order.
When an application creates a window, the system puts it at the top of the z-order for windows of the same type. You
can use the BringWindowToTop() function to bring a window to the top of the z-order for windows of the same
type. You can rearrange the z-order by using the SetWindowPos() and DeferWindowPos() functions.
The user changes the z-order by activating a different window. The system positions the active window at the top of the
z-order for windows of the same type. When a window comes to the top of z-order, so do its child windows. You can use
the GetTopWindow() function to search all child windows of a parent window and return a handle to the child
window that is highest in z-order. The GetNextWindow() function retrieves a handle to the next or previous window
in z-order.
3.Window Show State
At any one given time, a window may be active or inactive, hidden or visible, and minimized, maximized, or restored.
These qualities are referred to collectively as the window state.
Only one top-level window in the system is active at a time. The user activates a top-level window by clicking on it (or
one of its child windows), or by using the ALT + ESC or ALT + TAB key combination. An application activates a top-level
window by calling the SetActiveWindow() function. Other functions can cause the system to activate a different
top-level window, including SetWindowPos(), DeferWindowPos(), SetWindowPlacement(), and
DestroyWindow(). Although an application can activate a different top-level window at any time, to avoid confusing
the user, it should do so only in response to a user action. An application uses the GetActiveWindow() function to
retrieve the handle to the active window.
When the activation changes from a top-level window of one application to the top-level window of another, the system
sends a WM_ACTIVATEAPP message to both applications, notifying them of the change. When the activation changes
to a different top-level window in the same application, the system sends both windows a WM_ACTIVATE message.
By default, a window is enabled when created. An application can specify the WS_DISABLED style, however, to disable
a new window. An application enables or disables an existing window by using the EnableWindow() function. The
system sends a WM_ENABLE message to a window when its enabled state is about to change. An application can
determine whether a window is enabled by using the IsWindowEnabled() function.
When a child window is disabled, the system passes the child's mouse input messages to the parent window. The parent
uses the messages to determine whether to enable the child window.
Only one window at a time can receive keyboard input - that window is said to have the keyboard focus. If an application
uses the EnableWindow() function to disable a keyboard-focus window, the window loses the keyboard focus in
addition to being disabled. EnableWindow() then sets the keyboard focus to NULL meaning no window has the
focus. If a child window, or other descendant window, has the keyboard focus, the descendant window loses the focus
when the parent window is disabled.
A window is visible when the WS_VISIBLE style is set for the window. By default, the CreateWindowEx() function
creates a hidden window unless the application specifies the WS_VISIBLE style. Typically, an application sets the
WS_VISIBLE style after it has created a window to keep details of the creation process hidden from the user. For
example, an application may keep a new window hidden while it customizes the window's appearance. If the
WS_VISIBLE style is specified in CreateWindowEx(), the system sends the WM_SHOWWINDOW message to the
window after creating the window, but before displaying it.
An application can determine whether a window is visible by using the IsWindowVisible() function. An application
can show (make visible) or hide a window by using the ShowWindow(), SetWindowPos(), DeferWindowPos(),
or SetWindowPlacement() or SetWindowLong() function. These functions show or hide a window by setting or
removing the WS_VISIBLE style for the window. They also send the WM_SHOWWINDOW message to the window
before showing or hiding it.
When a parent window is visible, its associated child windows are also visible. Similarly, when the parent window is
hidden, its child windows are also hidden. Minimizing the parent window has no effect on the visibility state of the child
windows. That is, the child windows are minimized along with the parent, but the WS_VISIBLE style is not changed.
Even if a window has the WS_VISIBLE style, the user may not be able to see the window on the screen. Other
windows may completely overlap it or it may have been moved beyond the edge of the screen. Also, a visible child
window is subject to the clipping rules established by its parent-child relationship. If the parent window moves beyond
the edge of the screen, the child window also moves because a child window is drawn relative to the parent's upper left
corner. For example, a user may move the parent window containing the child window far enough off the edge of the
screen that the user may not be able to see the child window, even though the child window and its parent window
both have the WS_VISIBLE style.
A minimized window is a window that has the WS_MINIMIZED style. By default, the system reduces a minimized
window to the size of its taskbar button and moves the minimized window to the taskbar. A restored window is a
window that has been returned to its previous size and position, that is, the size it was before it was minimized or
maximized.
If an application specifies the WS_MAXIMIZE or WS_MINIMIZE style in the CreateWindowEx() function, the
window is initially maximized or minimized. After creating a window, an application can use the CloseWindow()
function to minimize the window. The ArrangeIconicWindows() function arranges the icons on the desktop, or it
arranges a parent window's minimized child windows in the parent window. The OpenIcon() function restores a
minimized window to its previous size and position.
The ShowWindow() function can minimize, maximize, or restore a window. It can also set the window's visibility and
activation states. The SetWindowPlacement() function includes the same functionality as ShowWindow(), but it
can override the window's default minimized, maximized, and restored positions.
The IsZoomed() and IsIconic() functions determine whether a given window is maximized or minimized,
respectively. The GetWindowPlacement() function retrieves the minimized, maximized, and restored positions for
the window, and als determines the window's show state.
When the system receives a command to maximize or restore a minimized window, it sends the window a
WM_QUERYOPEN message. If the window procedure returns FALSE, the system ignores the maximize or restore
command.
The system automatically sets the size and position of a maximized window to the system-defined defaults for a
maximized window. To override these defaults, an application can either call the SetWindowPlacement() function
or process the WM_GETMINMAXINFO message that is received by a window when the system is about to maximize the
window. WM_GETMINMAXINFO includes a pointer to a MINMAXINFO structure containing values the system uses to
set the maximized size and position. Replacing these values overrides the defaults.
The coordinates of a top-level window are relative to the upper left corner of the screen.
The coordinates of a child window are relative to the upper left corner of the parent window.
An application specifies a window's initial size and position when it creates the window, but it can change the window's
size and position at any time.
Specifying CW_USEDEFAULT when creating a child or pop-up windowo causes the system to set the window's size to
the default minimum window size.
Tracking Size
The system maintains a minimum and maximum tracking size for a window of the WS_THICKFRAME style. A window
with this tyle has a sizing border. The minimum tracking size is the smallest window size you can produce by dragging
the window's sizing border. Similarly, the maximum tracking size is the largest window size you can produce by dragging
the sizing border.
A window's minimum and maximum tracking sizes are set to system-defined default values when the system creates the
window. An application can discover the defaults and override them by processing the WM_GETMINMAXINFO message.
System Commands
An application that has a window menu can change the size and position of the window by sending system commands.
System commands are generated when the user chooses commands from the window menu. An application can
emulate the user action by sending a WM_SYSCOMMAND message to the window. The following system commands
affect the size and position of a window:
Command Description
Closes the window. This command sends a WM_CLOSE message to the window. The
SC_CLOSE
window carries out any steps needed to clean up and destroy itself.
SC_MAXIMIZE Maximizes the window.
SC_MINIMIZE Minimizes the window
SC_MOVE Moves the window.
SC_RESTORE Restores a minimized or maximized window to its previous size and position.
SC_SIZE Starts a size command. To change the size of the window, use the mouse or keyboard.
SetWindowPlacement() - Sets a window's minimized position, maximized position, restored size and
position, and show state.
MoveWindow() - Sets the size or position of a single application window.
SetWindowPos() - Sets the size or position of a single application window. This function includes a set of
flags that affect the window's show state.
DeferWindowPos() - Use the BeginDeferWindowPos(), DeferWindowPos(), and
EndDeferWindowPos() functions to simultaneously set the position of a number of windows, including the
size, position, position in the z-order, and show state.
An application can retrieve the coordinates of a window's bounding rectangle by using the GetWindowRect()
function. GetWindowRect() fills a RECT structure with the coordinates of the window's upper left and lower right
corners. The coordinates are relative to the upper left corner of the screen, even for a child window. The
ScreenToClient() or MapWindowPoints() function maps the screen coordinates of a child window's bounding
rectangle to coordinates relative to the parent window's client area.
The GetClientRect() function retrieves the coordinates of a window's client area. GetClientRect() fills a
RECT structure with the coordinates of the upper left and lower right corners of the client area, but the coordinates are
relative to the client area itself. This means the coordinates of a client area's upper left corner are always (0, 0), and the
coordinates of the lower right corner are the width and height of the client area.
The CascadeWindows() function cascades the windows on the desktop or cascades the child windows of the
specified parent window. The TileWindows() function tiles the windows on the desktop or tiles the child windows of
the specified parent window.
The system sends the WM_WINDOWPOSCHANGING message to a window whose size, position, position in the z-order,
or show state is about to change. This message includes a pointer to a WINDOWPOS structure that specifies the
window's new size, position, position in the z-order, and show state. By setting the members of WINDOWPOS, an
application can affect the window's new size, position, and appearance.
After changing a window's size, position in the z-order, or show state, the system sends the WM_WINDOWPOSCHANGED
message to the window. This message includes a pointer to WINDOWPOS that informs the window of its new size,
position, position in the z-order, and show state. Setting the members of the WINDOWPOS structure that is passed with
WM_WINDOWPOSCHANGED has no effect on the window. A window that must process WM_SIZE and WM_MOVE
messages must pass WM_WINDOWPOSCHANGED to the DefWindowProc() function. Otherwise, the system does not
send the WM_SIZE and WM_MOVE messages to the window.
The system sends the WM_NCCALCSIZE message to a window when the window is created or sized. The system uses
the message to calculate the size of a window's client area and the position of the client area relative to the upper left
corner of the window. A window typically passes this message to the default window procedure. However, this message
can be useful in applications that customize a window's nonclient area or preserve portions of the client area when the
window is sized.
Window Animation
You can produce special effects when showing or hiding windows by using the AnimateWindow() function. When the
window is animated in this manner, the system will either roll, slide, or fade the window, depending on the flags you
specify in a call to AnimateWindow().
By default, the system uses the roll animation. With this effect, the window appears to roll open (showing the window)
or roll closed (hiding the window). You can use the dwFlags parameter to specify whether the window rolls
horizontally, vertically, or diagonally.
When you specify the AW_SLIDE flag, the system uses slide animation. With this effect, the window appears to slide
into view (showing the window) or slide out of view (hiding the window). You can use the dwFlags parameter to
specify whether the window slides horizontally, vertically, or diagonally.
When you specify the AW_BLEND flag, the system uses an alpha-blended fade.
You can also use the AW_CENTER flag to make a window appear to collapse inward or expand outward.
Window Destruction
In general, an application must destroy all the windows it creates. It does this by using the DestroyWindow()
function. When a window is destroyed, the system hides the window, fi it is visible, and then removes any internal data
associated with the window. This invalidates the window handle, which cna no longer be used by the application.
An application destroys many of the windows it creates soon after creating them. For example, an application usually
destroys a dialog box window as soon as the application has sufficient input form the user to continue its task. An
application eventually destroys the main window of the application (before temrinating).
Before destroying a window, an application should save or remove any data associated with the window, and it should
release any system resources allocated for the window. If the application does not release the resources, the system will
free any resources not freed by the application.
Destroying a window does not affect the window class from which the window is created. New windows can still be
created using that class, and any existing windows of that class continue to operate. Destroying a window also destroys
the window's descendant windows. The DestroyWindow() function sends a WM_DESTROY message first to the
window, then to its child windows and descendant windows. In this way, all descendant windows of the window being
destroyed are also destroyed.
A window with a window menu receives a WM_CLOSE message when the user clicks Close. By processing this message,
an application can prompt the user for confirmation before destroying the window. If the user confirms that the window
should be destroyed, the application can call the DestroyWindow() function to destroy the window.
If the window being destroyed is the active window, both the active and focus states are transferred to another window.
The window that becomes the active window is the next windows, as determined by the ALT + ESC key combination. The
new active window then determines which window receives the keyboard focus.
Using Windows
Creating a Main Window
The first window an application creates is typically the main window. You create the main window by using the
CreateWindowEx() function, specifying the window class, window name, window styles, size, position, menu
handle, instance handle, and creation data. A main window belongs to an application-defined window class, so you must
register the window class and provide a window procedure for the class before creating the main window.
Most applications typically use the WS_OVERLAPPEDWINDOW style to create the main window. This style gives the
window a title bar, a window menu, a sizing border, and minimize and maximize buttons. The CreateWindowEx()
function returns a handle that uniquely identifies the window.
The following example creates a main window belonging to an application-defined window class. The window name, will
appear in the window's title bar. By combining the WS_VSCROLL and WS_HSCROLL styles with the
WS_OVERLAPPEDWINDOW style, the application creates a main window with horizontal and vertical scroll bars in
addition to the components provided by the WS_OVERLAPPEDWINDOW style. The four occurences of the
CW_USEDEFAULT constant set the initial size and position of the window to the system-defined default values. By
specifying NULL instead of a menu handle, the window will have the menu defined for the window class.
HWND hWndMain = CreateWindowEx(
0, // no extended styles
L"MyWindowClass", // class name
L"Window Title", // window name
WS_OVERLAPPEDWINDOW | // overlapped window
WS_HSCROLL | // horizontal scroll bar
WS_VSCROLL, // vertical scroll bar
CW_USEDEFAULT, // default horizontal position
CW_USEDEFAULT, // default vertical position
CW_USEDEFAULT, // default width
CW_USEDEFAULT, // default height
(HWND) NULL, // no parent or owner window
(HMENU) NULL, // class menu used
hInstance, // instance handle
NULL); // no window creation data
if (!hWndMain) {
return FALSE;
}
It should be noted that the preceding example calls the ShowWindow() function after creating the main window. This
is done because the system does not automatically display the main window after creating it. By passing the
SW_SHOWDEFAULT flag to ShowWindow(), the application allows the program that started the application to set the
initial show state of the main window. The UpdateWindow() function sends the window its first WM_PAINT
message.
Creating, Enumerating, and Sizing Child Windows
You can divide a window's client area into different functional areas by using child windows. Creating a child window is
like creating a main window - you use the CreateWindowEx() function. To create a window of an application-
defined window class, you must register the window class and provide a window procedure before creating the child
window. You must give the child window the WS_CHILD style and specify a parent window for the child window when
you create it.
The following example divides the client area of an application's main window into three functional areas by creating
three child windows of equal size. Each child window is the same height as the main window's client area, but each is
one third it's width. The main window creates the child windows in response to the WM_CREATE message, which the
main window receives during its own window-creation process. Because each child window has the WS_BORDER style,
each has a thin line border. Also, because WS_VISIBLE style is not specified, each child window is initially hidden.
Notice also that each child window is assigned a child-window identifier.
The main window sizes and positions the child windows in response to the WM_SIZE message, which the main window
receives when its size changes. In response to WM_SIZE, the main window retrieves the dimensions of its client area by
using the GetClientRect() function and then passes the handle to each child window, in turn, to the application-
defined EnumChildProc() callback function. The size and position are based on the dimensions of the main
window's client area and the identifier of the child window. Afterward, EnumChildProc() calls the ShowWindow()
function to make the window visible.
#define ID_FIRSTCHILD 100
#define ID_SECONDCHILD 101
#define ID_THIRDCHILD 102
switch (uMsg) {
case WM_CREATE:
for (int i = 0; i < 3; i++) {
CreateWindow(0,
"ChildWClass",
(LPCTSTR) NULL,
WS_CHILD | WS_BORDER,
0, 0, 0, 0,
hWnd,
(HMENU) (int) (ID_FIRSTCHILD + i),
hInstance,
NULL);
}
return 0;
case WM_SIZE: // main window changed size
// Get the dimensions of the main window's client
// area, and enumerate the child windows. Pass the
// dimensions to the child windows during enumeration.
GetClientRect(hWnd, &rcClient);
EnumChildWindows(hWnd, EnumChildProc, (LPARAM) &rcClient);
return 0;
// Process other messages.
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
if (idChild == ID_FIRSTCHILD) {
i = 0;
} else if (idChild == ID_SECONDCHILD) {
i = 1;
} else {
i = 2;
}
ShowWindow(hWndChild, SW_SHOW);
return TRUE;
}
Destroying a Window
You can use the DestroyWindow() function to destroy a window. Typically, an application sends the WM_CLOSE
message before destroying a window, giving the window the opportunity to prompt the user for confirmation before the
window is destroyed. A window that includes a window menu automatically receives the WM_CLOSE message when the
user clicks Close from the window menu. If the user confirms that the window should be destroyed, the application calls
DestroyWindow(). The system sends the WM_DESTROY message to the window after removing it from the screen.
In response to WM_DESTROY, the window saves its data and frees any resources it allocated. A main window concludes
its processing of WM_DESTROY by calling the PostQuitMessage() function to quit the application.
The following example shows how to prompt for user confirmation before destroying a window. In response to
WM_CLOSE, the example displays a dialog box that contains Yes, No, and Cancel buttons. If the user clicks Yes,
DestroyWindow() is called. Otherwise, the window is not destroyed. Because the window being destroyed is a main
window, the example calls PostQuitMessage() in response to WM_DESTROY.
case WM_CLOSE:
// Create the message box. If the user clicks
// the Yes button, destroy the main window.
PostQuitMessage(0);
return 0;
Note that the third parameter of SetLayeredWindowAttributes() is a value that ranges from 0 to 255, with 0
making the window completely transparent and 255 making it completely opaque. This parameter mimics the more
versatile BLENDFUNCTION of the AlphaBlend() function.
To make this window completely opaque again, remove the WS_EX_LAYERED bit by calling SetWindowLong() and
then ask the window to repaint. Removing the bit is desired to let the system know that it can free up some memory
associated with layering and redirection. The code might look like this:
// Remove WS_EX_LAYERED from the window styles.
SetWindowLong(hWnd,
GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
In order to use layered child windows, the application has to declare itself Windows 8-aware in the manifest.
Window Classes
A window class is a set of attributes that the system uses as a template to create a window. Each window is a member of
a window class. All window classes are process specific.
The window procedure processes messages for all windows of that class and therefore controls their behavior and
appearance.
A process must register a window class before it can create a window of that class. Registering a window class associates
a window procedure, class styles, and other class attributes with a class name. When a process specifies a class name in
the CreateWindow() or CreateWindowEx() function, the system creates a window with the window procedure,
styles, and other attributes associated with that class name.
System classes
Application global classes
Application local classes
These types differ in scope and in when and how they are registered and destroyed.
The system registers the system classes for a process the first time one of its threads calls a User or Windows Graphics
Device Interface (GDI) function.
Each application receives its own copy of the system classes. All 16-bit Windows-based applications in the same VDM
share system classes, just as they do on 16-bit Windows.
The following table describes the system classes that are available for use by all processes:
Class Description
Button The class for a button.
ComboBox The class for a combo box.
Edit The class for an edit control.
ListBox The class for a list box.
MDIClient The class for an MDI client window.
ScrollBar The class for a scroll bar.
Static The class for a static control.
The following table describes the system classes that are available only for use by the system:
Class Description
ComboLBox The class for the list box contained in a combo box.
DDEMLEvent The class for Dynamic Data Exchange Management Library (DDEML) events.
Message The class for a message-only window.
#32768 The class for a menu.
#32769 The class for the desktop window.
#32770 The class for a dialog box.
#32771 The class for the task switch window.
#32772 The class for icon titles.
To create a class that can be used in every process, create the window class in a .dll and load the .dll in every process. To
load the .dll in every process, add its name to AppInit_DLLs value in the following registry key:
HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\Windows
Whenever a process starts, the system loads the specified .dll in the context of the newly started process before calling
its entry-point function. The .dll must register the class during its initialization procedure and must specify the
CS_GLOBALCLASS style.
To remove an application global class and free the storage associated with it, use the UnregisterClass() function.
1.3 Application Local Classes
An application local class is any window class that an executable or .dll registers for its exclusive use. Although you can
register any number of local classes, it is typical to register only one. This window class supports the window procedure
of the application's main window.
The system destroys a local class when the module that registered it closes. An application can also use the
UnregisterClass() function to remove a local class and free the storage associated with it.
1. Search the list of application local classes for a class with the specified name whose instance handle matches the
module's instance handle. (Several modules can use the same name to register local classes in the same
process.)
2. If the name is not in the application local class list, search the list of application global classes.
3. If the name is not in the application global class list, search the list of system classes.
All windows created by the application use this procedure, including windows created by the system on the application's
behalf, such as dialog boxes. It is possible to override system classes without affecting other applications. That is, an
application can register an application local class having the same name as a system class. This replaces the system class
in the context of the application but does not prevent other applications from using the system class.
style
icon
cursor
menu
window procedure
The first step in registering a window class is to fill in a WNDCLASSEX structure with the window class information. The
next step is to pass the structure to the RegisterClassEx() function.
To register an application global class, specify the CS_GLOBALCLASS style in the style member of the
WNDCLASSEX structure. When registering an application local class, do not specify the CS_GLOBALCLASS style.
If you register the window class using the ANSI version of RegisterClassEx() (RegisterClassExA()), the
application requests that the system pass text parameters of messages to the windows of the created class using the
ANSI character set. If you register the class using the Unicode version of RegisterClassEx()
(RegisterClassExW()), the application requests that the system pass text parameters of messages to the windows
of the created class using the Unicode character set. The IsWindowUnicode() function enables applications to
query the nature of each window.
The executable or DLL that registered the class is the owner of the class. The system determines class ownership from
the hInstance member of the WNDCLASSEX structure passed to the RegisterClassEx() function when the
class is registered. For DLLs, the hInstance member must be the handle to the .dll instance.
The class is not destroyed when the .dll that owns it is unloaded. Therefore, if the system calls the window procedure for
a window of that class, it will cause an access violation, because the .dll containing the window procedure is no longer in
memory. The process must destroy all windows using the class before the .dll is unloaded and call the
UnregisterClass() function.
Although a complete window class consists of many elements, the system requires only that an application supply the:
class name
window procedure address
instance handle
Use the other elements to define default attributes for windows of the class, such as the shape of the cursor and the
content of the menu for the window. You must initialize any unused members of the WNDCLASSEX structure to zero or
NULL. The window class elements are as shown in the following table:
Element Purpose
Class Name Distinguishes the class from other registered classes.
Pointer to the function that processes all messages sent to windows in the class and
Window Procedure Address
defines the behavior of the window.
Instance Handle Identifies the application or .dll that registered the class.
Class Cursor Defines the mouse cursor that the system displays for a window of the class.
Class Icons Defines the large icon and the small icon.
Defines the color and pattern that fill the client area when the window is opened or
Class Background Brush
painted.
Class Menu Specifies the default menu for windows that do not explicitly define a menu.
Defines how to update the window after moving or resizing it, how to process double-
Class Styles clicks of the mouse, how to allocate space for the device context, and other aspects of
the window.
Specifies the amount of extra memory, in bytes, that the system should reserve for the
Extra Class Memory class. All windows in the class share the extra memory and can use it for any
application-defined purpose. The system initializes this memory to zero.
Specifies the amount of extra memory, in bytes, that the system should reserve for
Extra Window Memory each window belonging to the class. The extra memory can be used for any
application-defined purpose. The system initializes this memory to zero.
The GetClassName() function retrieves the name of the class to which a given window belongs.
The system passes an instance handle to the entry-point function of each executable (such as WinMain()) and .dll
(DllMain()). The executable or .dll assigns this instance handle to the class by copying it to the hInstance member of
the WNDCLASSEX structure.
The system does not require a class cursor. If an application sets the hCursor member of the WNDCLASSEX structure
to NULL, no class cursor is defined. The system assumes the window sets the cursor shape each time the cursor moves
into the window. A window can set the cursor shape by calling the SetCursor() function whenever the window
receives the WM_MOUSEMOVE message.
The system displays a window's large class icon in the task-switch window that appears when the user presses
ALT + TAB, and in the large icon views of the task bar and explorer.
The small class icon appears in a window's title bar and in the small icon views of the task bar and explorer.
To assign a large and small icon to a window class, specify the handles of the icons in the hIcon and hIconSm
members of the WNDCLASSEX structure. The icon dimensions must conform to required dimensions for large and small
class icons:
For a large class icon, you can determine the required dimensions by specifying the SM_CXICON and
SM_CYICON values in a call to the GetSystemMetrics() function.
For a small class icon, specify the SM_CXSMICON and SM_CYSMICON values in a call to the
GetSystemMetrics() function.
If an application sets the hIcon and hIconSm members of the WNDCLASSEX structure to NULL, the system uses the
default application icon as the large and small class icons for the window class. If you specify a large class icon but not a
small one, the system creates a small class icon based on the large one. However, if you specify a small class icon but not
a large one, the system uses the default application icon as the large class icon and the specified icon as the small class
icon.
You can override the large or small class icon for a particular window by using the WM_SETICON message. You can
retrieve the current large or small class icon by using the WM_GETICON message.
4.6 The Class Background Brush
A class background brush prepares the client area of a window for subsequent drawing by the application. The system
uses the brush to fill the client area with a solid color or pattern, thereby removing all previous images from that
location whether they belong to the window or not. The system notifies a window that its background should be painted
by sending the WM_ERASEBKGND message to the window.
To assign a background brush to a class, create a brush by using the appropriate GDI functions and assign the returned
brush handle to the hbrBackground member of the WNDCLASSEX structure.
Instead of creating a brush, an application can set the hbrBackground member to one of the standard system color
values.
To use a standard system color, the application must increase the background-color value by one. For example,
COLOR_BACKGROUND + 1 is the system background color. Alternatively, you can use the GetSysColorBrush()
function to retrieve a handle to a brush that corresponds to a standard system color, and then specify the handle in the
hbrBackground member of the WNDCLASSEX structure.
The system does not require that a window class have a class background brush. If this parameter is set to NULL, the
window must paint its own background whenever it receives the WM_ERASEBKGND message.
You can assign a menu to a class by setting the lpszMenuName member of the WNDCLASSEX structure to the address
of a null-terminated string that specifies the resource name of the menu. The menu is assumed to be a resource in the
given application. The system automatically loads the menu when it is needed. If the menu resource is identified by an
integer and not by a name, the application can set the lpszMenuName member to that integer by applying the
MAKEINTRESOURCE macro before assigning the value.
The system does not require a class menu. If an application sets the lpszMenuName member of the WNDCLASSEX
structure to NULL, windows in the class have no menu bars. Even if no class menu is given, an application can still define
a menu bar for a window when it creates the window.
If a menu is given for a class and a child window of that class is created, the menu is ignored.
If no device-context style is explicitly given, the system assumes each window uses a device context retrieved from a
pool of contexts maintained by the system. In such cases, each window must retrieve and initialize the device context
before painting and free it after painting.
To avoid retrieving a device context each time it needs to paint inside a window, an application can specify the
CS_OWNDC style for the window class. This class style directs the system to create a private device context - that is, to
allocate a unique device context for each window in the class. The application need only retrieve the context once and
then use it for all subsequent painting.
Because extra memory is allocated from the system's local heap, an application should use extra class memory sparingly.
The RegisterClassEx() function fails if the amount of extra class memory requested is greater than 40 bytes. If an
application requires more tha 40 bytes, it should allocate its own memory and store a pointer to the memory in the
extra class memory.
The SetClassWord() and SetClassLong() functions copy a value to the extra class memory. To retrieve a value
from the extra class memory, use the GetClassWord() and GetClassLong() functions. The cbClsExtra
member of the WNDCLASSEX structure specifies the amount of extra class memory to allocate. An application that does
not use extra class memory must initialize the cbClsExtra member to zero.
Because extra memory is allocated from the system's local heap, an application should use extra window memory
sparingly. The RegisterClassEx() function fails if the amount of extra memory requeste is greater than 40 bytes. If
an application requires more than 40 bytes, it should allocate its own memory and store a pointer to the memory in the
extra window memory.
The SetWindowLong() function copies a value to the extra memory. The GetWindowLong() function retrieves a
value from the extra memory. The cbWndExtra member of the WNDCLASSEX structure specifies the amount of extra
window memory to allocate. An application that does not use the memory must initialize cbWndExtra to zero.
Each process must register its own window classes. To register an application local class, use the
RegisterClassEx() function. You must define the window procedure, fill the members of the WNDCLASSEX
structure, and then pass a pointer to the structure to the RegisterClassEx() function.
The following example shows how to register a local window calss and use it to create a main window:
#include <Windows.h>
// Global variable
HINSTANCE hInst;
// Function prototypes
INT WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
BOOL InitApplication(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
if (!InitInstance(hInstance, nCmdShow))
return FALSE;
if (!hWnd)
return FALSE;
Registering an application global class is similar to registering an application local class, except that the style member
of the WNDCLASSEX structure must specify the CS_GLOBALCLASS style.
Window Procedures
Every window has an associated window procedure - a function that processes all messages sent or posted to all
windows of the class. All aspects of a window's appearance and behavior depend on the window procedure's response
to these messages.
Window procedures for dialog boxes (called dialog box procedures) have a similar structure and function as regular
window procedures. All points referring to window procedures apply to dialog box procedures as well.
Message parameters often contain information in both their low-order and high-order words. There are several macros
an application can use to extract information from the message parameters. The LOWORD macro, for example, extracts
the low-order word (bits 0 through 15) from a message parameter. Other macros include HIWORD, LOBYTE, HIBYTE.
The interpretation of the return value depends on the particular message. Consult the description of each message to
determine the appropriate return value.
Because it is possible to call a window procedure recursively, it is important to minimize the number of local variables
that it uses. When processing individual messages, an application should call functions outside the window procedure to
avoid excessive use of local variables, possibly causing the stack to overflow during deep recursion.
Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular
window before the window has a chance to process them. By subclassing a window, an application can subclass a
window belonging to a system global class, such as an edit control or a list box. For example, an application could
subclass an edit control to prevent the control from accepting certain characters. However, you cannot subclass a
window or class that belongs to another application. All subclassing must be performed within the same process.
An application subclasses a window by replacing the address of the window's original window procedure with the
address of a new window procedure, called the subclass procedure. Thereafter, the subclass procedure receives any
message sent or posted to the window.
The subclass procedure can take three actions upon receiving a message:
If the subclass procedure processes a message, it can do so before, after, or both before and after it passes the message
to the original window procedure.
Instance subclassing - In instance subclassing, an application replaces the window procedure address of a single
instance of a window. An application must use instance subclassing to subclass an existing window.
Global subclassing - In global subclassing, an application replaces the address of the window procedure in the
WNDCLASS (or WNDCLASSEX) structure of a window class. All subsequent windows created with the class have
the address of the subclass procedure, but existing windows of the class are not affected.
In this case, SetWindowLong() returns the address of the window's original window procedure. The application must
save the address, using it in subsequent calls to the CallWindowProc() function, to pass intercepted messages to
the window procedure. The application must also have the original window procedure address to remove the subclass
from the window. To remove the subclass, the application calls SetWindowLong() again, passing the address of the
original window procedure with the GWL_WNDPROC flag and the handle to the window.
The system owns the system global classes, and aspects of the controls might change from one version of the system to
the next. If the application must subclass a window that belongs to a system global class, the developer may need to
update the application when a new version of the system is released.
Because instance subclassing occurs after a window is created, you cannot add any extra bytes to the window.
Applications that subclass a window should use the window's property list to store any data needed for an instance of
the subclasses window.
When an application subclasses a subclassed window, it must remove the subclasses in the reverse order they were
performed. If the removal order is not reversed, an unrecoverable system error may occur.
The original window procedure address is used in global subclassing in the same way it is used in instance subclassing.
The subclass procedure passes messages to the original window procedure by calling CallWindowProc(). The
application removes the subclass from the window class by calling SetClassLong() again, specifying the address of
the original window procedure, the GCL_WNDPROC flag, and the handle to a window of the class being subclassed. An
application that globally subclasses a control class must remove the subclasses when the application terminates,
otherwise an unrecoverable system error may occur.
Global subclassing has the same limitations as instance subclassing, plus some additional restrictions. An application
should not use the extra bytes for either the class or the window instance without knowing exactly how the original
window procedure uses them. If the application must associate data with a window, it should use window properties.
4. Window Procedure Superclassing
Superclassing is a technique that allows an application to create a new window class with the basic functionality of the
existing class, plus enhancements provided by the application. A superclass is based on an existing window class called
the base class. Frequently, the base class is a system global window class such as an edit control, but it can be any
window class.
A superclass has its own window procedure, called the superclass procedure. The superclass procedure can take three
actions upon receiving a message:
If the superclass procedure processes a message, it can do so before, after, or both before and after it passes the
message to the original window procedure.
Unlike a subclass procedure, a superclass procedure can process window creation messages ( WM_NCCREATE,
WM_CREATE, and so on), but it must also pass them to the original base-class window procedure so that the base-class
window procedure can perform its initialization procedure.
To superclass a window class, an application first calls the GetClassInfo() function to retrieve information about
the base class. GetClassInfo() fills a WNDCLASS structure with the values from the WNDCLASS structure of the
base class. Next, the application copies its own instance handle into the hInstance member of the WNDCLASS structure
and copies the name of the superclass into the lpszClassName member. If the base class has a menu, the application
must provide a new menu with the same menu identifiers and copy the menu name into the lpszMenuName member.
If the superclass procedure processes the WM_COMMAND message and does not pass it to the window procedure of the
base class, the menu need not have corresponding identifiers. GetClassInfo() does not return the
lpszMenuName, lpszClassName, or hInstance member of the WNDCLASS structure.
An application must also set the lpfnWndProc member of the WNDCLASS structure. The GetClassInfo()
function fills this member with the address of the original window procedure for the class. The application must save this
address, to pass messages to the original window procedure, and then copy the address of the superclass procedure
into the lpfnWndProc member. The application can, if necessary, modify any other members of the WNDCLASS
structure. After it fills the WNDCLASS structure, the application registers the superclass by passing the address of the
structure to the RegisterClass() function. The superclass can then be used to create windows.
Because superclassing registers a new window class, an application can add to both the extra class bytes and the extra
window bytes. The superclass must not use the original extra bytes for the base class or the window for the same
reasons that an instance subclass or a global subclass should not use them. Also, if the application adds extra bytes for
its use to either the class or the window instance, it must reference the extra bytes relative to the number of extra bytes
used by the original base class. Because the number of bytes used by the base class may vary from one version of the
base class to the next, the starting offset for the superclass's own extra bytes may also vary from one version of the base
class to the next.
The WM_NCCREATE message is sent just after your window is created, but if an application responds to this message by
returning FALSE, the CreateWindowEx() function fails. The WM_CREATE message is sent after your window is
already created.
The WM_DESTROY message is sent when your window is about to be destroyed. The DestroyWindow() function
takes care of destroying any child windows of the window being destroyed. The WM_NCDESTROY message is sent just
before a window is destroyed.
At the very least, a window procedure should process the WM_PAINT message to draw itself. Typically, it should handle
mouse and keyboard messages as well.
Your application can call the DefWindowProc() function as part of the processing of a message. In such a case, the
application can modify the message parameters before passing the message to DefWindowProc(), or it can continue
with the default processing after performing its own operations.
The following example shows how to associate the window procedure in the previous example with a window class:
INT APIENTRY WinMain(
HINSTANCE hInstance, // handle to the current instance
HINSTANCE hPrevInstance, // handle to the previous instance
LPSTR lpCmdLine, // address of the command-line string
int nCmdShow) { // show-window type
WNDCLASS wc = {};
if (!RegisterClass(&wc))
return FALSE;
// Process messages.
}
3. Subclassing a Window
To subclass an instance of a window, call the SetWindowLong() function and specify the handle to the window to
subclass, the GWL_WNDPROC flag, and a pointer to the subclass procedure. SetWindowLong() returns a pointer to
the original window procedure. Use this pointer to pass messages to the original procedure. The subclass window
procedure must use the CallWindowProc() function to call the original window procedure.
It should be noted that to write code that is compatible with both 32-bit and 64-bit versions of Windows, the
SetWindowLongPtr() function should be used instead.
The following example shows how to subclass an instance of an edit control in a dialog box. The subclass window
procedure enables the edit control to receive all keyboard input, including the ENTER and TAB keys, whenever the
control has the input focus.
WNDPROC wpOrigEditProc;
LRESULT APIENTRY EditBoxProc(
HWND hWndDlg,
UINT uMsg,
WPARAM wParam,
LPARAM lParam) {
HWND hWndEdit;
switch (uMsg) {
case WM_INITDIALOG:
// Retrieve the handle to the edit control.
hWndEdit = GetDlgItem(hWndDlg, ID_EDIT);
// Subclass procedure
LRESULT APIENTRY EditSubclassProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam) {
if (uMsg == WM_GETDLGCODE)
return DLGC_WANTALLKEYS;
The system passes all input for an application to the various windows in the application. Each window has a function,
called a window procedure, that the system calls whenever it has input for the window. The window procedure
processes the input and returns control to the system.
If a top-level window stops responding to messages for more than several seconds, the system considers the window to
be not responding. In this case, the system hides the window and replaces it with a ghost window that has the same z-
order, location, size, and visual attributes. This allows the user to move it, resize it, or even close the application.
However, these are the only actions available because the application is actually not responding. When in the debugger
mode, the system does not generate a ghost window.
1. Windows Messages
The system passes input to a window in the form of a message. Messages are generated by both the system and
applications:
The system generates a message at each input event - for example, when the user types, moves the mouse, or
clicks a control such as a scroll bar. The system also generates message in response to changes in the system
brought about by an application, such as when an application changes the pool of system font resources or
resizes one of its windows.
An application can generate messages to direct its own windows to perform tasks or to communicate with
windows in other applications..
The system sends a message to a window procedure with a set of four parameters:
The window handle identifies the window for which the message is intended. The system uses it to determine which
window procedure should receive the message.
A message identifier is a named constant that identifies the purpose of a message. When a window procedure receives a
message, it uses a message identifier to determine how to process the message. For example, the message identifier
WM_PAINT tells the window procedure that the window's client area has changed and must be repainted.
Message parameters specify data or the location of data used by a window procedure when processing a message. The
meaning and value of the message parameters depend on the message. A message parameter can contain an integer,
packed bit flags, a pointer to a structure containing additional data, and so on. When a message does not use message
parameters, they are typically set to NULL. A window procedure must check the message identifier to determine how to
interpret the message parameters.
2. Message Types
There are two types of windows messages:
System-defined messages
Application-defined messages
Each system-defined message has a unique message identifier and a corresponding symbolic constant (defined in the
software development kit (SDK) header files) that states the purpose of the message. For example, the WM_PAINT
constant requests that a window paint its contents.
Symbolic constants specify the category to which system-defined messages belong. The prefix of the constant identifies
the type of window that can interpret and process the message. Following are the prefixes and their related message
categories:
General windows messages cover a wide range of information and requests, including messages for mouse keyboard
input, menu and dialog box input, window creation and management, and Dynamic Data Exchange (DDE).
The system reserves message-identifier values in the range 0x0000 through 0x03FF (the value of WM_USER - 1)
for system-defined messages. Applications cannot use these values for private messages.
Values in the range 0x0400 (the value of WM_USER) through 0x7FFF are available for message identifiers for
private window classes.
If your application is marked version 4.0, you can use message-identifier values in the range 0x8000 ( WM_APP)
through 0xBFFF for private messages.
The system returns a message identifier in the range 0xC000 through 0xFFFF when an application calls the
RegisterWindowMessage() function to register a message. The message identifier returned by this
function is guaranteed to be unique throughout the system. Use of this function prevents conflicts that can arise
if other applications use the same message identifier for different purposes.
3. Message Routing
The system uses two methods to route messages to a window procedure:
Posting messages to a first-in, first-out (FIFO) queue called a message queue (a system-defined memory object
that temporarily stores messages).
Sending messages directly to a window procedure.
A message that is posted to a message queue is called a queued message. These are primarily the result of user input
entered through the mouse or keyboard, such as WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, and WM_CHAR
messages. Other queued messages include the timer, paint, and quit messages: WM_TIMER, WM_PAINT and WM_QUIT.
Most other messages, which are sent directly to a window procedure, are called nonqueued messages.
The system maintains a single system message queue and one thread-specific message queue for each GUI thread. To
avoid the overhead of creating a message queue for non-GUI threads, all threads are created initially without a message
queue. The system creates a thread-specific message queue only when the thread makes its first call to one of the
specific user functions. No GUI function calls result in the creation of a message queue.
Whenever the user moves the mouse, clicks the mouse buttons, or types on the keyboard, the device driver for the
mouse or keyboard converts the input into messages and places them in the system message queue. The system
removes the messages, one at a time, from the system message queue, examines them to determine the destination
window, and then posts them to the message queue of the thread that created the destination window. A thread's
message queu receives all mouse and keyboard messages for the window created by the thread. The thread removes
messages from its queue and directs the system to send them to the appropriate window procedure for processing.
With the exception of the WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, the system
always posts messages at the end of a message queue. This ensures that a window receives its input messages in the
proper first-in, first-out (FIFO) sequence. The WM_PAINT message, the WM_TIMER message, and the WM_QUIT
message, however, are kept in the queue and are forwarded to the window procedure only when the queue contains no
other messages. In addition, multiple WM_PAINT messages for the same window are combined into a single WM_PAINT
message, consolidating all invalid parts of the client area into a single area. Combining WM_PAINT messages reduces
the number of times a window must redraw the contents of its client area.
The system posts a message to a thread's message queue by filling an MSG structure and then copying it to the message
queue. Information in MSG includes:
A thread can post a message to its own message queue or to the queue of another thread by using the
PostMessage() or PostThreadMessage() function.
An application can remove a message from its queue by using the GetMessage() function. To examine a message
without removing it from its queue, an application can use the PeekMessage() function. This function fills an MSG
object with information about the message.
After removing a message from its queue, an application can use the DispatchMessage() function to direct the
system to send the message to a window procedure for processing. DispatchMessage() takes a pointer to MSG
that was filled by a previous call to the GetMessage() or PeekMessage() function. DispatchMessage()
passes the window handle, the message identifier, and the two message parameters to the window procedure, but it
does not pass the time the message was posted or mouse cursor position. An application can retrieve this information
by calling the GetMessageTime() and GetMessagePos() functions while processing a message.
A thread can use the WaitMessage() function to yield control to other threads when it has no messages in its queue.
The function suspends the thread and does not return until a new message is placed in the thread's message queue.
You can call the SetMessageExtraInfo() function to associate a value with the current thread's message queue.
Then call the GetMessageExtraInfo() function to get the value associated with the last message retrieved by the
GetMessage() or PeekMessage() function.
BroadcastSystemMessage()
BroadcastSystemMessageEx()
SendMessage()
SendMessageTimeout()
SendNotifyMessage()
4. Message Handling
An application must remove and process messages posted to the message queue of its threads. A single-threaded
application usually uses a message loop in its WinMain() function to remove and send messages to the appropriate
window procedures for processing. Applications with multiple threads can include a message loop in each thread that
creates a window.
GetMessage()
TranslateMessage()
DispatchMessage()
Note that if there is an error, GetMessage() returns -1, thus the need for the special testing:
MSG msg = {};
BOOL bRet;
The GetMessage() function retrieves a message from the queue and copies it to a structure of type MSG. It returns a
nonzero value, unless it encounters the WM_QUIT message, in which case it returns FALSE and ends the loop. In a
single-threaded application, ending the message loop is often the first step in closing the application. An application can
end its own loop by using the PostQuitMessage() function, typically in response to the WM_DESTROY message in
the window procedure of the application's main window.
If you specify a window handle as the second parameter of GetMessage(), only messages for the specified window
are retrieved from the queue. GetMessage() can also filter messages in the queue, retrievign only those messages
that fall within a specified range.
A thread's message loop must include TranslateMessage() if the thread is to receive character input from the
keyboard. The system generates virtual-key messages (WM_KEYDOWN and WM_KEYUP) each time the user presses a
key. A virtual-key message contains a virtual-key code that identifies which key was pressed, but not its character value.
To retrieve this value, the message loop must contain TranslateMessage(), which translates the virtual-key
message into a character message (WM_CHAR) and places it back into the application message queue. The character
message can then be removed upon a subsequent iteration of the message loop and dispatched to a window procedure.
The DispatchMessage() function sends a message to the window procedure associated with the window handle
specified in the MSG structure. If the window handle is HWND_TOPMOST, DispatchMessage() sends the message
to the window procedures of all top-level windows in the system. If the window handle is NULL,
DispatchMessage() does nothing with the message.
An application's main thread starts its message loop after initializing the application and creating at least one window.
After it is started, the message loop continues to retrieve messages from the thread's message queue and to dispatch
them to the appropriate windows. The message loop ends when the GetMessage() function removes the WM_QUIT
message from the queue.
Only one message loop is needed for a message queue, even if an application contains many windows.
DispatchMessage() always dispatches the message to the proper window. This is because each message in the
queue is an MSG structure that contains the handle of the window to which the message belongs.
You can modify a message loop in a variety of ways. For example, you can retrieve messages from the queue without
dispatching them to a window. This is useful for applications that post messages not specifying a window. You can also
direct GetMessage() to search for specific messages, leaving other messages in the queue. This is useful if you must
temporarily bypass the usual FIFO order of the message queue.
An application that uses accelerator keys must be able to translate keyboard messages into command messages. To do
this, the application's message loop must include a call to the TranslateAccelerator() function.
If a thread uses a modeless dialog box, the message loop must include the IsDialogMessage() function so that the
dialog box can receive keyboard input.
4.2 The Window Procedure
A window procedure is a function that receives and processes all messages sent to the window. Every window class has
a window procedure, and every window created with that class uses the same window procedure to respond to
messages.
The system sends a message to a window procedure by passing the message data as arguments to the procedure. The
window procedure then performs an appropriate action for the message. It checks the message identifier and, while
processing the message, uses the information specified by the message parameters.
A window procedure does not usually ignore a message. If it does not process a message, it must send the message back
to the system for default processing. The window procedure does this by calling the DefWindowProc() function,
which performs a default action and returns a message result. The window procedure must then return this value as its
own message result. Most window procedures process just a few messages and pass the others on the system by calling
DefWindowProc().
Because a window procedure is shared by all windows belonging to the same class, it can process a message for several
different windows. To identify the specific window affected by the message, a window procedure can examine the
window handle passed with a message.
5. Message Filtering
An application can choose specific messages to retrieve from the message queue (while ignoring other messages) by
using the GetMessage() or PeekMessage() function to specify a message filter. The filter is a range of message
identifiers (specified by a first and last identifier), a window handle, or both. GetMessage() and PeekMessage()
use a message filter to select which messages to retrieve from the queue. Message filtering is useful if an application
must search the message queue for messages that have arrived later in the queue. It is also useful if an application must
process input (hardware) messages before processing posted messages.
The WM_KEYFIRST and WM_KEYLAST constants can be used as filter values to retrieve all keyboard messages. The
WM_MOUSEFIRST and WM_MOUSELAST constants can be used to retrieve all mouse messages.
Any application that filters messages must ensure that a message satisfying the message filter can be posted. For
example, if an application filters for a WM_CHAR message in a window that does not receive keyboard input, the
GetMessage() function does not return. This effectively "hangs" the application.
An application can post a message without specifying a window. If the application supplies a NULL window handle when
calling PostMessage(), the message is posted to the queue associated with the current thread. Because no window
handle is specified, the application must process the message in the message loop. This is one way to create a message
that applies to the entire application, instead of to a specific window.
Occasionally, you may want to post a message to all top-level windows in the system. An application can post a message
to all top-level windows by caling PostMessage() and specifying HWND_TOPMOST in the hWnd parameter.
A common programming error is to assume that the PostMessage() function always posts a message. This is not
true when the message queue is full. An application should check the return value of the PostMessage() function to
determine whether the message has been posted and, if it has not been, repost it.
The SendMessageCallback() function also sends a message to the window procedure corresponding to the given
window. However, this function returns immediately. After the window procedure processes the message, the system
calls the specified callback function. For more information about the callback function, see the SendAsyncProc()
function.
Occasionally, you may want to send a message to all top-level windows in the system. For example, if the application
changes the system time, it must notify all top-level windows about the change by sending a WM_TIMECHANGE
message. An application can send a message to all top-level windows by calling SendMessage() and specifying
HWND_TOPMOST in the hWnd parameter. You can also broadcast a message to all applications by calling the
BroadcastSystemMessage() function and specifying BSM_APPLICATIONS in the lpdwRecipients
parameter.
By using the InSendMessage() or InSendMessageEx() function, a window procedure can determine whether it
is processing a message sent by another thread. This capability is useful when message processing depends on the origin
of the message.
7. Message Deadlocks
A thread that calls the SendMessage() function to send a message to another thread cannot continue executing until
the window procedure that receives the message returns. If the receiving thread yields control while processing the
message, the sending thread cannot continue executing, because it is waiting for SendMessage() to return. If the
receiving thread is attached to the same queue as the sender, it can cause an application deadlock to occur. (Note that
journal hooks attach threads to the same queue.)
Note that the receiving thread need not yield control explicitly. Calling any of the following functions can cause a thread
to yield control implicitly:
DialogBox()
DialogBoxIndirect()
DialogBoxIndirectParam()
DialogBoxParam()
GetMessage()
MessageBox()
PeekMessage()
SendMessage()
To avoid potential deadlocks in your application, consider using the SendNotifyMessage() or
SendMessageTimeout() functions. Otherwise, a window procedure can determine whether a message it has
received was sent by another thread by calling the InSendMessage() or InSendMessageEx() function. Before
calling any of the functions in the preceding list while processing a message, the window procedure should first call
InSendMessage() or InSendMessageEx(). If this function returns TRUE, the window procedure must call the
ReplyMessage() function before any function that causes the thread to yield control.
8. Broadcasting Messages
Each message consists of a message identifier and two parameters, wParam and lParam. The message identifier is a
unique value that specifies the message purpose. The parameters provide additional information that is message-
specific, but the wParam parameter is generally a type value that provides more information about the message.
A message broadcast is simply the sending of a message to multiple recipients in the system. To broadcast a message
from an application, use the BroadcastSystemMessage() function, specifying the recipients of the message.
Rather than specifying individual recipients, you must specify one or more types of recipients. These types are
applications, installable drivers, network drivers, and system-level drivers. The system sends broadcast messages to all
members of each specified type.
The system typically broadcasts messages in response to changes that take place within system-level device drivers or
related components. The driver or related component broadcasts the message to applications and other components to
notify them of the change. For example, the component responsible for disk drives broadcasts a message whenever the
device driver for the floppy disk drive detects a change of media such as when the user inserts a disk in the drive.
This means that system-level device drivers, if chosen as recipients, always get the first opportunity to respond to a
message. Within a given recipient type, no driver is guaranteed to receive a given message before any other driver. This
means that a message intended for a specific driver must have a globally-unique message identifier so that no other
driver unintentionally processes it.
You can also broadcast messages to all top-level windows by specifying HWND_BROADCAST in the SendMessage(),
SendMessageCallback(), SendMessageTimeout() or SendNotifyMessage() function.
Applications receive messages through the window procedure of their top-level windows. Messages are not sent to child
windows. Services can receive messages through a window procedure or their service control handlers.
It should be noted that system-level device drivers use a relayed, system-level function to broadcast system messages.
9. Query Messages
You can create your own custom messages and use them to coordinate activities between your applications and other
components in the system. This is especially useful if you have created your own installable drivers or system-level
device drivers. Your custom messages can carry information to and from your driver and the applications that use the
driver.
To poll recipients for permission to carry out a given action, use a query message. You can generate your own query
messages by setting the BSF_QUERY value in the dwFlags parameter when calling
BroadcastSystemMessage(). Each recipient of the query message must return TRUE for the function to send the
message to the next recipient. If any recipient returns BROADCAST_QUERY_DENY, the broadcast ends immediately
and the function returns a zero.
Because the system directs messages to individual windows in the application, a thread must create at least one window
before starting its message loop. Most applications contain a single thread that creates windows. A typical application
registers the window class for its main window, creates and shows the main window, and then starts its message loop -
all in the WinMain() function.
You create a message loop by using the GetMessage() and DispatchMessage() functions. If your application
must obtain character input from the user, incldue the TranslateMessage() function in the loop.
TranslateMessage() translates virtual-key messages into character messages. The following example shows the
message loop in the WinMain() function of a simple Windows-based application:
HINSTANCE hInst;
HWND hWndMain;
if (!RegisterClass(&wc))
return FALSE;
}
The following example shows a message loop for a thread that uses accelerators and displays a modeless dialog box.
When TranslateAccelerator() or IsDialogMessage() returns TRUE (indicating that the message has been
processed), TranslateMessage() and DispatchMessage() are not called. The reason for this is that
TranslateAccelerator() and IsDialogMessage() performs all necessary translating and dispatching of
messages.
HWND hWndMain;
HWND hWndDlgModeless = NULL;
MSG msg;
BOOL bRet;
HACCEL haccel;
You can use the PeekMessage() function to examine a message queue during a lengthy operation.
PeekMessage() is similar to the GetMessage() function - both check a message queue for a message that
matches the filter criteria and then copy the message to an MSG structure. The main difference between the two
functions is that GetMessage() does not return until a message matching the filter criteria is placed in the queue,
whereas PeekMessage() returns immediately regardless of whether a message is in the queue.
The following example shows how to use PeekMessage() to examine a message queue for mouse clicks and
keyboard input during a lengthy operation:
HWND hWnd;
BOOL fDone;
MSG msg;
fDone = FALSE;
while (!fDone) {
fDone = DoLengthyOperation(); // application-defined function
Other functions, including GetQueueStatus() and GetInputState(), also allow you to examine the contents of
a thread's message queue:
GetQueueState() - Returns an array of flags that indicates the types of messages in the queue. Using it is
the fastest way to discover whether the queue contains any messages.
GetInputState() - Returns TRUE if the queue contains mouse or keyboard messages. Both of these
functions can be used to determine whether the queue contains messages that need to be processed.
3. Posting a Message
You can post a message to a message queue by using the PostMessage() function. PostMessage() places a
message at the end of a thread's message queue and returns immediately, without waiting for the thread to process the
message. The function's parameters include a window handle, a message identifier, and two message parameters. The
system copies these parameters to an MSG structure, fills the time and pt members of the structure, and places the
structure in the message queue.
The system uses the window handle passed with the PostMessage() function to determine which thread message
queue should receive the message. If the handle is HWND_TOPMOST, the system posts the message to the thread
message queue of all top-level windows.
You can use the PostThreadMessage() function to post a message to a specific thread message queue.
PostThreadMessage() is similar to PostMessage(), except the first parameter is a thread identifier rather than
a window handle. You can retrieve the thread identifier by calling the GetCurrentThreadId() function.
Use the PostQuitMessage() function to exit a message loop. PostQuitMessage() posts the WM_QUIT
message to the currently executing thread. The thread's message loop terminates and returns control to the system
when it encounters the WM_QUIT message. An application usually calls PostQuitMessage() in response to the
WM_DESTROY message, as shown in the following example:
case WM_DESTROY:
// Perform cleanup tasks.
PostQuitMessage(0);
break;
4. Sending a Message
The SendMessage() function is used to send a message directly to a window procedure. SendMessage() calls a
window procedure and waits for that procedure to process the message and return a result.
A message can be sent to any window in the system. All that is required is a window handle. The system uses the handle
to determine which window procedure should receive the message.
Before processing a message that may have been sent from another thread, a window procedure should first call the
InSendMessage() function. If this function returns TRUE, the window procedure should call ReplyMessage()
before any function that causes the thread to yield control, as shown in the following example:
case WM_USER + 5:
if (InSendMessage())
ReplyMessage(TRUE);
DialogBox(hInst, "MyDialogBox", hWndMain, (DLGPROC) MyDlgProc);
break;
A number of messages can be sent to controls in a dialog box. These control messages set the appearance, behavior, and
content of controls or retrieve information about controls. For example, the CB_ADDSTRING message can add a string
to a combo box, and the BM_SETCHECK message can set the check state of a check box or radio button.
Use the SendDlgItemMessage() function to send a message to a control, specifying the identifier of the control
and the handle of the dialog box window that contains the control. The following example, taken from a dialog box
procedure, copies a string from a combo box's edit control into its list box. The example uses
SendDlgItemMessage() to send a CB_ADDSTRING message to the combo box:
HWND hWndCombo;
int cTxtLen;
PSTR pszMem;
switch (uMsg) {
case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDD_ADDCBITEM:
// Get the handle of the combo box and the
// length of the string in the edit control
// of the combo box.
hWndCombo = GetDlgItem(hWndDlg, IDD_COMBO);
cTxtLen = GetWindowTextLength(hWndCombo);
Timers
A timer is an internal routine that repeatedly measures a specified interval, in milliseconds.
About Timers
An application uses a timer to schedule an event for a window after a specified time has elapsed. Each time the specified
interval (or time-out value) for a timer elapses, the system notifies the window associated with the timer. Because a
timer's accuracy depends on the system clock rate and how often the application retrieves messages from the queue,
the time-out value is only approximate.
1. Timer Operations
Applications create timers by using the SetTimer() function. A new timer starts timing the interval as soon as its
created. An application can change a timer's time-out value by using SetTimer() and can destroy a timer by using the
KillTimer() function. To use system resources efficiently, applications should destroy timers that are no longer
necessary.
Each timer has a unique identifier. When creating a timer, an application can either specify an identifier or have the
system create a unique value. The first parameter of a WM_TIMER message contains the identifier of the timer that
posted the message.
If you specify a window handle in the call to SetTimer(), the application associates the timer with that window.
Whenever the time-out value for the timer elapses, the system posts a WM_TIMER message to the window associated
with the timer. If no window handle is specified in the call to SetTimer(), the application that created the timer must
monitor its message queue for WM_TIMER messages and dispatch them to the appropriate window. If you specify a
TimerProc() callback function, the default window procedure calls the callback function when it processes
WM_TIMER. Therefore, you need to dispatch messages in the calling thread, even when you use TimerProc()
instead of processind WM_TIMER.
2. High-Resolution Timer
A counter is a general term used in programming to refer to an incrementing variable. Some systems include a high-
resolution performance counter that provides high-resolution elapsed times.
If a high-resolution performance counter exists on the system, you can use the QueryPerformanceFrequency()
function to express the frequency, in counts per second. The value of the count is processor dependent. On some
processors, for example, the count might be the cycle rate of the processor clock.
The QueryPerformanceCounter() function retrieves the current value of the high-resolution performance counter. By
calling this function at the beginning and end of a section of code, an application essentially uses the counter as a high-
resolution timer. For example, suppose that QueryPerformanceFrequency() indicates that the frequency of the high-
resolution performance counter is 50,000 counts per second. If the application calls QueryPerformanceCounter()
immediately before and immediately after the section of code to be timed, the counter values might be 1500 counts and
3500 counts, respectively. These values would indicate that 0.04 seconds (2000 counts) elapsed while the code
executed.
Menus
A menu is a list of items that specify options or groups of options (a submenu) for an application. Clicking a menu item
opens a submenu or causes the application to carry out a command.
About Menus
1. Menu Bars and Menus
A menu is arranged in a hierarchy - at the top level of the hierarchy is the menu bar, which contains a list of menus,
which in turn can contain submenus. A menu bar is sometimes called a top-level menu, and the menus and submenus
are also known as pop-up menus.
A menu item can either carry out a command or open a submenu. An item that carries out a command is called a
command item or a command.
An item on the menu bar almost always opens a menu. Menu bars rarely contain command items. A menu opened from
the menu bar drops down from the menu bar and is sometimes called a drop-down menu. When a drop-down menu is
displayed, it is attached to the menu bar. A menu item on the menu bar that opens a drop-down menu is also called a
menu name.
The menu names on a menu bar represent the main categories of commands that an application provides. Selecting a
menu name from the menu bar typically opens a menu whose menu items correspond to the commands in a category.
For example, a menu bar might contain a File menu name that, when clicked by the user, activates a menu with menu
items such as New, Open, and Save. To get information about a menu bar, call the GetMenuBarInfo() function.
Only an overlapped or pop-up window can contain a menu bar. A child window cannot contain one. If the window has a
title bar, the system positions the menu bar just below it. A menu bar is always visible. A submenu is not visible,
however, until the user selects a menu item that activates it.
Each menu must have an owner window. The system sends messages to a menu's owner window when the user selects
the menu or chooses an item from the menu.
A shortcut menu remains hidden until the user activates it, typically by right-clicking a selection, a toolbar, or a taskbar
button. The menu is usually displayed at the position of the caret or mouse cursor.
The window menu provides a standard set of menu items that the user can choose to change a window's size or
position, or close the application. Items on the window menu can be added, deleted, and modified, but most
applications just use the standard set of menu items. An overlapped, pop-up, or child window can have a window menu.
It is uncommon for an overlapped or pop-up window not to include a window menu.
When the user chooses a command from the window menu, the system sends a WM_SYSCOMMAND message to the
menu's owner window. In most applications, the window procedure does not process messages from the window menu.
Instead, it simply passes the messages to the DefWindowProc() function for system-default processing of the
message. If an application adds a command to the window menu, the window procedure must process the command.
An application can use the GetSystemMetrics() function to create a copy of the default window menu to modify.
Any window that does not use the GetSystemMenu() function to make its own copy of the window receives the
standard window menu.
Keystroke Action
Selects the first menu item with the specified character as its access key. If the selected
Alphabetical character item invokes a menu, the menu is displayed and the first item is highlighted. Otherwise,
the menu item is chosen.
ALT Toggles in and out of menu bar mode.
ALT + SPACEBAR Displays the window menu.
Activates a menu and selects the first menu item if an item has a menu associated with
ENTER it. Otherwise, this keystroke chooses the item as if the user released the mouse button
while the item was selected.
ESC Exists menu mode.
Cycles to the previous top-level menu item. Top-level menu items include menu names
LEFT ARROW and the window menu. If the selected item is in a menu, the previous column in the
menu is selected or the previous top-level menu item is selected.
Works like the LEFT ARROW key, except in the opposite direction. In menus, this
RIGHT ARROW keystroke moves forward one column. When the currently selected item is in the far-
right column, the next menu is selected.
Activates a menu when pressed in a menu name. When pressed in a menu, the UP
UP or DOWN ARROWS ARROW keystroke selects the previous item. The DOWN ARROW keystroke selects the
next item.
To create an access key for a menu item, precede any character in the item's text string with an ampersand (&). For
example, the text string "&Move" causes the system to underline the letter "M".
3. Menu Creation
You can create a menu using either a menu template or menu creation functions. Menu templates are typically defined
as resources. Menu-template resources can be loaded explicitly or assigned as the default menu for a window class. You
can also create menu-template resources dynamically in memory.
After you create a menu-template resource and add it to your application's executable (.exe) file, you can use the
LoadMenu() function to load the resource into memory. This function returns a handle to the menu, which you can
then assign to a window by using the SetMenu() function. You can assign a menu to any window that is not a child
window.
Implementing menus as resources makes an application easier to localize for use in multiple countries/regions. Only the
resource-definition file needs to be localized for each language, not the application's source code.
To retrieve a handle to the menu bar for a menu that has been created or loaded, use the GetMenu() function. To
retrieve a handle to the submenu associated with a menu item, use the GetSubMenu() or GetMenuItemInfo()
function. To retrieve a handle to a window menu, use the GetSystemMenu() function.
To display a shortcut menu use the TrackPopupMenuEx() function. Shortcut menus, also called floating pop-up
menus or context menus, are typically displayed when the WM_CONTEXTMENU message is processed.
You can assign a menu to any window that is not a child window.
The older TrackPopupMenu() function is still supported, but new applications should use the
TrackPopupMenuEx() function.
By default, every window is assigned the class menu for its window class so you do not need to explicitly load the menu
and assign it to each window. You can override the class menu by specifying a different menu handle in a call to the
CreateWindowEx() function. You can also change a window's menu after it is created by using the SetMenu()
function.
4. Menu Items
4.1 Command Items and Items that Open Submenus
When the user chooses a command item, the system sends a command message to the window that owns the menu:
If the command is on the window menu, the system sends the WM_SYSCOMMAND message.
Otherwise, the system sends the WM_COMMAND message.
Associated with each menu item that opens a submenu is a handle to the corresponding submenu. When the user
points to such an item, the system opens the submenu. No command message is sent to the owner window. However,
the system sends a WM_INITMENUPOPUP message to the owner window before displaying the submenu. You can get
the handle to the submenu associated with an item by using the GetSubMenu() or GetMenuItemInfo() function.
A menu bar typically contains menu names, but it can also contain command items. A submenu typically contains
command items, but it can also contain items that open nested submenus. By adding such items to submenus, you can
nest menus to any depth. To provide a casual cue for the user, the system automatically displays a small arrow to the
right of the text of a menu item that opens a submenu.
Menu items that open submenus have identifiers just as command items do. However, the system does not send a
command message when such an item is selected from a menu. Instead, the system opens the submenu associated with
the menu item.
To retrieve the identifier of the menu item at a specified position, use the GetMenuItemID() or
GetMenuItemInfo() function.
4.3 Menu-Item Position
In addition to having a unique identifier, each menu item in a menu bar or menu has a unique position value. The
leftmost item in a menu bar, or the top item in a menu, has position zero. The position value is incremented for
subsequent menu items. The system assigns a position value to all items in a menu, including separators. The following
illustration shows the position values of items in a menu bar and in a menu:
When calling a menu function that modifies or retrieves information about a specific menu item, you can specify the
item using either its identifier or its position.
Applications typically check or clear a menu item to indicate whether an option is in effect. For example, suppose an
application has a toolbar that the user can show or hide by using a Toolbar command on a menu. When the toolbar is
hidden, the Toolbar menu item is clear. When the user chooses the command, the application checks the menu item
and shows the toolbar.
A check mark attribute controls whether a menu item is selected. You can set a menu item's check mark attribute by
using the CheckMenuItem() function. You can use the GetMenuState() function to determine whether a menu
item is currently selected or cleared.
Instead of CheckMenuItem() and GetMenuState(), you can use the GetMenuItemInfo() and
SetMenuItemInfo() functions to retrieve and set the check state of a menu item.
Sometimes, a group of menu items corresponds to a set of mutually exclusive options. In this case, you can indicate the
selected option by using a selected radio menu item (analogous to a radio button control). Selected radio items are
displayed with a bullet bitmap instead of a check mark bitmap. To check a menu item and make it a radio item, use the
CheckMenuRadioItem() function.
By default, the system displays a check mark or bullet bitmap next to selected menu items, and no bitmap next to
cleared menu items. However, you can use the SetMenuItemBitmaps() function to associate application-defined
selected and cleared bitmaps with a menu items. The system then uses the specified bitmaps to indicate the menu
item's selected or cleared state.
Application-defined bitmaps associated with a menu item must be the same size as the default check mark bitmap, the
dimensions of which may vary depending on screen resolution. To retrieve the correct dimensions, use the
GetSystemMetrics() function. You can create multiple bitmap resources for different screen resolutions. Create
one bitmap resource and scale it, if necessary. Or create a bitmap at run time and draw an image in it. The bitmaps may
be either monochrome or color. However, because menu items are inverted when highlighted, the appearance of
certain inverted color bitmaps may be undesirable.
When menu items are not available to the user, they should be grayed or disabled. Grayed and disabled menu items
cannot be chosen. A disabled item looks just like an enabled item. When the user clicks on a disabled item, the item is
not selected, and nothing happens. Disabled items can be useful in, for example, a tutorial that presents a menu that
looks active but isn't.
An application grays an unavailable menu item to provide a visual cue to the user that a command is not available. You
can use a grayed item when an action is not appropriate (for example, you can gray the Print command in the File menu
when the system does not have a printer installed).
The EnableMenuItem() function enables, grays, or disables a menu item. To determine whether a menu item is
enabled, grayed, or disabled, use the GetMenuItemInfo() function.
Instead of GetMenuItemInfo() you can use the GetMenuState() function to determine whether a menu item is
enabled, grayed, or disabled.
When a menu contains more items than will fit in one column, the menu will be truncated. You can cause a column
break to occur at a specific item in a menu by assigning it the MFT_MENUBREAK type flag to the item or by using the
MENUBREAK option in the MENUITEM statement. The system places that item and all subsequent items in a new
column. The MFT_MENUBREAK type flag has the same effect, except that a vertical line appears between the new
column and the old.
If you use the AppendMenu(), InsertMenu(), or ModifyMenu() functions to asign line breaks, you should assign
the type flags MF_MENUBREAK or MF_MENUBARBREAK.
When the user activates an item on the menu bar, the owner window first receives a WM_SYSCOMMAND message. This
message includes a flag that indicates whether the user activated the menu by using the keyboard (SC_KEYMENU) or
the mouse (SC_MOUSEMENU).
Next, before displaying any menus, the system that sends the WM_INITMENU message to the window procedure so
that an application can modify the menus before the user sees them. The system sends the WM_INITMENU message
only once per menu activation.
Each time the user moves the highlighting from one item to another, the system sends a WM_MENUSELECT message to
the window procedure of the menu's owner window. This message identifies the currently selected menu item. Many
applications provide an information area at the bottom of their main windows and use this message to display additional
information about the selected menu item.
When the user chooses a command item form a menu, the system sends a WM_COMMAND message to the window
procedure. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the
chosen item. The window procedure should examine the identifier and process the message accordingly.
You can save information for a menu using the MENUINFO structure. If the menu is defined with a
MENUINFO.dwStyle value of MNS_NOTIFYBYPOS, the system sends WM_MENUCOMMAND instead of WM_COMMAND
when an item is selected. This allows you to access the information in the MENUINFO structure and also provides the
index of the selected item directly.
Not all menus are accessible through a window's menu bar. Many applications display shortcut menus when the user
clicks the right mouse button at a specific location. Such applications should process the WM_CONTEXTMENU message
and display a shortcut menu, if appropriate. If an application does not display a shortcut menu, it should pass the
WM_CONTEXTMENU message to the DefWindowProc() function for default processing.
The WM_MENURBUTTONUP message is sent when the user releases the right mouse button while the cursor is on a
menu item. This message is provided so that applications can display a context-sensitive or shortcut menu for a menu
item.
There are a few messages that only involve drag-and-drop menus. The WM_MENUGETOBJECT is sent to the owner of a
drag-and-drop menu when the mouse cursor enters a menu item or moves from the center of an item to the top or
bottom of an item. The WM_MENUDRAG message is sent when the user actually drags a menu item.
When a drop-down menu or a submenu has been destroyed, the system sends a WM_UNINITMENUPOPUP message.
6. Menu Destruction
If a menu is assigned to a window and that window is destroyed, the system automatically destroys the menu and its
submenus, freeing the menu's handle and the memory occupied by the menu. The system does not automatically
destroy a menu that is not assigned to a window. An application must destroy the unassigned menu by calling the
DestroyMenu() function. Otherwise, the menu continues to exist in the memory even after the application closes. To
end the calling thread's active menu, use EndMenu(). If a platform does not support EndMenu(), send the owner of
the active menu a WM_CANCELMODE message.
Windows Controls
A control is a child window that an application uses in conjunction with another window to enable user interaction.
Controls are most often used within dialog boxes, but they can also be used in other windows. Controls within dialog
boxes provide the user with a way to type text, choose options, and initiate actions. Controls in other windows provide a
variety of services, such as letting the user choose commands, view status, and view and edit text.
Because various versions of ComCtl32.dll were districuted with Internet Explorer, the version that is active is sometimes
different from the version that was shipped with the operating system. Therefore, your application must directly
determine which version of ComCtl32.dll is present.
In the common controls reference documentation, many programming elements specify a minimum supported DLL
version number. This version number indicates that the programming element is implemented in that version and
subsequent versions of the DLL unless otherwise specified. If no version number is specified, the programming element
is implemented in all existing versions of the DLL.
The following sample function loads a specified DLL and attempts to call its DllGetVersion() function. If successful,
it uses a macro to pack the major and minor version numbers from the DLLVERSIONINFO structure into a DWORD that
is returned to the calling application. If the DLL does not export DllGetVersion(), the function returns zero. You
can modify the function to handle the possibility that DllGetVersion() returns a DLLVERSIONINFO2 structure. If
so, use the information in that DLLVERSIONINFO2 structure's ullVersion member to compare versions, build
numbers, and service pack releases. The MAKEDLLVERULL macro simplifies the task of comparing these values to
those in ullVersion.
#include <Windows.h>
#include <Windef.h>
#include <Winbase.h>
#include <shlwapi.h>
if (hInstDll) {
DLLGETVERSIONPROC pDllGetVersion;
pDllGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hInstDll,
"DllGetVersion");
// Because some DLLs might not implement this function, you must
// test for it explicitly. Depending on the particular DLL, the
// lack of the DllGetVersion() function can be a useful indicator
// of the version.
if (pDllGetVersion) {
DLLVERSIONINFO dvi;
HRESULT hr;
ZeroMemory(&dvi, sizeof(dvi));
dvi.info1.cbSize = sizeof(dvi);
hr = (*pDllGetVersion)(&dvi);
if (SUCCEEDED(hr)) {
dwVersion = PACKVERSION(dvi.info1.dwMajorVersion,
dvi.info1.dwMinorVersion);
}
}
FreeLibrary(hInstDll);
}
return dwVersion;
}
It should be noted that using LoadLibrary() incorrectly can pose security risks.
The following code example shows how you can use the GetVersion() function to test whether ComCtl32.dll is
version 6.0 or later:
LPCTSTR lpszDllName = L"C:\\Windows\\System32\\ComCtl32.dll";
DWORD dwVer = GetVersion(lpszDllName);
DWORD dwTarget = PACKVERSION(6, 0);
if (dwVer >= dwTarget) {
// This version of ComCtl32.dll is version 6.0 or later
} else {
// Proceed knowing that version 6.0 or later additions
// are not available. Use an alternate approach for the
// older DLL version.
}
For example, the macro name _WIN32_IE is commonly found in older headers. You are responsible for defining the
macro as a hexadecimal number. This version number defines the target version of the application that is using the DLL.
The following table shows the available version numbers and the effect each has on your application:
Version Action
The application is compatible with ComCtl32.dll version 4.70 and later. The application
0x0300
cannot implement features that were added after version 4.70.
The application is compatible with ComCtl32.dll version 4.71 and later. The application
0x0400
cannot implement features that were added after version 4.71.
The application is compatible with ComCtl32.dll version 4.72 and later. The application
0x0401
cannot implement features that were added after version 4.72.
The application is compatible with ComCtl32.dll version 5.80 and later. The application
0x0500
cannot implement features that were added after version 5.80.
The application is compatible with ComCtl32.dll version 5.81 and later. The application
0x0501
cannot implement features that were added after version 5.81.
The application is compatible with ComCtl32.dll version 6.0 and later. The application
0x0600
cannot implement features that were added after version 6.0.
If you do not define the _WIN32_IE macro in your project, it is automatically defined as 0x0500. To define a different
value, you can add the following to the compiler directives in your make file. Substitute the desired version number for
0x0400:
/D _WIN32_IE=0x0400
Another method is to add a line similar to the following in your source code before you include the Shell header files.
Substitute the desired version number for 0x0400:
#define _WIN32_IE 0x0400
#include <commctrl.h>
You create a common control by specifying the name of the window class when calling the CreateWindowEx()
function or by specifying the appropriate class name in a dialog box template.
Each type of common control has a set of control styles that you can use to vary the appearance and behavior of the
control. The common control library also includes a set of control styles that apply to two or more types of common
controls.
3. Unicode Support for Common Controls
For versions 5.80 and later of ComCtl32.dll, common controls notifications support both ANSI and Unicode formats on
Windows 95 systems or later. The system determines which format to use by sending your window a
WM_NOTIFYFORMAT message. To specify a format, return NFR_ANSI for ANSI notifications or NFR_UNICODE for
Unicode notifications. If you do not handle this message, the system calls IsWindowsUnicode() to determine the
format. Since Windows 95 and Windows 98 always return FALSE to this function call, they use ANSI notifications by
default.
Control Messages
1. Messages to Common Controls
Because common controls are windows, an application can communicate with them by using common Microsoft Win32
messages such as WM_GETFONT or WM_SETTEXT. In addition, the window class of each common control supports a set
of control-specific messages. Typically, an application uses SendMessage() or SendDlgItemMessage() to pass
messages to the control (often receiving information in the return value).
Some common controls also have a set of macros that an application can use instead of SendMessage(). The macros
are typically easier to use than the functions. The following example code retrieves the text of the selected tree-view
item, first by using the raw messages, and second by using the equivalent macros. Assume that hWnd is the handle of
the control window:
BOOL fSuccess;
WCHAR itemText[99];
TVITEM tvItem = {};
tvItem.mask = TVIF_TEXT;
tvItem.cchTextMax = ARRAYSIZE(itemText);
tvItem.pszText = itemText;
// This...
tvItem.hItem = (HTREEITEM)SendMessage(hWnd, TVM_GETNEXTITEM, TVGN_CARET,
NULL);
fSuccess = SendMessage(hWnd, TVM_GETITEM, 0, (LPARAM)&tvItem);
When a change is made to the system color settings, Windows sends a WM_SYSCOLORCHANGE message to all top-level
windows. Your top-level window must forward the WM_SYSCOLORCHANGE to its common controls. Otherwise, the
controls will not be notified of the color change. Forwarding the message ensures that the colors used by your common
controls are consistent with those used by other user interface objects. For example, a toolbar control uses the "3-D
Objects" color to draw its buttons. If the user changes the 3-D Objects color but the WM_SYSCOLORCHANGE message is
not forwarded to the toolbar, the toolbar buttons will remain in their original color (or even change to a combination of
old and new colors) while the color of other buttons in the system changes.
The lParam parameter of WM_NOTIFY is either the address of an NMHDR structure or the address of a larger structure
that incldues NMHDR as its first member. The structure contains the notification code that identifies the common control
that sent the notification message. The meaning of the remaining structure members, if any, varies depending on the
notification code.
Each type of common control has a corresponding set of notification codes. The common control library also provides
notification codes that can be sent by more than one type of common control. See the documentation for the control of
interest to determine which notification codes it will send and what format they take.
Custom Controls
1. Creating Owner-Drawn Controls
Buttons, menus, static text controls, list boxes, and combo boxes can be created with an owner-drawn style flag. When a
control has the owner-drawn style, the system handles the user's interaction with the control as usual, performing such
tasks as detecting when a user has chosen a button and notifying the button's owner of the event. However, because the
control is owner-drawn, the parent window of the control is responsible for the visual appearance of the control. The
parent window receives a message whenever the control must be drawn.
For buttons and static text controls, the owner-drawn style affects how the system draws the entire control. For list
boxes and combo boxes, the parent window draws the items within the control, and the control draws its own outline.
Control Library
1. Buttons
A button is a control the user can click to provide input to an application.
There are several types of buttons and one or more button styles to distinguish between buttons of the same type.
The user clicks a button by using the mouse or keyboard. Clicking a button typically changes its visual appearance and
state (from checked to cleared, for example).
The system, the button, and the application cooperate in changing the button's appearance and state. A button can send
messages to its parent window, and a parent window can send messages to a button.
Some buttons are painted by the system, and some by the application.
Buttons can be used alone or in groups and can appear without application-defined text (a label).
The BM_SETCHECK message sets the check state of a check box or radio button (the message returns zero). The
BM_SETSTATE message sets the push state of a button (this message also returns zero). The BM_SETSTYLE message
changes the style of a button. It is designed for changing button styles within a type (for example, changing a check box
to an automatic check box). It is not designed for changing between types (for example, changing a check box to a radio
button). An application should not change a button from one type to another.
A button of the BS_BITMAP or BS_ICON style displays a bitmap or icon instead of text. The BM_SETIMAGE message
associates a handle to a bitmap or icon with a button. The BM_GETIMAGE message retrieves a handle to the bitmap or
icon associated with a button.
An application can also use the DM_GETDEFID message to retrieve the identifier or the default push button control in a
dialog box. An application can use the DM_SETDEFID message to set the default push button for a dialog box.
Both the message and the parent window's response depend on the type, style, and current state of the button.
Following are the button notification codes an application should monitor and process:
A button sends the BN_DISABLE, BN_PUSHED, BN_KILLFOCUS, BN_PAINT, BN_SETFOCUS, and BN_UNPUSHED
notification codes only if it has the BS_NOTIFY style. BN_DBLCLK notification codes are sent automatically for
BS_USERBUTTON, BS_RADIOBUTTON, and BS_OWNERDRAWN buttons. Other button types send BN_DBLCLK only if
they have the BS_NOTIFY style. All buttons send the BN_CLICKED notification code regardless of their button styles.
For automatic buttons, the system changes the push state and paints the button. In this case, the application typically
processes only the BN_CLICKED and BN_DBLCLK notification codes. For buttons that are not automatic, the
application typically responds to the notification code by sending a message to change the state of the button.
When the user selects an owner-drawn button, the button sends its parent window a WM_DRAWITEM message
containing the identifier of the control to be drawn and information about its dimensions and state.
The system sends a WM_CTLCOLORBTN message to a button's parent window before drawing a button. This message
contains a handle to the push button's device context and a handle to the child window. The parent window can use
these handles to change the button's text and background colors. However, only owner-drawn buttons respond to the
parent window processing the message.
The Resource Compiler
The Microsoft Windows Resource Compiler (RC) is a tool used in building Windows-based applications.
1. Create individual files for your cursors, icons, bitmaps, dialog boxes, and fonts.
2. Create a resource-definition script (.rc) file that describes the resources used by your application.
3. Compile the script with RC.
4. Link the compiled resource (.res) file into the application's executable file with your linker.
A resource file is a text file with the extension .rc. The file can use single-byte, double-byte, or Unicode characters. The
synta and semantics of the RC preprocessor are similar to those of the Microsoft C/C++ compiler. However, RC supports
a subset of the preprocessor directives, and pragmas in a script.
The script file defines resources. For a resource that exists in a separate file, such as an icon or cursor, the script specifies
the resource and the file that contains it. For some resources, such as a menu, the entire definition of the resource exists
within the script.
1. Comments
RC supports C-style syntax for both single-line comments and block comments:
Single-line comments begin with two forward slashes (//) and run to the end of the line.
Block comments begin with an opening delimiter (/*) and run to the closing delimiter (*/).
2. Predefined Macros
RC does not support the ANSI C predefined macros (__DATE__, __FILE__, __LINE__, __STDC__, __TIME__,
__TIMESTAMP__). Therefore, you cannot include these macros in header files that you will include in your resource
script.
RC does define RC_INVOKED, which enables you to conditionally compile portions of your header files, depending on
whether the compiler is your C compiler or the RC compiler. This is important because the RC compiler supports only a
subset of the statements a C compiler would support.
To conditionally compile your code with the RC compiler, surround the code that RC cannot compile with #ifndef
RC_INVOKED and #endif.
3. Preprocessor Directives
You can use the directives described in the following table as needed in your resource script. They instruct RC to perform
actions or to assign values to names.
Directive Description
#define Defines a specified name by assigning it a given value.
#elif Marks an optional clause of a conditional-compilation block.
#else Marks the last optional clause of a conditional-compilation block.
#endif Marks the end of a conditional-compilation block.
#if Conditionally compiles the script if a specified expression is true.
#ifdef Conditionally compiles the script if a specified name is defined.
#ifndef Conditionally compiles the script if a specified name is not defined.
#include Copies the contents of a file into the resource-definition file.
#undef Removes the definition of the specified name.
To define symbols for your resource identifiers, use the #define directive to define them in a header file. Include this
header both in the resource script and your application source code. Similarly, you define the values for resource
attributes and styles by including Windows.h in the resource script.
RC treats files with the .c and .h extensions in a special manner. It assumes that a file with one of these extensions does
not contain resources. If a file has the .c or .h file name extension, RC ignores all lines in the file except the preprocessor
directives. Therefore, to include a file that contains resources in another resource script, give the file to be included an
extension other than .c or .h.
4. Preprocessor Operators
RC supports using the standard C preprocessing operators in macro definitions. These operators are described in the
following table:
Operator Description
# Encloses the argument in quotes
#@ Encloses the argument in single quotes.
## Concatenates tokens used as arguments to form other tokens.
5. Pragma Directives
RC does not support the pragma directives supported by the C/C++ compiler. However, RC does support the following
pragma directive for changing the code page:
#pragma code_page( [ DEFAULT | CodePageNum ] )
This pragma is not supported in an included resource file (.rc). Therefore, if you have a parent .rc file and it includes .rc
files for multiple languages, use this pragma before including another .rc file rather than using it in the included .rc file
itself.
6. Resource-Definition Statements
The resource-definition statements define the resources that the resource compiler puts in the resource ( .res) file. After
the .res file is linked to the executable file, the application can load its resources at run time as needed. All resource
statements associate an identifying name or number with a given resource.
Resources
Controls
Statements
6.1 Resources
Operator Description
ACCELERATORS Defines menu accelerator keys.
Defines a bitmap by naming it and specifying the name of the file that contains it. (To
BITMAP
use a particular bitmap, the application requests it by name.)
Defines a cursor or animated cursor by naming it and specifying the name of the file
CURSOR
that contains it. (To use a particular cursor, the application requests it by name.)
DIALOG Defines a template that an application can use to create dialog boxes.
DIALOGEX Defines a template that an application can use to create dialog boxes.
FONT Specifies the name of a file that contains a font.
HTML Specifies an HTML file.
Defines an icon or animated icon by naming it and specifying the name of the file that
ICON
contains it. (To use a particular cursor, the application requests it by name.)
MENU Defines the appearance and function of a menu.
MENUEX Defines the appearance and function of a menu.
Defines a message table by naming it and specifying the name of the file that contains
MESSAGETABLE
it. The file is a binary resource file generated by the message compiler.
POPUP Defines a menu item that can contain menu items and submenus.
Defines data resources. Data resources let you include binary data in the executable
RCDATA
file.
STRINGTABLE Defines string resources that can be loaded from the executable file.
TEXTINCLUDE A special resource that is interpreted by Visual C++.
TYPELIB A special resource that is used with the /TLBID and /TLBOUT linker options.
User-Defined Defines a resource that contains application-specific data.
Defines a version-information resource. Contains information such as the version
VERSIONINFO
number, intended operating system, and so on.
6.2 Controls
Control Description
AUTO3STATE Creates an automatic three-state check box control.
AUTOCHECKBOX Creates an automatic check box control.
AUTORADIOBUTTON Creates an automatic radio button control.
CHECKBOX Creates a check box control.
COMBOBOX Creates a combo box control.
CONTROL Creates an application-defined control.
CTEXT Creates a centered-text control.
DEFPUSHBUTTON Creates a default pushbutton control.
EDITTEXT Creates an edit control.
GROUPBOX Creates a group box control.
ICON Creates an icon control. This control is an icon displayed in a dialog box.
LISTBOX Creates a list box control.
LTEXT Creates a left-aligned text control.
PUSHBOX Creates a push box control.
PUSHBUTTON Creates a push button control.
RADIOBUTTON Creates a radio button control.
RTEXT Creates a right-aligned text control.
SCROLLBAR Creates a scroll bar control.
STATE3 Creates a three-state check box control.
6.3 Statements
Statement Description
CAPTION Sets the title for a dialog box.
Specifies information about a resource that can be used by a tool that can read or
CHARACTERISTICS
write resource-definition files.
CLASS Sets the class of the dialog box.
EXSTYLE Sets the extended window style of the dialog box.
FONT Sets the font with which the system will draw text for the dialog box.
Sets the language for all resources up to the next LANGUAGE statement or to the end
of the file. When the LANGUAGE statement appears before the beginning of the body
LANGUAGE
of an ACCELERATORS, DIALOG, MENU, RCDATA or STRINGTABLE resource
definition, the specified language applies only to that resource.
MENU Sets the menu for the dialog box.
MENUITEM Defines a menu item.
STYLE Sets the window style for the dialog box.
Specifies version information for a resource that can be used by a tool that can read or
VERSION
write resource-definition files.
ShapesMenu MENU
{
POPUP "&Shape"
{
MENUITEM "&Clear", ID_CLEAR
MENUITEM "&Rectangle", ID_RECT
MENUITEM "&Triangle", ID_TRIANGLE
MENUITEM "&Star", ID_STAR
MENUITEM "&Ellipse", ID_ELLIPSE
}
}
The CURSOR statement names the application's cursor resource ShapesCursor and specifies the cursor file
SHAPES.CUR, which contains the image for that cursor.
The ICON statement names the application's icon resource ShapesIcon and specifies the icon file SHAPES.ICO, which
contains the image for that icon.
The MENU statement defines an application menu named ShapesMenu, a pop-up menu with five menu items.
The body of the menu definition, enclosed by curly braces, or the BEGIN and END keywords, specifies each menu item
and the menu identifier that is returned when the user selects that item. For example, the first item on the menu, Clear,
returns the menu identifier ID_CLEAR when the user selects it. The menu identifiers are defined in the application
header file, SHAPES.H.