internal unsafe CategorySample(byte[] data, CategoryEntry entry, PerformanceCounterLib library) {
            this.entry = entry;
            this.library = library;
            int categoryIndex = entry.NameIndex;
            NativeMethods.PERF_DATA_BLOCK dataBlock = new NativeMethods.PERF_DATA_BLOCK();
            fixed (byte* dataPtr = data) {
                IntPtr dataRef = new IntPtr((void*) dataPtr);

                Marshal.PtrToStructure(dataRef, dataBlock);
                this.SystemFrequency = dataBlock.PerfFreq;
                this.TimeStamp = dataBlock.PerfTime;
                this.TimeStamp100nSec = dataBlock.PerfTime100nSec;
                dataRef = (IntPtr)((long)dataRef + dataBlock.HeaderLength);
                int numPerfObjects = dataBlock.NumObjectTypes;
                if (numPerfObjects == 0) {
                    this.CounterTable = new Hashtable();
                    this.InstanceNameTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
                    return;
                }

                //Need to find the right category, GetPerformanceData might return
                //several of them.
                NativeMethods.PERF_OBJECT_TYPE perfObject = null;
                bool foundCategory = false;
                for (int index = 0; index < numPerfObjects; index++) {
                    perfObject = new NativeMethods.PERF_OBJECT_TYPE();
                    Marshal.PtrToStructure(dataRef, perfObject);

                   if (perfObject.ObjectNameTitleIndex == categoryIndex) {
                        foundCategory = true;
                        break;
                    }

                    dataRef = (IntPtr)((long)dataRef + perfObject.TotalByteLength);
                }

                if (!foundCategory)
                    throw new InvalidOperationException(SR.GetString(SR.CantReadCategoryIndex, categoryIndex.ToString(CultureInfo.CurrentCulture)));

                this.CounterFrequency = perfObject.PerfFreq;
                this.CounterTimeStamp = perfObject.PerfTime;
                int counterNumber = perfObject.NumCounters;
                int instanceNumber = perfObject.NumInstances;

                if (instanceNumber == -1)
                    IsMultiInstance = false;
                else
                    IsMultiInstance = true;
                
                // Move pointer forward to end of PERF_OBJECT_TYPE
                dataRef = (IntPtr)((long)dataRef + perfObject.HeaderLength);

                CounterDefinitionSample[] samples = new CounterDefinitionSample[counterNumber];
                this.CounterTable = new Hashtable(counterNumber);
                for (int index = 0; index < samples.Length; ++ index) {
                    NativeMethods.PERF_COUNTER_DEFINITION perfCounter = new NativeMethods.PERF_COUNTER_DEFINITION();
                    Marshal.PtrToStructure(dataRef, perfCounter);
                    samples[index] = new CounterDefinitionSample(perfCounter, this, instanceNumber);
                    dataRef = (IntPtr)((long)dataRef + perfCounter.ByteLength);

                    int currentSampleType = samples[index].CounterType;
                    if (!PerformanceCounterLib.IsBaseCounter(currentSampleType)) {
                        // We'll put only non-base counters in the table. 
                        if (currentSampleType != NativeMethods.PERF_COUNTER_NODATA)
                            this.CounterTable[samples[index].NameIndex] = samples[index];
                    }
                    else {
                        // it's a base counter, try to hook it up to the main counter. 
                        Debug.Assert(index > 0, "Index > 0 because base counters should never be at index 0");
                        if (index > 0)
                            samples[index-1].BaseCounterDefinitionSample = samples[index];
                    }
                }

                // now set up the InstanceNameTable.  
                if (!IsMultiInstance) {
                    this.InstanceNameTable = new Hashtable(1, StringComparer.OrdinalIgnoreCase);
                    this.InstanceNameTable[PerformanceCounterLib.SingleInstanceName] = 0;

                    for (int index = 0; index < samples.Length; ++ index)  {
                        samples[index].SetInstanceValue(0, dataRef);
                    }
                }
                else {
                    string[] parentInstanceNames = null;
                    this.InstanceNameTable = new Hashtable(instanceNumber, StringComparer.OrdinalIgnoreCase);
                    for (int i = 0; i < instanceNumber; i++) {
                        NativeMethods.PERF_INSTANCE_DEFINITION perfInstance = new NativeMethods.PERF_INSTANCE_DEFINITION();
                        Marshal.PtrToStructure(dataRef, perfInstance);
                        if (perfInstance.ParentObjectTitleIndex > 0 && parentInstanceNames == null)
                            parentInstanceNames = GetInstanceNamesFromIndex(perfInstance.ParentObjectTitleIndex);

                        string instanceName;
                        if (parentInstanceNames != null && perfInstance.ParentObjectInstance >= 0 && perfInstance.ParentObjectInstance < parentInstanceNames.Length - 1)
                            instanceName = parentInstanceNames[perfInstance.ParentObjectInstance] + "/" + Marshal.PtrToStringUni((IntPtr)((long)dataRef + perfInstance.NameOffset));
                        else
                            instanceName = Marshal.PtrToStringUni((IntPtr)((long)dataRef + perfInstance.NameOffset));

                        //In some cases instance names are not unique (Process), same as perfmon
                        //generate a unique name.
                        string newInstanceName = instanceName;
                        int newInstanceNumber = 1;
                        while (true) {
                            if (!this.InstanceNameTable.ContainsKey(newInstanceName)) {
                                this.InstanceNameTable[newInstanceName] = i;
                                break;
                            }
                            else {
                                newInstanceName =  instanceName + "#" + newInstanceNumber.ToString(CultureInfo.InvariantCulture);
                                ++  newInstanceNumber;
                            }
                        }


                        dataRef = (IntPtr)((long)dataRef + perfInstance.ByteLength);
                        for (int index = 0; index < samples.Length; ++ index)
                            samples[index].SetInstanceValue(i, dataRef);

                        dataRef = (IntPtr)((long)dataRef + Marshal.ReadInt32(dataRef));
                    }
                }
            }
        }
        internal unsafe string[] GetInstanceNamesFromIndex(int categoryIndex) {
            byte[] data = library.GetPerformanceData(categoryIndex.ToString(CultureInfo.InvariantCulture));
            fixed (byte* dataPtr = data) {
                IntPtr dataRef = new IntPtr((void*) dataPtr);

                NativeMethods.PERF_DATA_BLOCK dataBlock = new NativeMethods.PERF_DATA_BLOCK();
                Marshal.PtrToStructure(dataRef, dataBlock);
                dataRef = (IntPtr)((long)dataRef + dataBlock.HeaderLength);
                int numPerfObjects = dataBlock.NumObjectTypes;

                NativeMethods.PERF_OBJECT_TYPE perfObject = null;
                bool foundCategory = false;
                for (int index = 0; index < numPerfObjects; index++) {
                    perfObject = new NativeMethods.PERF_OBJECT_TYPE();
                    Marshal.PtrToStructure(dataRef, perfObject);

                    if (perfObject.ObjectNameTitleIndex == categoryIndex) {
                        foundCategory = true;
                        break;
                    }

                    dataRef = (IntPtr)((long)dataRef + perfObject.TotalByteLength);
                }

                if (!foundCategory)
                    return new string[0];

                int counterNumber = perfObject.NumCounters;
                int instanceNumber = perfObject.NumInstances;
                dataRef = (IntPtr)((long)dataRef + perfObject.HeaderLength);

                if (instanceNumber == -1)
                    return new string[0];

                CounterDefinitionSample[] samples = new CounterDefinitionSample[counterNumber];
                for (int index = 0; index < samples.Length; ++ index) {
                    NativeMethods.PERF_COUNTER_DEFINITION perfCounter = new NativeMethods.PERF_COUNTER_DEFINITION();
                    Marshal.PtrToStructure(dataRef, perfCounter);
                    dataRef = (IntPtr)((long)dataRef + perfCounter.ByteLength);
                }

                string[] instanceNames = new string[instanceNumber];
                for (int i = 0; i < instanceNumber; i++) {
                    NativeMethods.PERF_INSTANCE_DEFINITION perfInstance = new NativeMethods.PERF_INSTANCE_DEFINITION();
                    Marshal.PtrToStructure(dataRef, perfInstance);
                    instanceNames[i] =  Marshal.PtrToStringUni((IntPtr)((long)dataRef + perfInstance.NameOffset));
                    dataRef = (IntPtr)((long)dataRef + perfInstance.ByteLength);
                    dataRef = (IntPtr)((long)dataRef + Marshal.ReadInt32(dataRef));
                }

                return instanceNames;
            }
        }
        static ProcessInfo[] GetProcessInfos(PerformanceCounterLib library, int processIndex, int threadIndex, byte[] data) {
            Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos()");
            Hashtable processInfos = new Hashtable();
            ArrayList threadInfos = new ArrayList();

            GCHandle dataHandle = new GCHandle();             
            try {
                dataHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
                IntPtr dataBlockPtr = dataHandle.AddrOfPinnedObject();
                NativeMethods.PERF_DATA_BLOCK dataBlock = new NativeMethods.PERF_DATA_BLOCK();
                Marshal.PtrToStructure(dataBlockPtr, dataBlock);
                IntPtr typePtr = (IntPtr)((long)dataBlockPtr + dataBlock.HeaderLength);
                NativeMethods.PERF_INSTANCE_DEFINITION instance = new NativeMethods.PERF_INSTANCE_DEFINITION();
                NativeMethods.PERF_COUNTER_BLOCK counterBlock = new NativeMethods.PERF_COUNTER_BLOCK();                        
                for (int i = 0; i < dataBlock.NumObjectTypes; i++) {
                    NativeMethods.PERF_OBJECT_TYPE type = new NativeMethods.PERF_OBJECT_TYPE();
                    Marshal.PtrToStructure(typePtr, type);
                    IntPtr instancePtr = (IntPtr)((long)typePtr + type.DefinitionLength);
                    IntPtr counterPtr = (IntPtr)((long)typePtr + type.HeaderLength);
                    ArrayList counterList = new ArrayList();
                    
                    for (int j = 0; j < type.NumCounters; j++) {                    
                        NativeMethods.PERF_COUNTER_DEFINITION counter = new NativeMethods.PERF_COUNTER_DEFINITION();
                        Marshal.PtrToStructure(counterPtr, counter);
                        string counterName = library.GetCounterName(counter.CounterNameTitleIndex);

                        if (type.ObjectNameTitleIndex == processIndex)
                            counter.CounterNameTitlePtr = (int)GetValueId(counterName);
                        else if (type.ObjectNameTitleIndex == threadIndex)
                            counter.CounterNameTitlePtr = (int)GetValueId(counterName);
                        counterList.Add(counter);
                        counterPtr = (IntPtr)((long)counterPtr + counter.ByteLength);
                    }
                    NativeMethods.PERF_COUNTER_DEFINITION[] counters = new NativeMethods.PERF_COUNTER_DEFINITION[counterList.Count];
                    counterList.CopyTo(counters, 0);
                    for (int j = 0; j < type.NumInstances; j++) {
                        Marshal.PtrToStructure(instancePtr, instance);
                        IntPtr namePtr = (IntPtr)((long)instancePtr + instance.NameOffset);
                        string instanceName = Marshal.PtrToStringUni(namePtr);            
                        if (instanceName.Equals("_Total")) continue;
                        IntPtr counterBlockPtr = (IntPtr)((long)instancePtr + instance.ByteLength);
                        Marshal.PtrToStructure(counterBlockPtr, counterBlock);
                        if (type.ObjectNameTitleIndex == processIndex) {
                            ProcessInfo processInfo = GetProcessInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
                            if (processInfo.processId == 0 && string.Compare(instanceName, "Idle", StringComparison.OrdinalIgnoreCase) != 0) {
                                // Sometimes we'll get a process structure that is not completely filled in.
                                // We can catch some of these by looking for non-"idle" processes that have id 0
                                // and ignoring those.
                                Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos() - found a non-idle process with id 0; ignoring.");
                            }
                            else {
                                if (processInfos[processInfo.processId] != null) {
                                    // We've found two entries in the perfcounters that claim to be the
                                    // same process.  We throw an exception.  Is this really going to be
                                    // helpfull to the user?  Should we just ignore?
                                    Debug.WriteLineIf(Process.processTracing.TraceVerbose, "GetProcessInfos() - found a duplicate process id");
                                }
                                else {
                                    // the performance counters keep a 15 character prefix of the exe name, and then delete the ".exe",
                                    // if it's in the first 15.  The problem is that sometimes that will leave us with part of ".exe"
                                    // at the end.  If instanceName ends in ".", ".e", or ".ex" we remove it.
                                    string processName = instanceName;
                                    if (processName.Length == 15) {
                                        if      (instanceName.EndsWith(".", StringComparison.Ordinal  )) processName = instanceName.Substring(0, 14);
                                        else if (instanceName.EndsWith(".e", StringComparison.Ordinal )) processName = instanceName.Substring(0, 13);
                                        else if (instanceName.EndsWith(".ex", StringComparison.Ordinal)) processName = instanceName.Substring(0, 12);
                                    }
                                    processInfo.processName = processName;
                                    processInfos.Add(processInfo.processId, processInfo);
                                }
                            }
                        }
                        else if (type.ObjectNameTitleIndex == threadIndex) {
                            ThreadInfo threadInfo = GetThreadInfo(type, (IntPtr)((long)instancePtr + instance.ByteLength), counters);
                            if (threadInfo.threadId != 0) threadInfos.Add(threadInfo);
                        }
                        instancePtr = (IntPtr)((long)instancePtr + instance.ByteLength + counterBlock.ByteLength);
                    }                                
                    
                    typePtr = (IntPtr)((long)typePtr + type.TotalByteLength);
                }
            }
            finally {
                if (dataHandle.IsAllocated) dataHandle.Free();
            }

            for (int i = 0; i < threadInfos.Count; i++) {
                ThreadInfo threadInfo = (ThreadInfo)threadInfos[i];
                ProcessInfo processInfo = (ProcessInfo)processInfos[threadInfo.processId];
                if (processInfo != null) {
                    processInfo.threadInfoList.Add(threadInfo);
                }
            }
                        
            ProcessInfo[] temp = new ProcessInfo[processInfos.Values.Count];
            processInfos.Values.CopyTo(temp, 0);
            return temp;
        }