Level 0 · Chapter 9

USER & GDI objects

Windows, menus, brushes, and device contexts: the two object families every visible pixel and every clickable control is built from.

Window stations & desktops covered the containers. This chapter covers what actually lives inside one: the two parallel families of objects — USER and GDI — that make up literally everything visible or clickable in a Windows session.

Two families, two jobs

USER objects represent interaction and structure:

  • Windows — every visible rectangle you can click, drag, or type into, from a top-level app window down to an individual button control, is a window.
  • Menus, cursors, icons, accelerator tables — supporting interactive elements.
  • Hooks — callbacks that intercept messages (keystrokes, mouse movement) system-wide or per-thread, the mechanism behind everything from screen readers to (historically) some of the more invasive forms of malware.

GDI objects represent the resources needed to actually put pixels on screen:

  • Device contexts (DCs) — a drawing surface plus a bundle of currently-selected drawing attributes (which pen, which brush, which font). Every drawing operation goes through a DC.
  • Pens, brushes, fonts, bitmaps, regions — the actual resources a DC can have selected into it at any given time.

A window, in practice, is a USER object that owns (or borrows) a DC to draw its contents, which in turn has GDI objects selected into it for whatever's currently being rendered — a font for text, a brush for a background fill.

Why this is a message-passing system, not direct calls

Windows doesn't let one window's code directly poke at another window's state. Instead, USER objects communicate through a message queue: every thread that creates a window gets one, and events (a keypress, a mouse click, a repaint request, a custom application message) are posted or sent as discrete messages that the receiving window's procedure processes one at a time, on its own thread. This is why a window that's blocked in a long-running operation on its own thread stops responding to input — not because Windows failed, but because nothing is pulling new messages off its queue.

Quotas exist for a reason you've probably seen

Every process has a limit on how many USER and GDI objects it can hold open at once, and every session has an aggregate limit as well. This isn't an arbitrary restriction — it exists because both object tables are a genuinely finite, shared kernel-mode resource. A program with a bug that creates a new brush, pen, or window on every redraw without ever releasing the old one (a GDI leak or USER handle leak) will, over enough hours or days of uptime, run into that quota. The visible symptom is exactly the mysterious kind of failure this causes real trouble in production: menus that silently stop appearing, windows that fail to paint correctly, or CreateWindow calls that start failing outright, with nothing in the application's own logic having changed.

Tools like Task Manager's "GDI objects" / "USER objects" columns, or Process Explorer's handle view, exist specifically to make this normally-invisible resource visible before it becomes a production incident.

A common mistake

GUI misbehavior gets blamed on "the graphics driver" far more often than the evidence supports. In practice, a large share of long-uptime GUI weirdness — controls that stop drawing, windows that won't create, menus that vanish — is a leaked USER or GDI handle slowly consuming a fixed per-process or per-session quota, which is a bug in ordinary application code, not in the video stack underneath it.

Where this connects

  • CSRSS, Win32k, and session UI plumbing covers where the kernel-mode side of USER/GDI object management actually lives, and why it's kernel-mode at all.
  • Pool & heap is the underlying kernel memory allocator that ultimately backs these object tables, the same way it backs every other kind of kernel object.