A device context is a Windows data structure containing information about the drawing attributes of an output device. When an application needs to send data to a screen or printer then it must first obtain a handle to a device context or DC of that device. Windows then fills the device context structure with the attribute values of the device being written to. The information held in the device context can then be used along with the relevant API function to display output as required. The process of writing to the screen is known as ‘painting’.
A window might need to be 'painted' or 'repainted' when the window is first created when the window is uncovered from behind another window, or as the result of some action by the user. Usually, an application will only be responsible for painting the client area. The client area is the rectangular part of a window inside the window’s border that doesn’t include the window frame, caption, menu, system menu, or scrollbars. The surrounding frame, including the title bar, is automatically painted by the operating system.
Windows does not keep a record of the application window content. If by some action, the client area or part of the client area is overwritten then widows will inform the application that the client area needs to ‘repainted’ by posting a WM_PAINT message to the application window. The region of the application client that needs updating is known as an “invalid area”. Windows maintains the size and coordinates of this region for each individual window.
The BeginPaint() function is generally used in conjunction with the WM_PAINT message to obtain a device context. It prepares the specified window for painting and fills a PAINTSTRUCT structure with information about the invalidated area which will exclude the area outside the update region. The device context returned from a call to BeginPaint is saved in an HDC datatype. The application can then use these values to redraw the contents of the window starting with the window's background which is repainted with the currentbrush selected in thedevice context.
Windows continues sending WM_PAINT messages to the message queue as long as there’s an invalidated area. It’s therefore important that the EndPaint() function is called after BeginPaint in order to validate the client area before leaving the WM_PAINT handler block. Failure to validate the client area will result in an endless WM_PAINT loop. The EndPaint marks the end of the screen validation and releases the display device context.
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. The return value of the function, if successful is a device context. If the function 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 is 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 are after 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.
The GetDC() function retrieves a handle to a display device context for the client area of a specified window or for the entire screen. GetDC is usually called when an application needs to 'repaint' the screen instantly and occurs in response to an action by the user which does not generate a WM_Paint message. The GetDC method 'paints' the Windows client area and not merely the invalid region. The prototype for this function is
HDC GetDC(HWND hWnd);
Where hWnd is a handle to the required windows device context. 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 DC for the required window’s client area. If the function fails, the return value is NULL.
Similar to the GetDC function the GetWindowDC() function retrieves the device context (DC) for the entire application window, including non-client areas such as the title and scroll bars. Since the origin of the device context will be the upper-left corner of the window instead of the client area the application will be able to paint anywhere in the application window. The prototype for this function is
HDC GetWindowDC(HWND hWnd);
Where hWnd is a handle to the required windows device context. 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 DC for the required window’s client area. If the function fails, the return value is NULL.
Releases a device context (DC), freeing it for use by other applications after a call to GetDC or GetWindowsDC. The prototype is
int ReleaseDC(HWND hWnd,HDC hdc);
Where hWnd is a handle to the window whose DC is to be released and hdc the device context to be released. The return value indicates whether the DC was released with a value of 1 indicating success and a value of zero indicating failure.
Allows an application to manually validate a Windows region. The prototype for this function is
BOOL ValidateRect(HWND hWnd,const RECT *lpRect);
Where hWnd is a handle to the window whose update region is to be modified and 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 then the system invalidates and 'redraws' the entire window. If the RECT structure is NULL then 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.
Allows an application to manually invalidate a windows region and tells windows that it needs to 'repaint' that region.
The prototype for this function is
BOOL InvalidateRect(HWND hWnd,const RECT *lpRect,BOOL bErase);
where
hWnd – is 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 that contains the client coordinates of the update region. If this 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.
Each time an application requests a device context its attributes are reset to the system's default and the default pen and brush will be used. In order to avoid this reinitialisaton, the current device context state can be saved with the API function SaveDC and restored with API function RestoreDC.
The following short program demonstrates the WM_PAINT message by keeping a running total of the number of times the client area has been repainted. The repaint request can be generated by dragging another window over the application window or by clicking the minimise and maximise icon.
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
MSG msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = TEXT("myWindowClass");
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wc);
CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("myWindowClass"), TEXT("Device Context"), WS_VISIBLE | WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 240, 120, NULL, NULL, hInstance, NULL);
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
TCHAR str[40]= TEXT("/0");
HDC hdc;
PAINTSTRUCT lpPaint;
static DWORD repaintcount = 0;
switch (msg)
{
case WM_PAINT://traps paint message
hdc = BeginPaint(hwnd,&lpPaint);
repaintcount += 1;
wsprintf(str,TEXT("repaint count %i"), repaintcount);//Microsoft specific function for converting characters and values
TextOut(hdc, 0, 0, str, lstrlen(str));//writes a character string at the specified location
EndPaint( hwnd, &lpPaint);
break;
case WM_CLOSE:
ReleaseDC(hwnd, hdc);//release device context
DeleteDC(hdc);//delete device context
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}