Receive Reply Data Block

When given 0x13 as its FunctionCode argument, the NtTraceControl function receives a data block of a sort that expects a reply. Microsoft’s name for this function code is not known. This note deals only with the function’s behaviour that is specific to this function code. The function’s general behaviour is here taken as assumed knowledge.


Microsoft presumably has a name for the eight-byte structure that is expected as the function’s input, but no such name is known. It is perhaps both too simple and too awkward:

Offset Interpretation Versions
0x00 unknown 64-bit container for HANDLE to reply object 6.0 only
unknown four bytes as handle to reply object 6.1 and higher
0x04 unknown ULONG as timeout in milliseconds 6.1 and higher

To be clear: the preceding applies to both 32-bit and 64-bit Windows. It seems the expected input was originally an 8-byte allowance for a HANDLE, using just the first four bytes in 32-bit Windows but all eight in 64-bit, but then the timeout was squeezed in. Quite why this was done without changing the expected size of input is not known. A consequence is that 64-bit Windows 7 and higher have only four bytes for the handle.

The output on success will be the received data block. It begins with an ETW_NOTIFICATION_HEADER and may continue arbitrarily. The total size, in bytes, is in the header as the NotificationSize.

Parameter Validation

The function returns STATUS_INVALID_PARAMETER if either of the following is true:

No reason is yet known that version 6.1 drops the minimum requirement for output. As noted below, if a reply is available for receipt but the output buffer is too small, then in all versions the reply is lost.

Reply Object

The input handle must be to an EtwRegistration object and allow WMIGUID_NOTIFICATION permission to its user-mode caller. Failure to reference the object is fatal to the function. The object is an ETW_REG_ENTRY but specifically of a sort that was created as a reply object. If the referenced object is not the right sort, with DbgReplyRegistration set among its Flags, the function returns STATUS_INVALID_HANDLE.

The reply object will have been created as a specialised registration when a notification was sent to all or some of the user-mode registrations of some event provider and the notification requested a reply.

The most distinctive property of a reply registration is that it has a RepyQueue. This is an ETW_REPLY_QUEUE that collects all the replies into a KQUEUE. The function retrieves one reply from the queue. This retrieval is what the timeout in the input is for. Version 6.0 hard-codes the timeout as 60 seconds. The configurable timeout in later versions has no special interpretations: in effect, zero means not to wait at all, but INFINITE means to wait approximately 49 days. If no reply is available, the function returns STATUS_TIMEOUT or STATUS_USER_APC.

Data Block

Each reply is an ETW_QUEUE_ENTRY from which to obtain the data block that is the reply as far as the caller will ever know. The output buffer receives a copy of the whole data block, both fixed-size header and any variable-size continuation. The total size is in the header, as the NotificationSize and is separately reported to the caller in the variable whose address was given as the ReturnSize argument.

If the output buffer is not large enough for this copy, the function returns STATUS_BUFFER_TOO_SMALL. See that this is an error. There is no recovery from it. The data block is not restored to the queue: the reply is lost. The function’s caller is, however, told how much was lost: nothing is put in the output buffer but the NotificationSize is reported through the ReturnSize argument.

Implementation Detail

The data block as it existed in the queue in kernel mode is treated as beginning with an ETWP_NOTIFICATION_HEADER. This is broadly compatible with the ETW_NOTIFICATION_HEADER that is here thought to be what the caller is intended to see as beginning the user-mode copy. The only difference that matters to this function is that what the public structure provides for an Offset from one header to the next in a sequence of replies is a RefCount in the private structure.

The caller who cares to look can see what was the RefCount at the time of the copy and the ReplyHandle that was used to send the reply. Of course, the expected caller doesn’t look. The expected caller is the NTDLL function EtwSendNotification, which both sends a notification and receives the replies.