Geoff Chappell, Software Analyst
With this function, an event provider creates a locally unique identifier for one instance of an event. This allows that occurrences of events can refer to one another. The typical use is through the EtwTraceEventInstance function to do either or both of tagging the newly written event and linking it to some (previously tagged) parent event. The event provider needs to have defined one or more classes of event that it will ever write with any such identifier. These classes are defined when the provider registers through the EtwRegisterTraceGuids function. What the classes represent is entirely up to the provider. Identifiers are maintained separately for the different classes, such that the full identifier for one instance of an event is its class plus the identifier that is created by this function. The class is common to all occurrences of the same event (and typically of others). The identifier differs for each occurrence of any event in the class (while the provider remains registered).
ULONG EtwCreateTraceInstanceId ( HANDLE RegHandle, EVENT_INSTANCE_INFO *InstInfo);
The required RegHandle argument represents a class of event. It is not truly a handle but is instead a pointer to an opaque structure that will have been created while the event provider was registered. The event provider will have given EtwRegisterTraceGuids a TRACE_GUID_REGISTRATION structure whose Guid member addresses a GUID for the event class. Successful registration will have filled in this structure’s RegHandle member with a pointer that represents the class and which can now be presented to this function as the RegHandle.
The required InstInfo argument is on input the address of a variable that will on output contain a unique identifier that the caller may then use for one instance of an event from the given class.
The function returns zero for success, else a Win32 error code (which the function also sets as the thread’s last error).
The EtwCreateTraceInstanceId function is exported by name from NTDLL in version 5.2 and higher. It has higher-level availability as a forward from the ADVAPI32 export CreateTraceInstanceId in its versions 5.2 and higher.
This note is concerned only with the function as implemented in NTDLL version 5.2 and higher. The earlier implementations in ADVAPI32 versions 5.0 and 5.1 are left for separate treatment some other time.
The EtwCreateTraceInstanceId function is not documented. Well-behaved user-mode software would call the documented CreateTraceInstanceId function instead (though a strict reading of Microsoft’s documentation prohibits calling the higher-level function, but not the lower-level, from a DllMain routine).
Both arguments are required. If either is NULL, the function returns ERROR_INVALID_PARAMETER.
Perhaps only as a sanity check, the opaque structure that RegHandle would otherwise just be assumed to point to retains the 32-bit process ID from when the provider registered. If the supposed structure does not have the current process ID at the expected place, the function returns ERROR_INVALID_PARAMETER.
The essence of the function is that the opaque structure contains an ever-advancing counter from which to read the next instance identifer for the class. Each call to the function increments the counter, though with some provision for skipping zero should the counter ever wrap around. In the EVENT_INSTANCE_INFO structure for the function’s output, the RegHandle is set to whatever was given as RegHandle and the InstanceId is the newly incremented counter.
See that if the provider is unregistered and re-registered (and repeats its class definitions), then the instance identifier for each class restarts at 1. Even putting aside the (unlikely) creation of over 4 billion identifiers until they repeat for any one registration, they are not unique between registrations. If references from one event to another in the same ETL file are not to get confused, there is a strong suggestion that the intended practice is to have a separate tracing session, e.g., writing to a separate ETL file, for each registration.
Given the function’s success, the contents of the EVENT_INSTANCE_INFO are usefully passed to one invocation of EtwTraceEventInstance through its pInstInfo argument so that the event it writes has this new identifer, and may then be passed to any number of subsequent invocations through the pParentInstInfo argument so that all these subsequently traced events link to the earlier one as their parent. In all such use, EtwTraceEventInstance will assume that the RegHandle member still points to the opaque structure, specifically so that the class’s GUID can be recorded in the event. The contents of the EVENT_INSTANCE_INFO as prepared by EtwCreateTraceInstanceId become unsafe to use the moment that the provider is unregistered.