The kernel’s core has its own set of objects that it works with independently of the Object Manager. Many kernel objects, but not all, begin with a DISPATCHER_HEADER. Some kernel objects, but not all, are the beginning of some other object that is managed by the Object Manager. Consider, for example, the process object that is referred to when a handle is obtained to a process: it is an EPROCESS which has the kernel’s KPROCESS at its start. All kernel objects begin with a byte or word that signifies the type of object. These signifiers are formally defined as the KOBJECTS enumeration.

Microsoft does not document the KOBJECTS enumeration, nor even declare it in header files from any Windows Driver Kit (WDK). However, Microsoft’s names for it and its values are known from public symbols for the kernel in Windows Vista and higher. Since symbol files for earlier versions do not contain type information for KOBJECTS, what’s known for these earlier versions is instead inferred from inspecting different versions of the kernel for corresponding use. Where close correspondence is found it seems reasonable to suppose continuity. Some use, however, has no correspondence, the code having changed too much. Even where the use hasn’t changed, tracking it down exhaustively would be difficult, if not impossible, even with source code. Beware, then, that the description below is something of a guess before Windows Vista.

Value Name Structure Versions
0x00 EventNotificationObject KEVENT all
0x01 EventSynchronizationObject KEVENT all
0x02 MutantObject KMUTANT all
0x03 MutexObject (proposed) KMUTEX 3.10 only
ProcessObject KPROCESS 3.50 and higher
0x04 QueueObject KQUEUE 3.50 and higher
0x04 (3.10);
SemaphoreObject KSEMAPHORE all
0x05 (3.10);
ThreadObject KTHREAD all
0x07 SpareObject (proposed)   4.0 to early 5.2
GateObject KGATE late 5.2 and higher
0x06 (3.10);
0x07 (3.50 to 3.51);
TimerObject (proposed) KTIMER 3.10 to 3.51
TimerNotificationObject KTIMER 4.0 and higher
0x09 TimerSynchronizationObject KTIMER 4.0 and higher
0x0A Spare2Object   4.0 and higher
0x0B Spare3Object   4.0 and higher
0x0C Spare4Object   4.0 and higher
0x0D Spare5Object   4.0 and higher
0x0E Spare6Object   4.0 and higher
0x0F Spare7Object   4.0 and higher
0x10 Spare8Object   4.0 and higher
0x11 Spare9Object   4.0 to 6.1
ProfileCallbackObject KPROFILE 6.2 and higher
0x07 (3.10);
0x08 (3.50 to 3.51);
ApcObject KAPC all
0x08 (3.10);
0x09 (3.50 to 3.51);
DpcObject KDPC all
0x09 (3.10);
0x0A (3.50 to 3.51);
DeviceQueueObject KDEVICE_QUEUE all
0x0A (3.10);
0x0B (3.50 to 3.51);
EventPairObject KEVENT_PAIR 3.10 to 6.2
PriQueueObject KPRIQUEUE 6.3 and higher
0x0B (3.10);
0x0C (3.50 to 3.51);
InterruptObject KINTERRUPT all
0x0C (3.10) apparently unused   3.10 only
0x0D (3.10 to 3.51) PowerStatusObject (proposed) KPOWER_STATUS (proposed) 3.10 only
apparently unused   3.50 to 3.51
0x0E (3.10 to 3.51) ProcessObject KPROCESS 3.10 only
apparently unused   3.50 to 3.51
0x0F (3.10 to 3.51);
ProfileObject KPROFILE all
0x18 Timer2NotificationObject KTIMER2 6.3 and higher
0x19 Timer2SynchronizationObject KTIMER2 6.3 and higher
0x18 (5.2 to 6.2);
ThreadedDpcObject KDPC 5.2 and higher
0x10 (3.10 to 3.51);
0x18 (4.0 to 5.1);
0x19 (5.2 to 6.2);

The enumeration is broadly in two sets, each arranged very nearly alphabetically: first the types for kernel objects that begin with a DISPATCHER_HEADER; then those that do not. This separation seems to have been an original intention. Conforming to it looks like the one and only reason that ProcessObject got renumbered when version 3.50 reworked the KPROCESS to start with a DISPATCHER_HEADER. The separation persisted until new timer objects for version 6.3 could not be added to the first set as a pair at a multiple of 8 without renumbering the second set.

It is not merely coincidental or neat that EventSynchronizationObject, TimerSynchronizationObject and Timer2SynchronizationObject are each one more than a multiple of 8. Kernel objects that begin with a DISPATCHER_HEADER—let’s call them dispatcher objects—are directly waitable. Their addresses can be passed to the KeWaitForSingleObject and KeWaitForMultipleObjects functions such that the function should not return unless the object or objects (all or any) get signalled. How a dispatcher object gets signalled varies with the object. What’s relevant to the KOBJECTS enumeration is that, starting with version 4.0, if the KOBJECTS for a dispatcher object has 1 for its low 3 bits, then the object is specifically a synchronisation object in contrast to a notification object: when it is signalled and a waiting thread is released from its wait, the object is automatically reset to non-signalled and no more threads are released from waiting until the object is signalled again.

The name MutexObject is proposed for the KOBJECTS value that selects what version 3.10 has as its KMUTEX structure. In this version only, the KMUTEX is a different structure from the KMUTANT.

The name TimerObject is proposed for what is later the TimerNotificationObject. The corresponding structure is the same KTIMER that later versions support in two types. Earlier versions than 4.0 implement the timer only as a notification object. They have no notion of a TimerNotificationObject as separate from a TimerSynchronizationObject, just as they have only a KeInitializeTimer function, not the later KeInitializeTimerEx that can initialise either type.

No later use of any kernel object of type 0x07 is known until Windows Server 2003 SP1 (which is what “late 5.2” means in the table above) introduced the gate object. It seems at least plausible that 0x07 was left as the original SpareObject or Spare1Object as a side-effect of skipping ahead to 0x08 and 0x09 for the new types of timer object so that notification versus synchronisation can be discerned in common for events and timers just from the low 3 bits.

For several of the object types, the corresponding structures are sufficiently internal to the kernel that even their names have never been revealed in public symbol files for the kernel.

The name KEVENT_PAIR for the structure that corresponds to EventPairObject is first known from the output of the !strct command as implemented by the KDEX2X86 debugging extension which Microsoft supplied with the Device Driver Kit (DDK) for Windows NT 4.0. Though obscure now—indeed, for decades—the event pair was once vital to the efficiency of windowing and other functionality in the Win32 subsystem.

The structure that is signified by ProfileObject and (later) ProfileCallbackObject is known with the certainty of type information, just not from public symbols but instead from a statically linked library (named CLFSMGMT.LIB) that Microsoft supplies in the Software Development Kit (SDK) as if for user-mode programming. Even without this, there’d have been no surprise that Microsoft’s name for it is KPROFILE.

What filled the gap between InterruptObject and ProfileObject before version 4.0 is known only partly. The names PowerStatusObject and KPOWER_STATUS are proposed for whatever object is initialised and worked with through the exported functions KeInitializePowerStatus, KeInsertQueuePowerStatus and KeRemoveQueuePowerStatus which exist only in version 3.10. Symbol files show that an internal variable named KiPowerStatusLock persists to version 3.50 and so it seems at least possible that the value for the extinct object type remained defined as a placeholder until the large renumbering for version 4.0.