Geoff Chappell, Software Analyst
Every kernel object that can be waited on, e.g., by giving its address as the first argument to KeWaitForSingleObject, begins with a DISPATCHER_HEADER. Microsoft’s symbol files for the kernel identify the following structures as beginnning with a DISPATCHER_HEADER:
Through their common beginning, these objects have some common behaviour. When the address of a waitable object is given to KeWaitForSingleObject, the latter does not return until the object gets signalled. Meanwhile, the calling thread is left in an efficient wait state, with the kernel knowing not to waste time on the thread’s execution. How an object gets signalled varies with the type of object. For instance, a KEVENT is signalled by passing its address to KeSetEvent but a KPROCESS is signalled from within the kernel just as a consequence of the corresponding process’s termination.
The DISPATCHER_HEADER is not formally documented but the Device Driver Kit (DDK) from as far back as Windows NT 3.51 supplies a C-language definition in NTDDK.H, and each new DDK or Windows Driver Kit (WDK) has continued to, though the definition soon moved to WDM.H.
Of the objects that build on the DISPATCHER_HEADER, Microsoft publishes C-language definitions for some but not all. The main distinction concerns the allocation of memory for the object. For some types, such as KEVENT, memory for the object can be supplied by its intended user, such as a kernel-mode driver, who then calls a kernel function to initialise the object. Other types, such as KPROCESS, only ever exist in memory that is allocated by the kernel, the intended user of the object having called a kernel function that creates the object. Mostly, the former have C-language definitions in public header files and the latter don’t.
Although its layout is public, the DISPATCHER_HEADER surely is intended to be treated as opaque outside the kernel. The definition seems to have been published only so that in those cases where drivers and other kernel-mode modules create the waitable object they can know how much space to allocate. Yet how much space, and what sort of space, is all they need to know. What happens in the space is entirely in the hands of kernel functions that are provided for initialising and then working with the object. Microsoft might therefore have defined the DISPATCHER_HEADER as an array of bytes, with no consequences for programmers at large except if the size ever changed.
In all versions, the DISPATCHER_HEADER is 0x10 and 0x18 bytes in 32-bit and 64-bit Windows respectively. Constancy of size is not strictly required, but is more or less required for compatbility, given the expectation of opacity in user-supplied memory. The same opacity, however, means that interpretation within the constant size could change completely even between builds, if only in principle.
It happens that the DISPATCHER_HEADER has varied hardly at all, if understood as a common header for all waitable objects. In this sense, the simple structure from the early kits is all that any kernel-mode programmer needs to keep in mind, e.g., to recognise when debugging. In another sense, the layout has changed significantly as the structure has been elaborated here and there for different types of object that have picked up more functionality as Windows evolves even while they’ve been constrained to their original size. This article distinguishes three layouts according to the organisation of the first four bytes:
Offset | Definition | Versions |
---|---|---|
0x00 |
/* varying arrangements, see below */ |
3.51 and higher |
0x04 |
LONG SignalState; |
3.51 and higher |
0x08 |
LIST_ENTRY WaitListHead; |
3.51 and higher |
Though different types of object have different ways of changing the SignalState, what they have in common, excepting some special interpretation for mutant objects, is that the object is regarded as signalled when the SignalState is positive.
Any object can have multiple threads waiting on it simultaneously. To wait on an object, a thread needs a KWAIT_BLOCK structure. This may be, and typically is, one of several such structures that are built-in to the KTHREAD as its WaitBlock array, or it can be provided from outside, notably by callers of KeWaitForMultipleObjects. Threads that wait on the same object get their corresponding wait block appended to the double-linked list that begins with the object’s WaitListHead and links through the wait block’s WaitListEntry member.
Before Windows Server 2003, the first four bytes of the DISPATCHER_HEADER are straightforward:
Offset | Definition | Versions | Remarks |
---|---|---|---|
0x00 |
UCHAR Type; |
3.51 to 5.1 | |
0x01 |
UCHAR Spare; |
3.51 only | |
UCHAR Absolute; |
4.0 to 5.1 | ||
0x02 |
USHORT Size; |
3.51 only | |
UCHAR Size; |
4.0 to 5.1 | ||
0x03 |
UCHAR Inserted; |
4.0 to 5.1 | previously at 0x24 in KTIMER |
Though no DISPATCHER_HEADER definition in any DDK or WDK ever says so, the low 7 bits of the Type—or, in version 3.51, all 8 bits—come from the undocumented KOBJECTS enumeration. Starting with the WDM.H from the WDK for Windows 7, a comment would have it that the Type is “accessible via KOBJECT_TYPE” but the latter, which is presumably a macro, is not defined in any of the WDK headers. Other macros, for testing at run-time that an object is the type it’s expected to be, show that the result of applying KOBJECT_TYPE to an object is some such symbol as GateObject or EventSynchronizationObject. The WDM.H from the DDK for Windows Server 2003 SP1 lets slip that the mask for those low 7 bits of the Type is represented symbolically by KOBJECT_TYPE_MASK.
Though the high bit of the Type seems to have been planned from relatively early to have some other significance, it’s not known to have got used immediately. For several versions, it seems to have got used only for particular types of objects, notably queues, and only then in particular circumstances. By Windows 7, however, it’s in use as a lock for all types of waitable object, with what looks to be the intention that no changes are ever made to an object’s DISPATCH_HEADER without locking the object by setting the high bit—having waited in a spin, if necessary, for the high bit to become clear.
The Absolute and Inserted members are the chronologically first examples of the DISPATCHER_HEADER being specialised for a type of object, in this case the KTIMER when Windows NT 4.0 improved it enough to need more space. The boolean Absolute was genuinely new, and brought into use a byte that had been explicitly left as Spare. The other, Inserted, had been at the end of the KTIMER beyond the header. As a side-effect of alignment, moving its one byte to the header freed four for new use (to allow timers to be periodic). Space for Inserted in the header was found by taking from the Size.
The original 16-bit Size counted bytes. When narrowed to 8 bits for version 4.0, it changed to counting dwords.
Starting with Windows Server 2003 the role of the high bit of the Type as the object’s lock is formalised as one 32-bit Lock laid over the first four bytes so that all four bytes, but especially the high bit of the Type, can be accessed together with one instruction such as cmpxchg or bts that can take the lock prefix (and which anyway can’t operate on single bytes).
Offset | Definition | Versions |
---|---|---|
0x00 |
union { struct { /* varying members, see below */ }; LONG volatile Lock; }; |
5.2 to 6.1 |
union { struct { /* varying members, see below */ }; union { LONG volatile Lock; LONG LockNV; }; }; |
6.2 and higher |
Except for the Type, each of the first four bytes soon becomes a union of different members for different object types:
Offset | Definition | Versions |
---|---|---|
0x00 |
UCHAR Type; |
5.2 and higher |
0x01 |
UCHAR Absolute; |
early 5.2 only |
union { /* varying members, see below */ }; |
late 5.2 and higher | |
0x02 |
UCHAR Size; |
early 5.2 only |
union { UCHAR Size; UCHAR Hand; }; |
late 5.2 to 6.0 | |
union { /* varying members, see below */ }; |
6.1 and higher | |
0x03 |
union { /* varying members, see below */ }; |
5.2 and higher |
Better might have been to define the last three of these bytes as a union of three-byte structures for the different types, but the business of this article is to describe, not prescribe, however messy the description has to be. That said, the definition in WDM.H seems to have got messy enough that Microsoft redid it for Windows 10: you may prefer to skip ahead.
Offset | Definition | Versions | Remarks |
---|---|---|---|
0x01 |
union { UCHAR TimerControlFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher | |
UCHAR Abandoned; |
6.0 to 6.2 | becomes bit in QueueControlFlags | |
union { UCHAR QueueControlFlags; struct { /* bit fields, see below */ }; }; |
6.3 and higher | ||
UCHAR Absolute; |
late 5.2 to 6.0 | becomes bit in TimerControlFlags | |
UCHAR NpxIrql; |
late 5.2 to 6.0 | ||
BOOLEAN Signalling; |
6.0 and higher | ||
union { UCHAR Timer2Flags; struct { /* bit fields, see below */ }; }; |
6.3 and higher |
Windows Server 2003 SP1 made a simple union of what had just been the Size, adding Hand for timer objects—in that order. Windows 7 swapped them, and put them both after a new set of bit fields for thread objects.
Offset | Definition | Versions |
---|---|---|
0x02 |
union { UCHAR ThreadControlFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher |
UCHAR Hand; |
6.1 and higher | |
UCHAR Reserved3; |
6.3 and higher | |
UCHAR Size; |
6.1 and higher |
The last of the first four bytes had been used as Inserted for timer objects since version 4.0. Windows Server 2003 added DebugActive for thread objects, Windows Vista added DpcActive for mutants, and then Windows 7 made bit fields of the first two. For some reason, the bit fields that developed from DebugActive are dropped from the x86 builds for Windows 8 and higher.
Offset | Definition | Versions | Remarks |
---|---|---|---|
0x03 |
UCHAR Inserted; |
5.2 to 6.0 | becomes bit in TimerMiscFlags |
union { UCHAR TimerMiscFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher | ||
BOOLEAN DebugActive; |
5.2 to 6.0 | previously at 0x2C in KTHREAD | |
union { BOOLEAN DebugActive; struct { /* bit fields, see below */ }; }; |
6.1 only (x86); 6.1 and higher (x64) |
||
union { BOOLEAN DebugActive; }; |
6.2 and higher (x86) | ||
BOOLEAN DpcActive; |
6.0 and higher | ||
UCHAR Reserved5; |
6.3 and higher |
Windows 10 reorganises the first four bytes into a union of different four-byte structures for the different object types:
Offset | Definition | Versions |
---|---|---|
0x00 |
union { union { LONG volatile Lock; LONG LockNV; }; /* varying unnamed structures, see below */ }; |
10.0 and higher |
The objects that have no special interpretation are events, processes, semaphores and gates. By the way, event and gate objects are nothing but the DISPATCHER_HEADER.
Offset | Definition | Versions |
---|---|---|
0x00 |
UCHAR Type; |
3.51 and higher |
0x01 |
UCHAR Signalling; |
6.0 and higher |
0x02 |
UCHAR Size; |
4.0 and higher |
0x03 |
UCHAR Reserved1; |
10.0 and higher |
Offset | Definition | Versions |
---|---|---|
0x00 |
UCHAR TimerType; |
10.0 and higher |
0x01 |
union { UCHAR TimerControlFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher |
0x02 |
UCHAR Hand; |
late 5.2 and higher |
0x03 |
union { UCHAR TimerMiscFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher |
Offset | Definition | Versions |
---|---|---|
0x00 |
UCHAR Timer2Type; |
10.0 and higher |
0x01 |
union { UCHAR Timer2Flags; struct { /* bit fields, see below */ }; }; |
10.0 and higher |
0x02 |
UCHAR Timer2Reserved1; |
10.0 and higher |
0x03 |
UCHAR Timer2Reserved2; |
10.0 and higher |
Offset | Definition | Versions |
---|---|---|
0x00 |
UCHAR QueueType; |
10.0 and higher |
0x01 |
union { UCHAR QueueControlFlags; struct { /* bit fields, see below */ }; }; |
10.0 and higher |
0x02 |
UCHAR QueueSize; |
10.0 and higher |
0x03 |
UCHAR QueueReserved; |
10.0 and higher |
Offset | Definition | Versions |
---|---|---|
0x00 |
UCHAR ThreadType; |
10.0 and higher |
0x01 |
UCHAR ThreadReserved; |
10.0 and higher |
0x02 |
union { UCHAR ThreadControlFlags; struct { /* bit fields, see below */ }; }; |
6.1 and higher |
0x03 |
union { UCHAR DebugActive; struct { /* bit fields, see below */ }; }; |
5.2 and higher |
For mutant objects, the DpcActive member was at offset 0x03 in versions 6.0 to 6.2. Windows 10 actually does shift it to offset 0x02 (and the Size to offset 0x01). Whether this was intentional, or is just a typing error in Microsoft’s reorganisation of the surprisingly complex structure that the DISPATCH_HEADER had become, is not known.
Offset | Definition | Versions |
---|---|---|
0x00 |
UCHAR MutantType; |
10.0 and higher |
0x01 |
UCHAR MutantSize; |
10.0 and higher |
0x02 |
BOOLEAN DpcActive; |
10.0 and higher |
0x03 |
UCHAR MutantReserved; |
10.0 and higher |
However the first four bytes are organised in the different versions, several are nowadays interpreted as bit fields, though differently for different types of objects.
The byte at offset 0x01 in objects whose Type is TimerNotificationObject (0x08) or TimerSynchronizationObject (0x09) was originally the single member Absolute, but was broken into bit fields for Windows 7 to accommodate the TolerableDelay argument of the new KeSetCoalescableTimer function without extending the KTIMER.
Mask | Definition | Versions |
---|---|---|
0x01 |
UCHAR Absolute : 1; |
6.1 and higher |
0x02 |
UCHAR Coalescable : 1; |
6.1 only |
UCHAR Wake : 1; |
6.2 and higher | |
0x04 (6.1) |
UCHAR KeepShifting : 1; |
6.1 only |
0xF8 (6.1); 0xFC |
UCHAR EncodedTolerableDelay : 5; |
6.1 only |
UCHAR EncodedTolerableDelay : 6; |
6.2 and higher |
If a timer is set though KeSetCoalescableTimer with a non-zero TolerableDelay, then Coalescable is set. If a non-zero Period is specified too, then KeepShifting is set and the EncodedTolerableDelay is the binary logarithm of the TolerableDelay. Perhaps this was thought too coarse, at least for long tolerances on long periods. For instance, of the documentation’s suggestions, 150 and 250ms both get treated as 128. The encoding changes for Windows 8, to be proportional rather than exponential: specifically, the EncodedTolerableDelay is the TolerableDelay (in milliseconds) multiplied by 10,000 and shifted right by 18 bits, truncated to a maximum of 0x3F.
The byte at offset 0x01 in objects whose Type is QueueObject (0x04) was the single member Abandoned, starting with Windows Vista, until Windows 8.1 broke it into bit fields:
Mask | Definition | Versions |
---|---|---|
0x01 |
UCHAR Abandoned : 1; |
6.3 and higher |
0x02 |
UCHAR DisableIncrement : 1; |
6.3 and higher |
UCHAR QueueReservedControlFlags : 6; |
10.0 and higher |
Windows 8.1 introduced types Timer2NotificationObject (0x18) and Timer2SynchronizationObject (0x19). In the corresponding objects, the byte at offset 0x01 has been bit fields from the beginning:
Mask | Definition | Versions |
---|---|---|
0x01 |
UCHAR Timer2Inserted : 1; |
6.3 and higher |
0x02 |
UCHAR Timer2Expiring : 1; |
6.3 and higher |
0x04 |
UCHAR Timer2CancelPending : 1; |
6.3 and higher |
0x08 |
UCHAR Timer2SetPending : 1; |
6.3 and higher |
0x10 |
UCHAR Timer2Running : 1; |
6.3 and higher |
0x20 |
UCHAR Timer2Disabled : 1; |
6.3 and higher |
UCHAR Reserved1 : 2; |
6.3 only | |
UCHAR Timer2ReservedFlags : 2; |
10.0 and higher |
Objects whose type is ThreadObject (0x06) have bit fields at offset 0x02 in Windows 7 and higher:
Mask | Definition | Versions |
---|---|---|
0x01 |
UCHAR CpuThrottled : 1; |
6.1 only |
0x02 (6.1); 0x01 |
UCHAR CyceProfiling : 1; |
6.1 and higher |
0x04 (6.1); 0x02 |
UCHAR CounterProfiling : 1; |
6.1 and higher |
0x04 |
UCHAR GroupScheduling : 1; |
6.2 and higher |
0x08 |
UCHAR AffinitySet : 1; |
6.2 and higher |
0x10 |
UCHAR Tagged : 1; |
10.0 and higher |
0x20 |
UCHAR EnergyProfiling : 1; |
10.0 and higher |
0x40 |
UCHAR Instrumented : 1; |
10.0 and higher (x86) |
UCHAR Reserved : 5; |
6.1 only | |
UCHAR Reserved : 4; |
6.2 only | |
UCHAR Reserved2 : 4; |
6.3 only | |
UCHAR ThreadReservedControlFlags : 2; |
10.0 and higher (x64) | |
UCHAR ThreadReservedControlFlags : 1; |
10.0 and higher (x86) |
While introducing the TimerControlFlags at offset 0x01 in Timer objects, Windows 7 also made bit fields at offset 0x03, where there had been a boolean Inserted since version 4.0:
Mask (x86) | Mask (x64) | Definition | Versions |
---|---|---|---|
0x01 |
UCHAR Index: 1; |
6.1 and higher | |
0x3F |
UCHAR Index: 6; |
6.1 and higher | |
0x3E |
UCHAR Processor : 5; |
6.1 and higher | |
0x40 |
UCHAR Inserted : 1; |
6.1 and higher | |
0x80 |
UCHAR volatile Expired : 1; |
6.1 and higher |
A boolean DebugActive is defined for Thread objects from as far back as Windows Server 2003. Windows Vista made bit fields of it, though later versions have kept them only for the x64 builds:
Mask | Definition | Versions |
---|---|---|
0x01 |
BOOLEAN ActiveDR7 : 1; |
6.1 only (x86); 6.1 and higher (x64) |
0x02 |
BOOLEAN Instrumented : 1; |
6.1 only; 6.1 and higher (x64) |
0x04 |
BOOLEAN Minimal : 1; |
6.3 and higher (x64) |
BOOLEAN Reserved2 : 4; |
6.1 only; 6.1 to 6.2 (x64) |
|
BOOLEAN Reserved : 3; |
6.3 only (x64) | |
BOOLEAN Reserved4 : 3; |
10.0 and higher (x64) | |
0x40 |
BOOLEAN UmsScheduled : 1; |
6.1 only; 6.1 and higher (x64) |
0x80 |
BOOLEAN UmsPrimary : 1; |
6.1 only; 6.1 and higher (x64) |