What Is GDI and How MFC Wraps It
The Graphics Device Interface (GDI) is the Windows subsystem that mediates between an application and physical output devices — monitors, printers, plotters. Every drawing operation, from a single pixel to a filled polygon, passes through a device context (DC), represented at the Win32 level by an HDC handle. MFC wraps this handle in the CDC class hierarchy (CDC, CPaintDC, CClientDC, CWindowDC, CMetaFileDC), giving you type-safe, RAII-managed access to the same GDI functions — LineTo, Rectangle, TextOut — without manual handle bookkeeping.
Cricket analogy: Just as Virat Kohli's cover drive works the same whether he's playing at the MCG or Eden Gardens because the bat-and-ball mechanics don't change, GDI's drawing functions like LineTo behave identically whether the HDC points to a monitor or a printer.
Device Contexts and Handles
An HDC is a resource handle, not a pointer you can dereference — it identifies a drawing surface plus its current state (selected pen, brush, font, mapping mode) inside the GDI subsystem. MFC's OnPaint handler receives no HDC directly; instead you construct a CPaintDC on the stack, whose constructor calls BeginPaint and whose destructor calls EndPaint automatically. Outside WM_PAINT, CClientDC gives you a DC for the client area on demand (e.g., in response to a mouse click), while CWindowDC covers the entire window including the non-client frame.
Cricket analogy: Like a scorer's official scorebook entry that exists only for the duration of an innings and is opened and closed by the umpire's signals, CPaintDC opens with BeginPaint at the start of WM_PAINT and closes with EndPaint automatically when the stack object is destroyed.
GDI Objects: Pens, Brushes, and the SelectObject Pattern
Drawing attributes — line color and width, fill color, font — are themselves GDI objects: CPen, CBrush, CFont, CBitmap. You select one into a DC with CDC::SelectObject, which returns a pointer to the previously selected object. The universal MFC idiom is to save that returned pointer and call SelectObject again with it before the DC or the custom object goes out of scope, restoring the DC's original state and avoiding orphaned GDI handles, which are a limited system resource.
Cricket analogy: Like a captain rotating bowlers — bringing on a spinner for a specific over and then reverting to the pace bowler — SelectObject swaps in a CPen for a drawing operation and the saved pointer lets you bring back the original 'bowler' afterward.
void CMyView::OnPaint()
{
CPaintDC dc(this); // constructor calls BeginPaint
CPen pen(PS_SOLID, 2, RGB(0, 102, 204));
CPen* pOldPen = dc.SelectObject(&pen);
CBrush brush(RGB(255, 240, 200));
CBrush* pOldBrush = dc.SelectObject(&brush);
dc.Rectangle(20, 20, 220, 140);
// Always restore the DC's original objects before they go out of scope
dc.SelectObject(pOldPen);
dc.SelectObject(pOldBrush);
} // destructor calls EndPaintCoordinate Systems and Mapping Modes
By default MFC device contexts use MM_TEXT, where one logical unit equals one device pixel and the origin sits at the top-left corner with y increasing downward. CDC::SetMapMode lets you switch to modes like MM_LOENGLISH (0.01 inch units, y increasing upward) or MM_ISOTROPIC/MM_ANISOTROPIC combined with SetWindowExt/SetViewportExt for custom scaling — essential when you want drawings to scale consistently across different screen resolutions or when targeting printers with a fixed physical unit.
Cricket analogy: Like how pitch measurements are always given in fixed real-world units (22 yards) regardless of which stadium's scale you're viewing on TV, MM_LOENGLISH fixes logical units to real inches regardless of screen pixel density.
Every CPen, CBrush, or CFont you SelectObject into a DC must be deselected (by reselecting the previously saved object) before the GDI object is destroyed or goes out of scope. Deleting a GDI object while it is still selected into a device context is undefined behavior and a classic source of intermittent crashes and GDI handle leaks that show up only after prolonged use.
- GDI is the Windows layer that abstracts drawing from the specific output device; MFC's CDC hierarchy wraps the raw HDC handle.
- CPaintDC is used exclusively inside OnPaint and automatically brackets BeginPaint/EndPaint via its constructor and destructor.
- CClientDC and CWindowDC provide device contexts on demand outside WM_PAINT, scoped to the client area or full window respectively.
- GDI drawing attributes (pens, brushes, fonts) are objects you SelectObject into a DC, always saving and restoring the previous object.
- Never delete a GDI object while it remains selected into a device context — always deselect first.
- Mapping modes like MM_TEXT, MM_LOENGLISH, and MM_ISOTROPIC/MM_ANISOTROPIC control how logical coordinates translate to device pixels.
- GDI handles are a limited system resource; leaking pens, brushes, or DCs degrades the whole system, not just your app.
Practice what you learned
1. Which MFC class should you construct inside OnPaint to correctly bracket BeginPaint and EndPaint calls?
2. What does CDC::SelectObject return?
3. What is the default mapping mode for an MFC device context?
4. Why is it dangerous to delete a CBrush object while it is still selected into a DC?
5. Which class would you use to obtain a device context for the client area outside of a WM_PAINT handler, such as in response to a mouse click?
Was this page helpful?
You May Also Like
Drawing with CDC
A practical tour of the CDC drawing API — lines, shapes, text, and pens/brushes — and the conventions MFC applications follow to render correctly inside OnDraw and OnPaint.
MFC and GDI+
How to integrate GDI+ into an MFC application for anti-aliased vector graphics, alpha-blended bitmaps, and richer image format support alongside classic GDI.
Custom Drawing and Owner-Draw
How MFC lets you take over rendering of standard controls — buttons, list boxes, combo boxes, list controls — through the owner-draw mechanism and custom-draw notifications.
Double Buffering in MFC
Why complex or frequently updated views flicker under naive GDI drawing, and how to eliminate that flicker using an off-screen memory device context in MFC.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics