/// <summary>
        /// #EnableKernelProvider
        /// Enable the kernel provider for the session. If the session name is 'NT Kernel Session' then it
        /// operates on that.   This can be used to manipuate the kernel session.   If the name is not 'NT
        /// Kernel Session' AND it is a moduleFile based session, then it tries to approximate attaching the
        /// kernel session by creating another session logs to [basename].kernel.etl.  There is support in
        /// ETWTraceEventSource for looking for these files automatically, which give a good illusion that
        /// you can have a session that has both kernel and user events turned on.  
        /// <param name="flags">
        /// Specifies the particular kernel events of interest</param>
        /// <param name="stackCapture">
        /// Specifies which events should have their eventToStack traces captured too (VISTA only)</param>
        /// <returns>Returns true if the session had existed before and is now restarted</returns>
        /// </summary>
        public unsafe bool EnableKernelProvider(KernelTraceEventParser.Keywords flags, KernelTraceEventParser.Keywords stackCapture)
        {
            if (sessionName != KernelTraceEventParser.KernelSessionName)
            {
                if (kernelSession != null)
                    throw new Exception("A kernel session is already active.");
                if (string.IsNullOrEmpty(FileName))
                    throw new Exception("Cannot enable kernel events to a real time session unless it is named " + KernelTraceEventParser.KernelSessionName);
                string kernelFileName = System.IO.Path.ChangeExtension(FileName, ".kernel.etl");
                kernelSession = new TraceEventSession(KernelTraceEventParser.KernelSessionName, kernelFileName);
                return kernelSession.EnableKernelProvider(flags, stackCapture);
            }
            if (sessionHandle != TraceEventNativeMethods.INVALID_HANDLE_VALUE)
                throw new Exception("The kernel provider must be enabled as the only provider.");

            properties.Wnode.Guid = KernelTraceEventParser.ProviderGuid;
            if (Environment.OSVersion.Version.Major <= 5)
            {
                // TODO should we fail, or should we silently ignore?
                if (stackCapture != KernelTraceEventParser.Keywords.None)
                    throw new Exception("Stack trace capture only available on Windows 6 (VISTA) and above.");

                KernelTraceEventParser.Keywords vistaOnlyFlags =
                    KernelTraceEventParser.Keywords.ProcessCounters |
                    KernelTraceEventParser.Keywords.ContextSwitch |
                    KernelTraceEventParser.Keywords.Interrupt |
                    KernelTraceEventParser.Keywords.DiskIOInit |
                    KernelTraceEventParser.Keywords.Driver |
                    KernelTraceEventParser.Keywords.Profile |
                    KernelTraceEventParser.Keywords.FileIO |
                    KernelTraceEventParser.Keywords.FileIOInit |
                    KernelTraceEventParser.Keywords.Dispatcher |
                    KernelTraceEventParser.Keywords.VirtualAlloc;
                KernelTraceEventParser.Keywords setVistaFlags = flags & vistaOnlyFlags;
                if (setVistaFlags != KernelTraceEventParser.Keywords.None)
                    throw new Exception("A Kernel Event Flags {" + setVistaFlags.ToString() + "} specified that is not supported on Pre-VISTA OSes.");

                properties.EnableFlags = (uint) flags;
                return StartTrace();
            }

            // Initialize the stack collecting information
            const int stackTracingIdsMax = 96;
            int curID = 0;
            var stackTracingIds = stackalloc TraceEventNativeMethods.STACK_TRACING_EVENT_ID[stackTracingIdsMax];
            #if DEBUG
            // Try setting all flags, if we overflow an assert in SetStackTraceIds will fire.
            SetStackTraceIds((KernelTraceEventParser.Keywords)(-1), stackTracingIds, stackTracingIdsMax);
            #endif
            if (stackCapture != KernelTraceEventParser.Keywords.None)
                curID = SetStackTraceIds(stackCapture, stackTracingIds, stackTracingIdsMax);

            // The Profile event requires the SeSystemProfilePrivilege to succeed, so set it.
            if ((flags & KernelTraceEventParser.Keywords.Profile) != 0)
                TraceEventNativeMethods.SetSystemProfilePrivilege();

            bool ret = false;
            properties.EnableFlags = (uint)flags;

            int dwErr;
            try
            {
                dwErr = TraceEventNativeMethods.StartKernelTrace(out sessionHandle, ToUnmanagedBuffer(properties, fileName), stackTracingIds, curID);
                if (dwErr == 0xB7) // STIERR_HANDLEEXISTS
                {
                    ret = true;
                    Stop();
                    Thread.Sleep(100);  // Give it some time to stop.
                    dwErr = TraceEventNativeMethods.StartKernelTrace(out sessionHandle, ToUnmanagedBuffer(properties, fileName), stackTracingIds, curID);
                }
            }
            catch (BadImageFormatException)
            {
                // We use a small native DLL called KernelTraceControl that needs to be
                // in the same directory as the EXE that used TraceEvent.dll.  Unlike IL
                // Native DLLs are specific to a processor type (32 or 64 bit) so the easiest
                // way to insure this is that the EXE that uses TraceEvent is built for 32 bit
                // and that you use the 32 bit version of KernelTraceControl.dll
                throw new BadImageFormatException("Could not load KernelTraceControl.dll (likely 32-64 bit process mismatch)");
            }
            catch (DllNotFoundException)
            {
                // In order to start kernel session, we need a support DLL called KernelTraceControl.dll
                // This DLL is available by downloading the XPERF.exe tool (see
                // http://msdn.microsoft.com/en-us/performance/cc825801.aspx for instructions)
                // It is recommended that you get the 32 bit version of this (it works on 64 bit machines)
                // and build your EXE that uses TraceEvent to launch as a 32 bit application (This is
                // the default for VS 2010 projects).
                throw new DllNotFoundException("KernelTraceControl.dll missing from distribution.");
            }
            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>
        /// Given a mask of kernel flags, set the array stackTracingIds of size stackTracingIdsMax to match.
        /// It returns the number of entries in stackTracingIds that were filled in.
        /// </summary>
        private unsafe int SetStackTraceIds(KernelTraceEventParser.Keywords stackCapture, TraceEventNativeMethods.STACK_TRACING_EVENT_ID* stackTracingIds, int stackTracingIdsMax)
        {
            int curID = 0;

            // PerfInfo (sample profiling)
            if ((stackCapture & KernelTraceEventParser.Keywords.Profile) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PerfInfoTaskGuid;
                stackTracingIds[curID].Type = 0x2e;     // Sample Profile
                curID++;
            }

            if ((stackCapture & KernelTraceEventParser.Keywords.SystemCall) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PerfInfoTaskGuid;
                stackTracingIds[curID].Type = 0x33;     // SysCall
                curID++;
            }
            // TODO SysCall?

            // Thread
            if ((stackCapture & KernelTraceEventParser.Keywords.Thread) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.ThreadTaskGuid;
                stackTracingIds[curID].Type = 0x01;     // Thread Create
                curID++;
            }

            if ((stackCapture & KernelTraceEventParser.Keywords.ContextSwitch) != 0)
                {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.ThreadTaskGuid;
                if ((stackCapture & KernelTraceEventParser.Keywords.Thread) != 0)
                    stackTracingIds[curID].Type = 0x24;     // Context Switch
                curID++;
            }

            if ((stackCapture & KernelTraceEventParser.Keywords.Dispatcher) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.ThreadTaskGuid;
                stackTracingIds[curID].Type = 0x32;     // Ready Thread
                curID++;
            }

            // Image
            if ((stackCapture & KernelTraceEventParser.Keywords.ImageLoad) != 0)
            {
                // Confirm this is not ImageTaskGuid
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.ProcessTaskGuid;
                stackTracingIds[curID].Type = 0x0A;     // Image Load
                curID++;
            }

            // Process
            if ((stackCapture & KernelTraceEventParser.Keywords.Process) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.ProcessTaskGuid;
                stackTracingIds[curID].Type = 0x01;     // Process Create
                curID++;
            }

            // Disk
            if ((stackCapture & KernelTraceEventParser.Keywords.DiskIOInit) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.DiskIoTaskGuid;
                stackTracingIds[curID].Type = 0x0c;     // Read Init
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.DiskIoTaskGuid;
                stackTracingIds[curID].Type = 0x0d;     // Write Init
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.DiskIoTaskGuid;
                stackTracingIds[curID].Type = 0x0f;     // Flush Init
                curID++;
            }

            // Virtual Alloc
            if ((stackCapture & KernelTraceEventParser.Keywords.VirtualAlloc) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.VirtualAllocTaskGuid;
                stackTracingIds[curID].Type = 0x62;     // Flush Init
                curID++;
            }

            // Hard Faults
            if ((stackCapture & KernelTraceEventParser.Keywords.MemoryHardFaults) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid;
                stackTracingIds[curID].Type = 0x20;     // Hard Fault
                curID++;
            }

            // Page Faults
            if ((stackCapture & KernelTraceEventParser.Keywords.MemoryPageFaults) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid;
                stackTracingIds[curID].Type = 0x0A;     // Transition Fault
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid;
                stackTracingIds[curID].Type = 0x0B;     // Demand zero Fault
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid;
                stackTracingIds[curID].Type = 0x0C;     // Copy on Write Fault
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid;
                stackTracingIds[curID].Type = 0x0D;     // Guard Page Fault
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.PageFaultTaskGuid;
                stackTracingIds[curID].Type = 0x0E;     // Hard Page Fault
                curID++;

                // ! %02 49 ! Pagefile Mapped Section Create
                // ! %02 69 ! Pagefile Backed Image Mapping
                // ! %02 71 ! Contiguous Memory Generation
            }

            if ((stackCapture & KernelTraceEventParser.Keywords.FileIOInit) != 0)
            {
                // TODO allow stacks only on open and close;
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x40;     // Create
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x41;     // Cleanup
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x42;     // Close
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x43;     // Read
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x44;     // Write
                curID++;

            #if false
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x45;     // SetInformation
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x46;     // Delete
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x47;     // Rename
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x48;     // DirEnum
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x49;     // Flush
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x4A;     // QueryInformation
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x4B;     // FSControl
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.FileIoTaskGuid;
                stackTracingIds[curID].Type = 0x4D;     // DirNotify
                curID++;
            #endif
            }

            if ((stackCapture & KernelTraceEventParser.Keywords.Registry) != 0)
            {
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x0A;     // NtCreateKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x0B;     // NtOpenKey
                curID++;
            #if false
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x0C;     // NtDeleteKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x0D;     // NtQueryKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x0E;     // NtSetValueKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x0F;     // NtDeleteValueKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x10;     // NtQueryValueKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x11;     // NtEnumerateKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x12;     // NtEnumerateValueKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x13;     // NtQueryMultipleValueKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x14;     // NtSetInformationKey
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x15;     // NtFlushKey
                curID++;

                // TODO What are these?
                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x16;     // KcbCreate
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x17;     // KcbDelete
                curID++;

                stackTracingIds[curID].EventGuid = KernelTraceEventParser.RegistryTaskGuid;
                stackTracingIds[curID].Type = 0x1A;     // VirtualizeKey
                curID++;
            #endif
            }

            // Confirm we did not overflow.
            Debug.Assert(curID <= stackTracingIdsMax);
            return curID;
        }
 /// <summary>
 /// Shortcut that enables the kernel provider with no eventToStack trace capturing. 
 /// See code:#EnableKernelProvider (flags, stackCapture)
 /// </summary>
 public bool EnableKernelProvider(KernelTraceEventParser.Keywords flags)
 {
     return EnableKernelProvider(flags, KernelTraceEventParser.Keywords.None);
 }