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 caller-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:

Please bear in mind, however, that the difference between layouts is mostly cosmetic. It is Microsoft’s description in C that changes, not (with a few exceptions) the position and interpretation of the members.

Offset Definition Versions
0x00
/*  varying arrangements, see below  */
all
0x04
LONG SignalState;
all
0x08
LIST_ENTRY WaitListHead;
all

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. Each thread that waits on the same object gets a 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 (3.10 to 5.1)

Before Windows Server 2003, the first four bytes of the DISPATCHER_HEADER are straightforward. In the absence of a DDK for any earlier version than 3.51, it is left to inspection of binaries to see that the first four bytes were originally a 16-bit type and size. That space might be squeezed from these seems to have been anticipated for version 3.51 but was not acted on until version 4.0.

Offset Definition Versions Remarks
0x00
USHORT Type;
3.10 to 3.50  
UCHAR Type;
3.51 to 5.1  
0x01
UCHAR Spare;
3.51 only  
UCHAR Absolute;
4.0 to 5.1  
0x02
USHORT Size;
3.10 to 3.51  
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, before version 4.0, all of them—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, if not to the whole object, without setting the high bit to lock the object—having waited in a spin, if necessary, for the high bit to have 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 its functionality enough to need more space. The boolean Absolute was genuinely new, and brought into use a byte that version 3.51 had explicitly set aside 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 (5.2 to 6.3)

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 to 6.3

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 to 6.3
0x01
UCHAR Absolute;
early 5.2 only
union {
    /*  varying members, see below  */
};
late 5.2 to 6.3
0x02
UCHAR Size;
early 5.2 only
union {
    /*  varying members, see below  */
};
late 5.2 to 6.3
0x03
union {
    /*  varying members, see below  */
};
5.2 to 6.3

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, follow link  */
    };
};
6.1 to 6.3  
UCHAR Abandoned;
6.0 to 6.2 becomes bit in QueueControlFlags
union {
    UCHAR QueueControlFlags;
    struct {
        /*  bit fields, follow link  */
    };
};
6.3 only  
UCHAR Absolute;
late 5.2 to 6.0 becomes bit in TimerControlFlags
UCHAR NpxIrql;
late 5.2 to 6.0  
BOOLEAN Signalling;
6.0 to 6.3  
union {
    UCHAR Timer2Flags;
    struct {
        /*  bit fields, follow link  */
    };
};
6.3 only  

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
UCHAR Size;
late 5.2 to 6.0
union {
    UCHAR ThreadControlFlags;
    struct {
        /*  bit fields, follow link  */
    };
};
6.1 to 6.3
UCHAR Hand;
late 5.2 to 6.3
UCHAR Reserved3;
6.3 only
UCHAR Size;
6.1 to 6.3

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 DebugActive this merely formalised the earlier interpretation in bits—and then for some reason, these bit fields are dropped from the formal definition for x86 builds in 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, follow link  */
    };
};
6.1 to 6.3  
BOOLEAN DebugActive;
5.2 to 6.0 previously at 0x2C in KTHREAD
 union {
    BOOLEAN DebugActive;
    struct {
        /*  bit fields, follow link  */
    };
};
6.1 only (x86);
6.1 to 6.3 (x64)
 
union {
    BOOLEAN DebugActive;
};
6.2 to 6.3 (x86)  
BOOLEAN DpcActive;
6.0 to 6.3  
UCHAR Reserved5;
6.3 only  

Union of Structures (10.0 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

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;
10.0 and higher
0x01
UCHAR Signalling;
10.0 and higher
0x02
UCHAR Size;
10.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, follow link  */
    };
};
10.0 and higher
0x02
UCHAR Hand;
10.0 and higher
0x03
union {
    UCHAR TimerMiscFlags;
    struct {
        /*  bit fields, follow link  */
    };
};
10.0 and higher

Timer2 Objects

Offset Definition Versions
0x00
UCHAR Timer2Type;
10.0 and higher
0x01
union {
    UCHAR Timer2Flags;
    struct {
        /*  bit fields, follow link  */
    };
};
10.0 and higher
0x02
UCHAR Timer2Reserved1;
10.0 to 1607
UCHAR Timer2ComponentId;
1703 and higher
0x03
UCHAR Timer2Reserved2;
10.0 to 1607
UCHAR Timer2RelativeId;
1703 and higher

Queue Objects

Offset Definition Versions
0x00
UCHAR QueueType;
10.0 and higher
0x01
union {
    UCHAR QueueControlFlags;
    struct {
        /*  bit fields, follow link  */
    };
};
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 to 1709
union {
    UCHAR ThreadSpecControl;
    struct {
        UCHAR SpecControlIbrs : 1;
        UCHAR SpecControlStibp : 1;
        UCHAR SpecControlReserved : 6;
    };
};
1803 and higher
0x02
union {
    UCHAR ThreadControlFlags;
    struct {
        /*  bit fields, follow link  */
    };
};
10.0 and higher
0x03
union {
    UCHAR DebugActive;
    struct {
        /*  bit fields, follow link  */
    };
};
10.0 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