Geoff Chappell, Software Analyst
DRAFT: Take more than your usual care.
This function registers one sort or another of event provider.
ULONG EtwNotificationRegister ( LPCGUID Guid, ULONG Type, PETW_NOTIFICATION_CALLBACK Callback, PVOID Context, REGHANDLE *RegHandle);
The required Guid argument is the address of a GUID that represents the provider.
The Type argument specifies the type of notification. Though the argument is formally a ULONG, its supported values are those of the ETW_NOTIFICATION_TYPE enumeration.
The Callback argument is the address of a function that is to be called back in circumstances that are not presently understood. This argument can be NULL to mean that the caller does not want to be called back.
The Context argument is an arbitrary caller-defined value that is to be passed back to the caller as one of the arguments of the callback function. This argument is meaningful only if a callback function is supplied.
The required RegHandle argument is the address of a 64-bit variable that is to receive a registration handle to the registered provider.
The function returns zero for success, else a Win32 error code (which the function also sets as the thread’s last error).
The callback function has the prototype
typedef ULONG (*PETW_NOTIFICATION_CALLBACK) ( ETW_NOTIFICATION_HEADER *NotificationHeader, PVOID Context);
The EtwNotificationRegister function is exported by name from NTDLL in version 6.0 and higher.
As with many NTDLL exports, Microsoft does not document EtwNotificationRegister. Unlike many, no higher-level function corresponds roughly to it.
Microsoft has, however, published a C-language declaration in NTETW.H from the Enterprise edition of the Windows Driver Kit (WDK) for Windows 10 version 1511. This article reproduces Microsoft’s names for the function’s arguments. Some closely related names are known from public symbol files. For instance, the name ETW_NOTIFICATION_HEADER is known from as far back as Windows Vista because two modules that call this function supply callback routines that are written in C++ and their symbol files show the type in these routines’ decorated names.
Without a Guid for input and a RegHandle for output, the function can do nothing. If either is NULL, the function returns ERROR_INVALID_PARAMETER.
The function saves its inputs into a registration entry that becomes the event provider’s user-mode representation. If the function cannot create a registration entry, it returns ERROR_OUTOFMEMORY. The failure can indeed be caused by insufficient memory but a cause that’s less obviously indicated by this error code is that the creation exceeds a hard-coded limit on how many registrations a process can have at any given time. This limit is 0x0400 before version 6.2 and 0x0800 since. Note that Microsoft’s documentation today, 24th December, 2018, repeats in multiple places, not just for EventRegister and RegisterTraceGuids but in general guidance such as Writing Manifest-based Events, that a “process can register up to 1,024 provider GUIDs”. (Should one wonder why Microsoft doubled a limit that is already orders of magnitude beyond advice that “you should limit the number of providers that your process registers to one or two”?)
Unless the Type is EtwNotificationTypeInProcSession, registration also creates a kernel-mode representation for the event provider and even an Object Manager handle to an EtwRegistration object. Communication with the kernel requires a one-time preparation that is non-trivial and is not done until the process first attempts to register a provider with the kernel. Significant elements include: creating an event for the kernel to signal when a notification is ready for retrieval; creating a thread (or using one from the thread pool) to repeatedly wait for this signal and retrieve notifications; and telling the kernel of the event (through NtTraceControl case 0x1B). Failure at any of this preparation is failure for the function.
The essence of the function’s communication with the kernel to register a provider is NtTraceControl case 0x0F. (Microsoft’s names for the function codes are not all known.)