100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C++

GDI Basics in MFC

An introduction to the Windows Graphics Device Interface (GDI) and how MFC's CDC class family wraps device contexts, handles, and GDI objects for safe, structured drawing.

GraphicsBeginner9 min readJul 10, 2026
Analogies

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.

cpp
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 EndPaint

Coordinate 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

Was this page helpful?

Topics covered

#MFCMicrosoftFoundationClassesStudyNotes#MicrosoftTechnologies#GDIBasicsInMFC#GDI#MFC#Wraps#Device#StudyNotes#SkillVeris#ExamPrep