The internal WND structure

From user mode programs, one always identifies a window by it’s handle, and HWND instance. The Win32 api provides a vast number of API’s that allow you to get and set various properties of a given window, and that is quite sufficient.

However, it’s interesting to see how the system maintains this information internally. It turns out that the internal structure kept by windows NT for each window contains a number of interesting things, and it’s even more interesting to see how NT manipulates it internally.

For the sake of this article, I’ll refer to this internal structure maintained by NT as the WND structure. The WND structure has a layout similar to this:

// internal window class representations
typedef struct CLASSINFO
    DWORD       unk1;
    ATOM        atomWindowType;
    WORD        unk2;
    DWORD       unk3;
    DWORD       unk4;
    DWORD       unk5;
    DWORD       unk6;
    DWORD       unk7;
    DWORD       unk8;
    CLASSINFO*  pSelf;
    DWORD       unk10;
    DWORD       unk11;
    DWORD       dwStyle;
    DWORD       unk13;
    DWORD       unk14;
    DWORD       dwClassBytes;
    HINSTANCE   hInstance;
struct WNDMENU;

// possible flag values for dwFlags
#define WF_ACTIVECAPTION  (0x00000040)
#define WF_ANSI           (0x00080000)

// All pointers that appear in this struct point to memory
// above the 2GB limit, deep into System Address Space.
// However, like the WND structures themselves, all of the
// data pointed to by them can be found in the Process
// address space by substracting from them the same amount
// we use to find this WND*
typedef struct WND {
      HWND        hWnd;
      ULONG       unk1;
      ULONG       unk2;
      ULONG       unk3;
      WND*        pSelf;         // pointer to self 
      DWORD       dwFlags;
      ULONG       unk6;
      DWORD       dwStyleEx;
      DWORD       dwStyle;
      HINSTANCE   hInstance;
      ULONG       unk10;
      WND*        pNextWnd;      // pointer to "next" window 
      WND*        pParentWnd;    // pointer to parent wnd. For top-level, this points to the desktop
      WND*        pFirstChild;   // pointer to first child window 
      WND*        pOwnerWnd;     // pointer to owner window 
      RECT        rcWnd;         // in screen coordinates
      RECT        rcClient;      // in screen coordinates
      WNDPROC*    pWndProc;      // for system classes, this can be > 2GB
      CLASSINFO*  pWndClass;     // pointer to internal window class ???
      ULONG       unk25;
      ULONG       unk26;
      ULONG       unk27;
      ULONG       unk28;
      union {
         DWORD       dwWndID;    // control ID (lower 16-bits)
         WNDMENU*    pMenu;      // pointer to internal menu representation
      } id;
      ULONG       unk30;
      ULONG       unk31;
      ULONG       unk32;
      WCHAR*      pText;         // pointer to window caption
      DWORD       dwWndBytes;    // number of window bytes reserved in wlWndExtra
      ULONG       unk35;         // for top level windows, this is pSelf again....???
      ULONG       unk36;
      ULONG       wlUserData;
      ULONG       wlWndExtra[1];

Matt Pietrek touched briefly the WND structure in his November 199? "Under the Hood" Column in MSJ, but didn't give out too much detail of the structure itself. However, he did something perhaps more interesting: he told us how to the system retrieves a pointer to this structure when needed.

As it turns out, the internal data of the windows is kept by the kernel mode components of the Win32 subsystem, which helps protect the internal data from wrecked user mode components. However, the kernel maps this memory area to a read-only shared memory section maintained by USER32.DLL in the user mode accesible address space of each process. This allows many APIs to avoid switching into kernel mode to retrieve data, like GetWindowRect(), which helps improve windowing performance.

Inside USER32.DLL, the API's retrieve a WND* from an HWND by calling the @ValidateHwnd() function or one of its variants (like @ValidateHwndNoRip()), which in turn, rely on @HMValidateHandle(). Unfortunately, you can't call this undocumented function from your own apps, since it isn't exported by USER32.DLL.

Getting the pointer yourself is not hard with the help from a little assembly code, but it requires that you know where USER32.DLL keeps the window list in it's shared memory section (pointed to by _gSharedInfo). You can calculate the offset into this list from the HWND by extracting the lower 2 bytes out of the HWND and multiplying it by 12.


Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>