WND

The WND (formally tagWND) is the primary representation of a window. It is in some ways a very important structure, certainly to access quickly, and especially for USER32 to read directly in user mode.

Documentation Status

The WND is not documented. Though symbol files for WIN32K.SYS in Windows 8 and higher name the WND in the C++ decorations of internal routines, type information for the structure is present in symbol files for Windows 7 only—not before and not since.

That said, many of Microsoft’s names for both the WND and its members were long known in public from the plain-text output of meta-commands that are implemented in debugger extensions that Microsoft supplied with its debuggers in various kits for both kernel-mode and user-mode programming. See, for instance, USEREXTS.DLL from as far back as the Device Driver Kit (DDK) for Windows NT 3.51. Though the WND structure is not formally documented, Microsoft certainly has understood that knowledge of it may help programmers in the depths of debugging what they’re doing with Windows!

Variability

Perhaps not surprisingly, given its need to be accessible from both kernel-mode and user-mode components, the WND does not vary nearly as much as do other undocumented structures. Almost all of the members that are the most useful to know in practice, e.g., while debugging, have been stable since version 5.1. No member has shifted position since version 6.0. Additions since version 6.2 have all been made by appending to the structure. The following changes of size are known:

Version Size (x86) Size (x64)
3.10 0x9C  
3.51 0xA8  
4.0 0xB0  
5.0 0x98  
5.1 to 5.2 0xA0 0x0108
6.0 to 6.1 0xB0 0x0128
6.2 0xC8 0x0150
6.3 0xD8 0x0170
10.0 0xE0 0x0178

Layout

It is well known that the WND is not the whole of its size. Each WND is followed in its allocation by a number of “extra” bytes that is specified as cbWndExtra in the WNDCLASS or WNDCLASSEX when registering the window class that the window is created from.  

Offset (x86) Offset (x64) Definition Versions Remarks
0x00 0x00
HEAD head;
3.10 only  
THROBJHEAD head;
3.51 to 4.0  
THRDESKHEAD head;
5.0 and higher  
0x0C (3.10);
0x10 (3.51)
  unknown heap handle 3.10 to 3.51  
0x14 0x28
WW;
5.0 and higher previously at 0x90
0x10 (3.10);
0x14 (3.51 to 4.0);
0x2C
0x48
WND *spwndNext;
all  
0x30 0x50
WND *spwndPrev;
5.1 and higher  
0x14 (3.10);
0x18 (3.51 to 4.0);
0x30 (5.0);
0x34
0x58
WND *spwndParent;
all  
0x18 (3.10);
0x1C (3.51 to 4.0);
0x34 (5.0);
0x38
0x60
WND *spwndChild;
all  
0x1C (3.10);
0x20 (3.51 to 4.0);
0x38 (5.0);
0x3C
0x68
WND *spwndOwner;
all  
0x20 (3.10)   unaccounted 3.10 only  
0x24 (3.10 to 3.51)  
DESKTOP *spdeskParent;
3.10 to 3.51 next as rpdesk in head
0x28 (3.51);
0x24 (4.0)
 
WND *pwo;
3.51 to 4.0 next as SysWNDO property
0x28 (3.10);
0x2C (3.51);
0x28 (4.0);
0x3C (5.0);
0x40
0x70
RECT rcWindow;
all  
0x38 (3.10);
0x3C (3.51);
0x38 (4.0);
0x4C (5.0);
0x50
0x80
RECT rcClient;
all  
0x48 (3.10);
0x4C (3.51);
0x48 (4.0);
0x5C (5.0);
0x60
0x90
WNDPROC lpfnWndProc;
all  
0x4C (3.10);
0x50 (3.51);
0x4C (4.0);
0x60 (5.0);
0x64
0x98
CLS *pcls;
all  
0x54 (3.51);
0x50 (4.0);
 
INT cbwndExtra;
3.51 to 4.0 next at 0x88
0x50 (3.10);
0x58 (3.51);
0x54 (4.0);
0x64 (5.0);
0x68
0xA0
HRGN hrgnUpdate;
all  
0x54 (3.10);
0x5C (3.51);
0x58 (4.0)
 
WND *spwndLastActive;
3.10 to 4.0 next at 0x8C
0x58 (3.10);
0x60 (3.51);
0x5C (4.0);
0x68 (5.0);
0x6C
0xA8
PROPLIST *ppropList;
all  
0x5C (3.10);
0x64 (3.51);
0x60 (4.0);
0x6C (5.0);
0x70
0xB0
SBINFO *pSBInfo;
all  
0x60 (3.10);
0x68 (3.51);
0x64 (4.0);
0x70 (5.0);
0x74
0xB8
MENU *spmenuSys;
all  
0x64 (3.10);
0x6C (3.51);
0x68 (4.0);
0x74 (5.0);
0x78
0xC0
MENU *spmenu;
all  
0x78 (5.0);
0x7C
0xC8 (6.1)
HRGN hrgnClip;
5.0 and higher previously at 0x88
0x80 0xD0 (6.1)
HRGN hrgnNewFrame;
6.0 and higher  
0x68 (3.10);
0x70 (3.51);
0x6C (4.0);
0x7C (5.0);
0x80 (5.1 to 5.2);
0x84
0xD8 (6.1) unknown PWSTR 3.10 to 3.51  
LARGE_UNICODE_STRING strName;
4.0 and higher  
0x6C (3.10);
0x74 (3.51);
0x78 (4.0)
 
CHAR bFullScreen;
3.10 to 4.0 next as FullScreenMode in state2
0x6D (3.10);
0x75 (3.51);
0x79 (4.0)
 
CHAR cDC;
3.10 to 4.0  
0x6E (3.10);
0x76 (3.51);
0x7A (4.0)
 
USHORT fnid;
3.10 to 4.0 next at 0x16 in WW
0x70 (3.10);
0x78 (3.51);
0x7C (4.0)
 
DWORD dwExpWinVer;
3.10 to 4.0  
0x74 (3.10);
0x7C (3.51);
0x80 (4.0)
 
DWORD dwUserData;
3.10 to 4.0 next at 0x94
0x78 (3.10);
0x80 (3.51);
0x84 (4.0)
 
HDC hdcOwn;
3.10 to 4.0  
0x84 (3.51);
0x88 (4.0)
 
HRGN hrgnClip;
3.51 to 4.0 next at 0x78
0x8C (4.0)  
INT iHungRedraw;
4.0 only  
0x7C (3.10);
0x88 (3.51);
0x90 (4.0)
 
WW;
3.10 to 4.0 next at 0x14;
last member in 3.10 to 4.0
0x88 (5.0);
0x8C (5.1 to 5.2);
0x90
0xE0 (5.2);
0xE8
INT cbwndExtra;
5.0 and higher previously at 0x50
0x8C (5.0);
0x90 (5.1 to 5.2);
0x94
0xE8 (5.2);
0xF0
WND *spwndLastActive;
5.0 and higher previously at 0x58
0x90 (5.0);
0x94 (5.1 to 5.2);
0x98
0xF0 (5.2);
0xF8
HIMC hImc;
5.0 and higher  
0x94 (5.0);
0x98 (5.1 to 5.2);
0x9C
0xF8 (5.2);
0x0100
DWORD dwUserData;
5.0 and higher previously at 0x80;
last member in 5.0
0x9C (5.1 to 5.2);
0xA0
0x0100 (5.2);
0x0108
ACTIVATION_CONTEXT *pActCtx;
5.1 and higher last member in 5.1 to 5.2
0xA4 0x0110
D3DMATRIX *pTransform;
6.0 and higher  
0xA8 0x0118
WND *spwndClipboardListenerNext;
6.0 and higher  
0xAC 0x0120
union {
    ULONG ExStyle2;
    /* bit fields, follow link */
};
6.0 and higher last member in 6.0 to 6.1

Where its !dw command describes what symbol files later name as pSBInfo, the USEREXTS debugger extension for Windows NT 3.51 uses the name rgwScroll, but what’s pointed to is what’s later known as an SBINFO even if it and its members once had other names.

The spmenu member does a double duty that is not at all suggested by its name but is no surprise from the documented interpretation of the CreateWindow function’s hMenu argument. For a child window, spmenu is not a pointer to a MENU but is instead the window’s ID such as returned by the GetWindowLong function when given the index GWL_ID (-12).

Whatever may have been intended by its Hungarian prefix, the bFullScreen member is not a boolean even in version 3.10. As the FullScreenMode bit field in union with state2 for version 5.0 and higher, it is 3 bits wide.

Appended Since Windows 7

Microsoft’s names for members that have been added to the WND since Windows 7 may never be known. On the plus side, if only for neatness of presentation, new members have been added simply by appending. 

Offset (x86) Offset (x64) Definition Versions Remarks
0xB0 0x0128 unaccounted 6.2 and higher  
0xB4 0x0130 unknown dword 6.2 and higher  
0xB8 0x0134 unknown dword (version number) 6.2 and higher  
0xBC 0x0138 unknown THREADINFO pointer 6.2 and higher  
0xC0 0x0140 unknown dword (bit fields) 6.2 and higher  
0xC4 0x0148 unknown pointer 6.2 and higher last member in 6.2
0xC8 0x0150 unknown INPUTTRANSFORMLIST pointer 6.3 and higher  
0xCC 0x0158 unknown dword 6.3 and higher  
0xD0 0x0160 unknown HMONITOR 6.3 and higher  
0xD4 0x0168 unknown word 6.3 and higher last member in 6.3
0xD8 0x016C unknown dword 10.0 and higher  
0xDC 0x0170 unknown dword (bit fields) 10.0 and higher last member in 10.0

The pointer at offsets 0xC4 and 0x0148 is accessible through the GetWindowLongPtr and SetWindowLongPtr functions, using -2 as the index.

The word at offsets 0xD4 and 0x0168 is the window’s DPI scaling, such as posted in both the low and high 16 bits of the wParam for a WM_DPICHANGED (0x02E0) message.

WW

While the WW is thought to be nested in no other structure, it may as well be presented here. It is nowadays 0x18 or 0x20 bytes in 32-bit and 64-bit Windows, respectively, but is 0x20 bytes before version 5.0.

That said, even if the WW never exists except in a WND, or when copied from one, it evidently is intended to be visible in its own right. In all Windows versions, the address of the nested WW is returned by the GetWindowLong function when given the index -1 (which is conspicuously not among the documented inputs). That the structure is named WW and does not originally begin with the bit fields is obscure. The name seems to be public only in the output of the !dso debugger commands as implemented by the USEREXTS and USERKDX debugger extensions from the DDKs for Windows NT 4.0 and Windows 2000.

Offset (x86) Offset (x64) Definition Versions Remarks
0x00 (3.10 to 4.0)   unaccounted 3.10 to 4.0  
0x10 (3.10 to 3.51);
0x0C (4.0);
0x00
0x00
union {
    ULONG state;
    /* bit fields, follow link */
};
all  
0x10 (4.0);
0x04
0x04
union {
    ULONG state2;
    /* bit fields, follow link */
};
4.0 and higher  
0x14 (3.10 to 4.0);
0x08
0x08
union {
    ULONG ExStyle;
    /* bit fields, follow link */
};
all  
0x18 (3.10 to 4.0);
0x0C
0x0C
union {
    ULONG style;
    /* bit fields, follow link */
};
all  
0x1C (3.10 to 4.0);
0x10
0x10
PVOID hModule;
all  
0x14 0x18
USHORT hMod16;
5.0 and higher  
0x16 0x1A
USHORT fnid;
5.0 and higher previously at 0x7A in WND

That some sort of substructure wraps the state, state2 (in applicable versions), ExStyle and style has long been a supportable proposition. Right from the start, WINSRV and then WIN32K has code for accessing these members’ bits by a 16-bit encoding whose high and low bytes are respectively a byte offset from the first member and a byte-wide bit mask. The only known instance of this encoding in WINSRV version 3.10 is 0x0320 for the state bit that symbol files later name bAnsiCreator. Version 3.51, however, has 0x0A10 and 0x0A20 for bits in the style, which puts beyond doubt that these members make a tightly integrated set. Very likely, given the optimisation capability of Microsoft’s compiler even in the early 90s, most of this 16-bit encoding’s use in all versions is more readily visible in the source code than in the binary. By version 4.0, the encoding can be seen more in USER32 than in WIN32K: changes to these flags by USER32 require a transition to WIN32K with the encoding passed as an argument, not interpreted at compile-time and optimised away.

For the output of its !dw command, the USEREXTS debugger extension from the DDK for Windows NT 3.51 has dwExStyle for the extended window styles that symbol files and later versions of that debugger extension present simply as ExStyle. It is here thought that the difference is more of how the debugger extension’s programmer thought of the member, or thought it would better be presented to programmers, than of how it was named in Microsoft’s source code.