A device context (DC) is a Windows data structure that contains information about the drawing attributes and capabilities of an output device. These attributes include settings such as the current pen, brush, font, text alignment, mapping mode, and drawing colours.
When an application needs to draw output to a device such as the screen or a printer, it must first obtain a handle to a device context (HDC). Windows provides this handle and associates it with the target device. The drawing functions in the Windows API then use the information stored in the device context to determine how output should be rendered.
The process of sending graphical output to a window is known as painting.
A window may need to be painted or repainted when it is first created, resized, restored, or whenever part of its client area becomes invalid. In versions of Windows prior to Vista, this commonly occurred when a window that had been partially covered by another window became visible again. Modern versions of Windows use desktop composition, which reduces the need for repainting.
Usually, an application is responsible only for painting the client area of a window. The client area is the rectangular region inside the window’s borders where the application’s content is displayed. It does not include non-client areas such as the title bar, window frame, menus, system menu, or scroll bars.
The operating system is responsible for drawing and managing the non-client areas of a window, including the title bar and borders.
System Generated Repaint Requests
Windows does not maintain a permanent copy of an application’s window contents. When all or part of a window’s client area becomes invalid—for example, because the window has been resized, restored, uncovered, or explicitly invalidated—Windows sends the application a WM_PAINT message to indicate that the affected area must be redrawn.
The portion of the client area that requires repainting is known as the update region (or invalid region). Windows maintains the size and coordinates of this region for each window so that only the affected area needs to be repainted.
BeginPaint()
The BeginPaint() function is called in response to a WM_PAINT message to prepare a window for repainting. It returns a handle to a device context (DC) that is valid only for painting the window’s client area and fills a PAINTSTRUCT structure with information about the update region.
The PAINTSTRUCT includes a rectangle (rcPaint) that identifies the portion of the client area requiring repainting, allowing the application to redraw only the necessary area rather than the entire window. Before control is returned to the application, BeginPaint() also erases the background of the update region if the window class specifies a background brush.
After repainting has been completed, the application must call EndPaint(). This function releases the painting device context and validates the update region, informing Windows that the repaint request has been completed.
If an application fails to call EndPaint(), or otherwise leaves the update region invalid, Windows will continue to generate WM_PAINT messages resulting in what appears to be an endless repaint cycle.
The prototype of the BeginPaint function is as follows-
HDC BeginPaint(HWND hwnd,LPPAINTSTRUCT lpPaint);
Where
hwnd is the handle of the window for which the device context is being obtained
lpPaint is a pointer to a PAINTSTRUCT structure.
If the function is successful, its return value is the device context. If it fails, the return value is NULL.
The prototype of PAINTSTRUCT is as follows –
typedef struct tagPAINTSTRUCT {HDC hdc;BOOL fErase;RECT rcPaint;BOOL fRestore; BOOL fIncUpdate;BYTE rgbReserved[16];} PAINTSTRUCT;
Only 3 parameters are available to the user application; the rest are filled in by Windows when the user application calls BeginPaint. The hdc field is the handle to the device context returned from BeginPaint, fErase specifies whether the background needs to be redrawn, and rcPaint specifies the upper left and lower right corners of the rectangle in which the ‘painting’ is requested.
The EndPaint() function is required for each call to the BeginPaint function to validate the client after the screen ‘painting’ is complete. It has the following syntax
BOOL EndPaint(HWND hwnd, const PAINTSTRUCT *lpPaint);
Where hwnd is the Handle to the window that has been ‘repainted’ and lpPaint is a Pointer to a PAINTSTRUCT structure. The return value is always nonzero.
Other Device Context-Related API Functions
GetDC()
The GetDC() function retrieves a handle to a display device context (DC) for the client area of a specified window or, if required, for the entire screen. Unlike BeginPaint(), which is used only in response to a WM_PAINT message, GetDC() can be called at any time when an application needs to draw immediately.
Typical uses include drawing in response to mouse or keyboard input, displaying temporary graphics, or obtaining information about the display device. Since GetDC() is not associated with the window’s update region, it allows drawing anywhere within the window’s client area.
The prototype for this function is:
HDC GetDC(HWND hWnd);
Where hWnd is a handle to the window whose device context is required. If this value is NULL, GetDC() retrieves the device context for the entire screen. If the function succeeds, the return value is a handle to the device context for the specified window’s client area. If the function fails, the return value is NULL.
GetWindowDC()
The GetWindowDC() function is similar to GetDC(), but it retrieves a device context (DC) for the entire window, including both the client area and the non-client area. The non-client area includes the title bar, window frame, scroll bars, and any other window decorations managed by the operating system.
Unlike GetDC(), whose coordinate origin is the upper-left corner of the client area, the device context returned by GetWindowDC() has its origin at the upper-left corner of the entire window. This allows an application to draw anywhere within the window, including the non-client area.
Like GetDC(), the device context obtained from GetWindowDC() must be released by calling ReleaseDC() when it is no longer required.
In modern Windows applications, GetWindowDC() is used relatively infrequently because the operating system is responsible for painting most non-client areas. It is typically used only by applications that need to customize the appearance of the window frame or other non-client elements.
The prototype for this function is:
HDC GetWindowDC(HWND hWnd);
Where hWnd is a handle to the window whose device context is required. If this value is NULL, GetWindowDC() retrieves the device context for the entire screen. If the function succeeds, the return value is a handle to the device context for the specified window. If the function fails, the return value is NULL.
ReleaseDC()
The ReleaseDC() function releases a device context (DC) that was obtained by calling either GetDC() or GetWindowDC(). Releasing the device context returns it to the operating system so that it can be reused by other applications
The prototype for this function is:
int ReleaseDC(HWND hWnd,HDC hdc);
Where hWnd is a handle to the window whose device context is to be released, and hdc is the device context to be released. The return value indicates whether the device context was released successfully, with a value of 1 indicating success and 0 indicating failure.
ValidateRect()
Allows an application to validate a Windows region manually. The prototype for this function is
BOOL ValidateRect(HWND hWnd,const RECT *lpRect);
Where
hWnd is a handle to the window.
lpRect is a pointer to a RECT structure that contains the client coordinates of the rectangle to be removed from the update region. If the hWnd parameter is NULL the system invalidates and ‘redraws’ the entire window. If the RECT structure is NULL the entire client area is removed from the update rectangle. If the function is successful, the return value is nonzero.
If the function fails, the return value is zero.
InvalidateRect()
Allows an application to invalidate a Windows region manually and tells Windows to ‘repaint’ that region.
The prototype for this function is
BOOL InvalidateRect(HWND hWnd,const RECT *lpRect,BOOL bErase);
where
hWnd – is a handle to the window that needs to be updated. If this parameter is NULL, the system invalidates and redraws all windows, not just the windows for this application.
lpRect – is a pointer to a RECT structure containing the client coordinates of the update region. If the parameter is NULL, the entire client area is set for update.
bErase – specifies whether the background within the update region is to be erased when the update region is processed. If this parameter is TRUE, the background is erased when the BeginPaint function is called. If this parameter is FALSE, the background remains unchanged.
If the function is successful then the return value is nonzero. If the function fails, the return value is zero.
SaveDC and RestoreDC.
During a drawing operation, an application often changes the attributes of a device context by selecting different pens, brushes, fonts, colours, mapping modes, or clipping regions. If the original settings need to be restored later, the current state of the device context can be saved by calling the SaveDC() function.
SaveDC() stores the complete state of the device context on an internal stack and returns an integer identifying the saved state. The application can then modify the device context as required. When drawing has been completed, the original settings can be restored by calling RestoreDC().
Using SaveDC() and RestoreDC() allows an application to make temporary changes to a device context without having to save and restore each attribute individually. This simplifies drawing code and ensures that the device context is returned to its original state before further drawing operations are performed.
Example
The following program demonstrates the WM_PAINT message by keeping a running total of client area repaints. Clicking the minimise and maximise icons or resizing the window will generate a repaint request.