DISPATCHER_HEADER

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.

Documentation Status

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.

Layout

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.

Original

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.

Nested Unions

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.

Byte 1

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  

Byte 2

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

Byte 3

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  

Union of Structures

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

General Objects

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

Timer Objects

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

Timer2 Objects

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

Queue Objects

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

Thread Objects

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

Mutant Objects

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

Bit Fields

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.

TimerControlFlags

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.

QueueControlFlags

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

Timer2Flags

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

ThreadControlFlags

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)

TimerMiscFlags

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

DebugActive

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)