/// <summary>
        /// ETW trace sessions survive process shutdown. Thus you can attach to existing active sessions.
        /// GetActiveSessionNames() returns a list of currently existing session names.  These can be passed
        /// to the code:TraceEventSession constructor to control it.
        /// </summary>
        /// <returns>A enumeration of strings, each of which is a name of a session</returns>
        public unsafe static IEnumerable <string> GetActiveSessionNames()
        {
            const int MAX_SESSIONS     = 64;
            int       sizeOfProperties = sizeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES) +
                                         sizeof(char) * MaxNameSize + // For log moduleFile name
                                         sizeof(char) * MaxNameSize;  // For session name

            byte *sessionsArray = stackalloc byte[MAX_SESSIONS * sizeOfProperties];

            TraceEventNativeMethods.EVENT_TRACE_PROPERTIES **propetiesArray = stackalloc TraceEventNativeMethods.EVENT_TRACE_PROPERTIES *[MAX_SESSIONS];

            for (int i = 0; i < MAX_SESSIONS; i++)
            {
                TraceEventNativeMethods.EVENT_TRACE_PROPERTIES *properties = (TraceEventNativeMethods.EVENT_TRACE_PROPERTIES *) & sessionsArray[sizeOfProperties * i];
                properties->Wnode.BufferSize  = (uint)sizeOfProperties;
                properties->LoggerNameOffset  = (uint)sizeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES);
                properties->LogFileNameOffset = (uint)sizeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES) + sizeof(char) * MaxNameSize;
                propetiesArray[i]             = properties;
            }
            int sessionCount = 0;
            int hr           = TraceEventNativeMethods.QueryAllTraces((IntPtr)propetiesArray, MAX_SESSIONS, ref sessionCount);

            Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr));

            List <string> activeTraceNames = new List <string>();

            for (int i = 0; i < sessionCount; i++)
            {
                byte * propertiesBlob = (byte *)propetiesArray[i];
                string sessionName    = new string((char *)(&propertiesBlob[propetiesArray[i]->LoggerNameOffset]));
                activeTraceNames.Add(sessionName);
            }
            return(activeTraceNames);
        }
        /// <summary>
        /// Open an existing Windows Event Tracing Session, with name 'sessionName'.
        ///
        /// If you are opening a new session use TraceEventSession(string, string).
        ///
        /// To support the illusion that you can have a session with both kernel and user events,
        /// TraceEventSession might start up both a kernel and a user session.   When you want to 'attach'
        /// to such a combined session, the constructor needs to know if you want to control the kernel
        /// session or not.  If attachKernelSession is true, then it opens both sessions (and thus 'Close'
        /// will operation on both sessions.
        /// </summary>
        public TraceEventSession(string sessionName, bool attachKernelSession)
        {
            Init(sessionName);
            int hr = TraceEventNativeMethods.ControlTrace(0UL, sessionName, ToUnmanagedBuffer(properties, null), TraceEventNativeMethods.EVENT_TRACE_CONTROL_QUERY);

            Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr));
            isActive   = true;
            properties = (TraceEventNativeMethods.EVENT_TRACE_PROPERTIES)Marshal.PtrToStructure(unmanagedPropertiesBuffer, typeof(TraceEventNativeMethods.EVENT_TRACE_PROPERTIES));
            if (properties.LogFileNameOffset != 0)
            {
                fileName = Marshal.PtrToStringUni((IntPtr)(unmanagedPropertiesBuffer.ToInt64() + properties.LogFileNameOffset));
            }

            if (attachKernelSession)
            {
                bool success = false;
                try
                {
                    kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName);
                    success       = true;
                }
                finally
                {
                    if (!success)
                    {
                        Stop();
                    }
                }
            }
        }
        private unsafe bool EnableProvider(Guid providerGuid, TraceEventLevel providerLevel, ulong matchAnyKeywords, ulong matchAllKeywords, int providerDataType, byte[] providerData, int providerDataSize)
        {
            bool ret = InsureSession();

            TraceEventNativeMethods.EVENT_FILTER_DESCRIPTOR *dataDescrPtr = null;
            fixed(byte *providerDataPtr = providerData)
            {
                string regKeyName = @"Software\Microsoft\Windows\CurrentVersion\Winevt\Publishers\{" + providerGuid + "}";

                byte[] registryData = null;
                if (providerData != null || providerDataType != 0)
                {
                    TraceEventNativeMethods.EVENT_FILTER_DESCRIPTOR dataDescr = new TraceEventNativeMethods.EVENT_FILTER_DESCRIPTOR();
                    dataDescr.Ptr  = null;
                    dataDescr.Size = providerDataSize;
                    dataDescr.Type = providerDataType;
                    dataDescrPtr   = &dataDescr;

                    if (providerData == null)
                    {
                        providerData = new byte[0];
                    }
                    else
                    {
                        dataDescr.Ptr = providerDataPtr;
                    }

                    // Set the registry key so providers get the information even if they are not active now
                    registryData    = new byte[providerDataSize + 4];
                    registryData[0] = (byte)(providerDataType);
                    registryData[1] = (byte)(providerDataType >> 8);
                    registryData[2] = (byte)(providerDataType >> 16);
                    registryData[3] = (byte)(providerDataType >> 24);
                    Array.Copy(providerData, 0, registryData, 4, providerDataSize);
                }
                SetOrDelete(regKeyName, "ControllerData", registryData);
                int hr;

                try
                {
                    hr = TraceEventNativeMethods.EnableTraceEx(ref providerGuid, null, sessionHandle,
                                                               1, (byte)providerLevel, matchAnyKeywords, matchAllKeywords, 0, dataDescrPtr);
                }
                catch (EntryPointNotFoundException)
                {
                    // Try with the old pre-vista API
                    hr = TraceEventNativeMethods.EnableTrace(1, (int)matchAnyKeywords, (int)providerLevel, ref providerGuid, sessionHandle);
                }
                Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr));
            }

            isActive = true;
            return(ret);
        }
        /// <summary>
        /// Actually starts the trace, with added logic to retry if the session already exists.
        /// </summary>
        private bool StartTrace()
        {
            bool ret   = false;
            int  dwErr = TraceEventNativeMethods.StartTrace(out sessionHandle, sessionName, ToUnmanagedBuffer(properties, fileName));

            if (dwErr == 0xB7) // STIERR_HANDLEEXISTS
            {
                ret = true;
                Stop();
                Thread.Sleep(100);  // Give it some time to stop.
                dwErr = TraceEventNativeMethods.StartTrace(out sessionHandle, sessionName, ToUnmanagedBuffer(properties, fileName));
            }
            if (dwErr == 5 && Environment.OSVersion.Version.Major > 5)      // On Vista and we get a 'Accessed Denied' message
            {
                throw new UnauthorizedAccessException("Error Starting ETW:  Access Denied (Administrator rights required to start ETW)");
            }

            Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(dwErr));
            return(ret);
        }
        /// <summary>
        /// Once started, event sessions will persist even after the process that created them dies. They are
        /// only stoped by this explicit Stop() API.  If you used both kernel and user events, consider
        /// using the code:StopUserAndKernelSession API instead.
        /// </summary>
        public void Stop()
        {
            if (stopped)
            {
                return;
            }
            stopped = true;
            int hr = TraceEventNativeMethods.ControlTrace(0UL, sessionName,
                                                          ToUnmanagedBuffer(properties, null), TraceEventNativeMethods.EVENT_TRACE_CONTROL_STOP);


            if (hr != 4201)     // Instance name not found.  This means we did not start
            {
                Marshal.ThrowExceptionForHR(TraceEventNativeMethods.GetHRFromWin32(hr));
            }

            if (kernelSession != null)
            {
                kernelSession.Stop();
                kernelSession = null;
            }
        }