File: services\monitoring\system\diagnosticts\PerformanceCounterLib.cs
Project: ndp\fx\src\System.csproj (System)
namespace System.Diagnostics {
    using System.Runtime.InteropServices;
    using System.Globalization;
    using System.Security.Permissions;
    using System.Security;
    using System.Text;
    using System.Threading;
    using System.Reflection;
    using System.Collections;
    using System.ComponentModel;
    using System.Collections.Specialized;
    using Microsoft.Win32;
    using System.IO;
    using System.Runtime.Serialization;
    using System.Runtime.Versioning;
 
    internal class PerformanceCounterLib {
        internal const string PerfShimName = "netfxperf.dll";
        private  const string PerfShimFullNameSuffix = @"\netfxperf.dll";
        private  const string PerfShimPathExp = @"%systemroot%\system32\netfxperf.dll";
        internal const string OpenEntryPoint = "OpenPerformanceData";
        internal const string CollectEntryPoint = "CollectPerformanceData";
        internal const string CloseEntryPoint = "ClosePerformanceData";
        internal const string SingleInstanceName = "systemdiagnosticsperfcounterlibsingleinstance";
 
        private const string PerflibPath = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
        internal const string ServicePath = "SYSTEM\\CurrentControlSet\\Services";
        private const string categorySymbolPrefix = "OBJECT_";
        private const string conterSymbolPrefix = "DEVICE_COUNTER_";
        private const string helpSufix = "_HELP";
        private const string nameSufix = "_NAME";
        private const string textDefinition = "[text]";
        private const string infoDefinition = "[info]";
        private const string languageDefinition = "[languages]";
        private const string objectDefinition = "[objects]";
        private const string driverNameKeyword = "drivername";
        private const string symbolFileKeyword = "symbolfile";
        private const string defineKeyword = "#define";
        private const string languageKeyword = "language";
        private const string DllName = "netfxperf.dll";
 
        private const int EnglishLCID = 0x009;
 
        private static volatile string computerName;
        private static volatile string iniFilePath;
        private static volatile string symbolFilePath;
 
        private PerformanceMonitor performanceMonitor;
        private string machineName;
        private string perfLcid;
 
        private Hashtable customCategoryTable;
        private static volatile Hashtable libraryTable;
        private Hashtable categoryTable;
        private Hashtable nameTable;
        private Hashtable helpTable;
        private readonly object CategoryTableLock = new Object();
        private readonly object NameTableLock = new Object();
        private readonly object HelpTableLock = new Object();
 
        private static Object s_InternalSyncObject;
        private static Object InternalSyncObject {
            get {
                if (s_InternalSyncObject == null) {
                    Object o = new Object();
                    Interlocked.CompareExchange(ref s_InternalSyncObject, o, null);
                }
                return s_InternalSyncObject;
            }
        }
 
        internal PerformanceCounterLib(string machineName, string lcid) {
            this.machineName = machineName;
            this.perfLcid = lcid;
        }
 
        /// <internalonly/>
        internal static string ComputerName {
            get {
                if (computerName == null) {
                    lock (InternalSyncObject) {
                        if (computerName == null) {
                            StringBuilder sb = new StringBuilder(256);
                            SafeNativeMethods.GetComputerName(sb, new int[] {sb.Capacity});
                            computerName = sb.ToString();
                        }
                    }
                }
 
                return computerName;
            }
        }
 
        private unsafe Hashtable CategoryTable {
            get {
                if (this.categoryTable == null) {
                    lock (this.CategoryTableLock) {
                        if (this.categoryTable == null) {
                            byte[] perfData =  GetPerformanceData("Global");
 
                            fixed (byte* perfDataPtr = perfData) {
                                IntPtr dataRef = new IntPtr( (void*) perfDataPtr);
                                NativeMethods.PERF_DATA_BLOCK dataBlock = new NativeMethods.PERF_DATA_BLOCK();
                                Marshal.PtrToStructure(dataRef, dataBlock);
                                dataRef = (IntPtr)((long)dataRef + dataBlock.HeaderLength);
                                int categoryNumber = dataBlock.NumObjectTypes;
 
                                // on some machines MSMQ claims to have 4 categories, even though it only has 2.
                                // This causes us to walk past the end of our data, potentially crashing or reading
                                // data we shouldn't.  We use endPerfData to make sure we don't go past the end
                                // of the perf data.  (ASURT 137097)
                                long endPerfData = (long)(new IntPtr((void*)perfDataPtr)) + dataBlock.TotalByteLength;
                                Hashtable tempCategoryTable = new Hashtable(categoryNumber, StringComparer.OrdinalIgnoreCase);
                                for (int index = 0; index < categoryNumber && ((long) dataRef < endPerfData); index++) {
                                    NativeMethods.PERF_OBJECT_TYPE perfObject = new NativeMethods.PERF_OBJECT_TYPE();
 
                                    Marshal.PtrToStructure(dataRef, perfObject);
 
                                    CategoryEntry newCategoryEntry = new CategoryEntry(perfObject);
                                    IntPtr nextRef =  (IntPtr)((long)dataRef + perfObject.TotalByteLength);
                                    dataRef = (IntPtr)((long)dataRef + perfObject.HeaderLength);
 
                                    int index3 = 0;
                                    int previousCounterIndex = -1;
                                    //Need to filter out counters that are repeated, some providers might
                                    //return several adyacent copies of the same counter.
                                    for (int index2 = 0; index2 < newCategoryEntry.CounterIndexes.Length; ++ index2) {
                                        NativeMethods.PERF_COUNTER_DEFINITION perfCounter = new NativeMethods.PERF_COUNTER_DEFINITION();
                                        Marshal.PtrToStructure(dataRef, perfCounter);
                                        if (perfCounter.CounterNameTitleIndex != previousCounterIndex) {
                                            newCategoryEntry.CounterIndexes[index3] =  perfCounter.CounterNameTitleIndex;
                                            newCategoryEntry.HelpIndexes[index3] = perfCounter.CounterHelpTitleIndex;
                                            previousCounterIndex = perfCounter.CounterNameTitleIndex;
                                            ++ index3;
                                        }
                                        dataRef = (IntPtr)((long)dataRef + perfCounter.ByteLength);
                                    }
 
                                    //Lets adjust the entry counter arrays in case there were repeated copies
                                    if (index3 <  newCategoryEntry.CounterIndexes.Length) {
                                        int[] adjustedCounterIndexes = new int[index3];
                                        int[] adjustedHelpIndexes = new int[index3];
                                        Array.Copy(newCategoryEntry.CounterIndexes, adjustedCounterIndexes, index3);
                                        Array.Copy(newCategoryEntry.HelpIndexes, adjustedHelpIndexes, index3);
                                        newCategoryEntry.CounterIndexes = adjustedCounterIndexes;
                                        newCategoryEntry.HelpIndexes = adjustedHelpIndexes;
                                    }
 
                                    string categoryName = (string)this.NameTable[newCategoryEntry.NameIndex];
                                    if (categoryName != null)
                                        tempCategoryTable[categoryName] = newCategoryEntry;
 
                                    dataRef = nextRef;
                                }
 
                                this.categoryTable = tempCategoryTable;
                            }
                        }
                    }
                }
 
                return this.categoryTable;
            }
        }
 
        internal Hashtable HelpTable {
            get {
                if (this.helpTable == null) {
                    lock(this.HelpTableLock) {
                        if (this.helpTable == null)
                            this.helpTable = GetStringTable(true);
                    }
                }
 
                return this.helpTable;
            }
        }
 
        // Returns a temp file name
        private static string IniFilePath {
            [ResourceExposure(ResourceScope.AppDomain)]
            [ResourceConsumption(ResourceScope.AppDomain)]
            get {
                if (iniFilePath == null) {
                    lock (InternalSyncObject) {
                        if (iniFilePath == null) {
                            // Need to assert Environment permissions here
                            //                        the environment check is not exposed as a public
                            //                        method
                            EnvironmentPermission environmentPermission = new EnvironmentPermission(PermissionState.Unrestricted);
                            environmentPermission.Assert();
                            try {
                                iniFilePath = Path.GetTempFileName();
                            }
                            finally {
                                 EnvironmentPermission.RevertAssert();
                            }
                        }
                    }
                }
 
                return iniFilePath;
            }
        }
 
        internal Hashtable NameTable {
            get {
                if (this.nameTable == null) {
                    lock(this.NameTableLock) {
                        if (this.nameTable == null)
                            this.nameTable = GetStringTable(false);
                    }
                }
 
                return this.nameTable;
            }
        }
 
        // Returns a temp file name
        private static string SymbolFilePath {
            [ResourceExposure(ResourceScope.AppDomain)]
            [ResourceConsumption(ResourceScope.AppDomain | ResourceScope.Machine)]
            get {
                if (symbolFilePath == null) {
                    lock (InternalSyncObject) {
                        if (symbolFilePath == null) {
                            string tempPath;
                            
                            EnvironmentPermission environmentPermission = new EnvironmentPermission(PermissionState.Unrestricted);
                            environmentPermission.Assert();
                            tempPath = Path.GetTempPath();
                            EnvironmentPermission.RevertAssert();
 
                            // We need both FileIOPermission EvironmentPermission
                            PermissionSet ps = new PermissionSet(PermissionState.None);
                            ps.AddPermission(new EnvironmentPermission(PermissionState.Unrestricted));
                            ps.AddPermission(new FileIOPermission(FileIOPermissionAccess.Write, tempPath));
                            ps.Assert();
                            try {
                                symbolFilePath = Path.GetTempFileName();
                            }
                            finally {
                                 PermissionSet.RevertAssert();
                            }
                        }
                    }
                }
 
                return symbolFilePath;
            }
        }
 
        internal static bool CategoryExists(string machine, string category) {
            PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            if (library.CategoryExists(category)) 
                return true;
 
            if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                CultureInfo culture = CultureInfo.CurrentCulture;
                while (culture != CultureInfo.InvariantCulture) {
                    library = GetPerformanceCounterLib(machine, culture);
                    if (library.CategoryExists(category))
                        return true;
                    culture = culture.Parent;
                }
            }
 
            return false;
        }
 
        internal bool CategoryExists(string category) {
            return CategoryTable.ContainsKey(category);
        }
 
        internal static void CloseAllLibraries() {
            if (libraryTable != null) {
                foreach (PerformanceCounterLib library in libraryTable.Values)
                    library.Close();
 
                libraryTable = null;
            }
        }
 
        internal static void CloseAllTables() {
            if (libraryTable != null) {
                foreach (PerformanceCounterLib library in libraryTable.Values)
                    library.CloseTables();
            }
        }
 
        internal void CloseTables() {
            this.nameTable = null;
            this.helpTable = null;
            this.categoryTable = null;
            this.customCategoryTable = null;
        }
 
        internal void Close() {
            if (this.performanceMonitor != null) {
                this.performanceMonitor.Close();
                this.performanceMonitor = null;
            }
 
            CloseTables();
        }
 
        internal static bool CounterExists(string machine, string category, string counter) {
            PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            bool categoryExists = false;
            bool counterExists = library.CounterExists(category, counter, ref categoryExists);
 
            if (!categoryExists && CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                CultureInfo culture = CultureInfo.CurrentCulture;
                while (culture != CultureInfo.InvariantCulture) {
                    library = GetPerformanceCounterLib(machine, culture);
                    counterExists = library.CounterExists(category, counter, ref categoryExists);
                    if (counterExists)
                        break;
 
                    culture = culture.Parent;
                }
            }
 
            if (!categoryExists) {
                // Consider adding diagnostic logic here, may be we can dump the nameTable...
                throw new InvalidOperationException(SR.GetString(SR.MissingCategory));
            }
 
            return counterExists;
        }
 
        private bool CounterExists(string category, string counter, ref bool categoryExists) {
            categoryExists = false;
            if (!CategoryTable.ContainsKey(category))
                return false;
            else
                categoryExists = true;
 
            CategoryEntry entry = (CategoryEntry)this.CategoryTable[category];
            for (int index = 0; index < entry.CounterIndexes.Length; ++ index) {
                int counterIndex = entry.CounterIndexes[index];
                string counterName = (string)this.NameTable[counterIndex];
                if (counterName == null)
                   counterName = String.Empty;
 
                if (String.Compare(counterName, counter, StringComparison.OrdinalIgnoreCase) == 0)
                    return true;
            }
 
            return false;
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static void CreateIniFile(string categoryName, string categoryHelp, CounterCreationDataCollection creationData, string[] languageIds) {
            //SECREVIEW: PerformanceCounterPermission must have been demanded before
            FileIOPermission permission = new FileIOPermission(PermissionState.Unrestricted);
            permission.Assert();
            try {
                StreamWriter iniWriter = new StreamWriter(IniFilePath, false, Encoding.Unicode);
                try {
                    //NT4 won't be able to parse Unicode ini files without this
                    //extra white space.
                    iniWriter.WriteLine("");
                    iniWriter.WriteLine(infoDefinition);
                    iniWriter.Write(driverNameKeyword);
                    iniWriter.Write("=");
                    iniWriter.WriteLine(categoryName);
                    iniWriter.Write(symbolFileKeyword);
                    iniWriter.Write("=");
                    iniWriter.WriteLine(Path.GetFileName(SymbolFilePath));
                    iniWriter.WriteLine("");
 
                    iniWriter.WriteLine(languageDefinition);
                    foreach (string languageId in languageIds) {
                        iniWriter.Write(languageId);
                        iniWriter.Write("=");
                        iniWriter.Write(languageKeyword);
                        iniWriter.WriteLine(languageId);
                    }
                    iniWriter.WriteLine("");
 
                    iniWriter.WriteLine(objectDefinition);
                    foreach (string languageId in languageIds) {
                        iniWriter.Write(categorySymbolPrefix);
                        iniWriter.Write("1_");
                        iniWriter.Write(languageId);
                        iniWriter.Write(nameSufix);
                        iniWriter.Write("=");
                        iniWriter.WriteLine(categoryName);
                    }
                    iniWriter.WriteLine("");
 
                    iniWriter.WriteLine(textDefinition);
                    foreach (string languageId in languageIds) {
                        iniWriter.Write(categorySymbolPrefix);
                        iniWriter.Write("1_");
                        iniWriter.Write(languageId);
                        iniWriter.Write(nameSufix);
                        iniWriter.Write("=");
                        iniWriter.WriteLine(categoryName);
                        iniWriter.Write(categorySymbolPrefix);
                        iniWriter.Write("1_");
                        iniWriter.Write(languageId);
                        iniWriter.Write(helpSufix);
                        iniWriter.Write("=");
                        if (categoryHelp == null || categoryHelp == String.Empty)
                            iniWriter.WriteLine(SR.GetString(SR.HelpNotAvailable));
                        else
                            iniWriter.WriteLine(categoryHelp);
 
 
                        int counterIndex = 0;
                        foreach (CounterCreationData counterData in creationData) {
                            ++counterIndex;
                            iniWriter.WriteLine("");
                            iniWriter.Write(conterSymbolPrefix);
                            iniWriter.Write(counterIndex.ToString(CultureInfo.InvariantCulture));
                            iniWriter.Write("_");
                            iniWriter.Write(languageId);
                            iniWriter.Write(nameSufix);
                            iniWriter.Write("=");
                            iniWriter.WriteLine(counterData.CounterName);
 
                            iniWriter.Write(conterSymbolPrefix);
                            iniWriter.Write(counterIndex.ToString(CultureInfo.InvariantCulture));
                            iniWriter.Write("_");
                            iniWriter.Write(languageId);
                            iniWriter.Write(helpSufix);
                            iniWriter.Write("=");
 
                            Debug.Assert(!String.IsNullOrEmpty(counterData.CounterHelp), "CounterHelp should have been fixed up by the caller");
                            iniWriter.WriteLine(counterData.CounterHelp);
                        }
                    }
 
                    iniWriter.WriteLine("");
                }
                finally {
                    iniWriter.Close();
                }
            }
            finally {
                FileIOPermission.RevertAssert();
            }
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static void CreateRegistryEntry(string categoryName, PerformanceCounterCategoryType categoryType, CounterCreationDataCollection creationData, ref bool iniRegistered) {
            RegistryKey serviceParentKey = null;
            RegistryKey serviceKey = null;
            RegistryKey linkageKey = null;
 
            //SECREVIEW: Whoever is able to call this function, must already
            //                         have demmanded PerformanceCounterPermission
            //                         we can therefore assert the RegistryPermission.
            RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted);
            registryPermission.Assert();
            try {
                serviceParentKey = Registry.LocalMachine.OpenSubKey(ServicePath, true);
 
                serviceKey = serviceParentKey.OpenSubKey(categoryName + "\\Performance", true);
                if (serviceKey == null)
                    serviceKey = serviceParentKey.CreateSubKey(categoryName + "\\Performance");
 
                serviceKey.SetValue("Open","OpenPerformanceData");
                serviceKey.SetValue("Collect", "CollectPerformanceData");
                serviceKey.SetValue("Close","ClosePerformanceData");
                serviceKey.SetValue("Library", DllName);
                serviceKey.SetValue("IsMultiInstance", (int) categoryType, RegistryValueKind.DWord);
                serviceKey.SetValue("CategoryOptions", 0x3, RegistryValueKind.DWord);
 
                string [] counters = new string[creationData.Count];
                string [] counterTypes = new string[creationData.Count];
                for (int i = 0; i < creationData.Count; i++) {
                    counters[i] = creationData[i].CounterName;
                    counterTypes[i] = ((int) creationData[i].CounterType).ToString(CultureInfo.InvariantCulture);
                }
 
                linkageKey = serviceParentKey.OpenSubKey(categoryName + "\\Linkage" , true);
                if (linkageKey == null)
                    linkageKey = serviceParentKey.CreateSubKey(categoryName + "\\Linkage" );
 
                linkageKey.SetValue("Export", new string[]{categoryName});
 
                serviceKey.SetValue("Counter Types", (object) counterTypes);
                serviceKey.SetValue("Counter Names", (object) counters);
 
                object firstID = serviceKey.GetValue("First Counter");
                if (firstID != null)
                    iniRegistered  = true;
                else
                    iniRegistered  = false;
            }
            finally {
                if (serviceKey != null)
                    serviceKey.Close();
 
                if (linkageKey != null)
                    linkageKey.Close();
 
                if (serviceParentKey != null)
                    serviceParentKey.Close();
 
                RegistryPermission.RevertAssert();
            }
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static void CreateSymbolFile(CounterCreationDataCollection creationData) {
            //SECREVIEW: PerformanceCounterPermission must have been demanded before
            FileIOPermission permission = new FileIOPermission(PermissionState.Unrestricted);
            permission.Assert();
            try {
                StreamWriter symbolWriter = new StreamWriter(SymbolFilePath);
                try {
                    symbolWriter.Write(defineKeyword);
                    symbolWriter.Write(" ");
                    symbolWriter.Write(categorySymbolPrefix);
                    symbolWriter.WriteLine("1 0;");
 
                    for (int counterIndex = 1; counterIndex <= creationData.Count; ++ counterIndex) {
                        symbolWriter.Write(defineKeyword);
                        symbolWriter.Write(" ");
                        symbolWriter.Write(conterSymbolPrefix);
                        symbolWriter.Write(counterIndex.ToString(CultureInfo.InvariantCulture));
                        symbolWriter.Write(" ");
                        symbolWriter.Write((counterIndex * 2).ToString(CultureInfo.InvariantCulture));
                        symbolWriter.WriteLine(";");
                    }
 
                    symbolWriter.WriteLine("");
                }
                finally {
                    symbolWriter.Close();
                }
            }
            finally {
                FileIOPermission.RevertAssert();
            }
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static void DeleteRegistryEntry(string categoryName) {
            RegistryKey serviceKey = null;
 
            //SECREVIEW: Whoever is able to call this function, must already
            //                         have demmanded PerformanceCounterPermission
            //                         we can therefore assert the RegistryPermission.
            RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted);
            registryPermission.Assert();
            try {
                serviceKey = Registry.LocalMachine.OpenSubKey(ServicePath, true);
 
                bool deleteCategoryKey = false;
                using (RegistryKey categoryKey = serviceKey.OpenSubKey(categoryName, true)) {
                    if (categoryKey != null) {
                        if (categoryKey.GetValueNames().Length == 0) {
                            deleteCategoryKey = true;
                        }
                        else {
                            categoryKey.DeleteSubKeyTree("Linkage");
                            categoryKey.DeleteSubKeyTree("Performance");
                        }
                    }
                }
                if (deleteCategoryKey)
                    serviceKey.DeleteSubKeyTree(categoryName);
                
            }
            finally {
                if (serviceKey != null)
                    serviceKey.Close();
 
                RegistryPermission.RevertAssert();
            }
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static void DeleteTemporaryFiles() {
            try {
                File.Delete(IniFilePath);
            }
            catch {
            }
 
            try {
                File.Delete(SymbolFilePath);
            }
            catch {
            }
        }
 
        // Ensures that the customCategoryTable is initialized and decides whether the category passed in 
        //  1) is a custom category
        //  2) is a multi instance custom category
        // The return value is whether the category is a custom category or not. 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        internal bool FindCustomCategory(string category, out PerformanceCounterCategoryType categoryType) {
            RegistryKey key = null;
            RegistryKey baseKey = null;
            categoryType = PerformanceCounterCategoryType.Unknown;
            
            if (this.customCategoryTable == null) {
                Interlocked.CompareExchange(ref this.customCategoryTable, new Hashtable(StringComparer.OrdinalIgnoreCase), null);
            }
 
            if (this.customCategoryTable.ContainsKey(category)) {
                categoryType= (PerformanceCounterCategoryType) this.customCategoryTable[category];
                return true;
            }
            else {
                //SECREVIEW: Whoever is able to call this function, must already
                //                         have demanded PerformanceCounterPermission
                //                         we can therefore assert the RegistryPermission.
                PermissionSet ps = new PermissionSet(PermissionState.None);
                ps.AddPermission(new RegistryPermission(PermissionState.Unrestricted));
                ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
                ps.Assert();
                try {
                    string keyPath = ServicePath + "\\" + category + "\\Performance";
                    if (machineName == "." || String.Compare(this.machineName, ComputerName, StringComparison.OrdinalIgnoreCase) == 0) {
                        key = Registry.LocalMachine.OpenSubKey(keyPath);
                    }
                    else {
                        baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "\\\\" + this.machineName);
                        if (baseKey != null) {
                            try {
                                key = baseKey.OpenSubKey(keyPath);
                            } catch (SecurityException) {
                                // we may not have permission to read the registry key on the remote machine.  The security exception  
                                // is thrown when RegOpenKeyEx returns ERROR_ACCESS_DENIED or ERROR_BAD_IMPERSONATION_LEVEL
                                //
                                // In this case we return an 'Unknown' category type and 'false' to indicate the category is *not* custom.
                                //
                                categoryType = PerformanceCounterCategoryType.Unknown;
                                this.customCategoryTable[category] = categoryType;
                                return false;
                            }
                        }
                    }
 
                    if (key != null) {
                        object systemDllName = key.GetValue("Library", null, RegistryValueOptions.DoNotExpandEnvironmentNames);
                        if (systemDllName != null && systemDllName is string 
                            && (String.Compare((string)systemDllName, PerformanceCounterLib.PerfShimName, StringComparison.OrdinalIgnoreCase) == 0
                              || ((string)systemDllName).EndsWith(PerformanceCounterLib.PerfShimFullNameSuffix, StringComparison.OrdinalIgnoreCase))) {
                            
                            object isMultiInstanceObject = key.GetValue("IsMultiInstance");
                            if (isMultiInstanceObject != null) {
                                categoryType = (PerformanceCounterCategoryType) isMultiInstanceObject;
                                if (categoryType < PerformanceCounterCategoryType.Unknown || categoryType > PerformanceCounterCategoryType.MultiInstance)
                                    categoryType = PerformanceCounterCategoryType.Unknown;
                            }
                            else
                                categoryType = PerformanceCounterCategoryType.Unknown;
                                
                            object objectID = key.GetValue("First Counter");
                            if (objectID != null) {
                                int firstID = (int)objectID;
 
                                this.customCategoryTable[category] = categoryType;
                                return true;
                            }
                        }
                    }
                }
                finally {
                    if (key != null) key.Close();
                    if (baseKey != null) baseKey.Close();
                    PermissionSet.RevertAssert();
                }
            }
            return false;
        }
 
        internal static string[] GetCategories(string machineName) {
            PerformanceCounterLib library;
            CultureInfo culture = CultureInfo.CurrentCulture;
            while (culture != CultureInfo.InvariantCulture) {
                library = GetPerformanceCounterLib(machineName, culture);
                string[] categories = library.GetCategories();
                if (categories.Length != 0 ) 
                    return categories;
                culture = culture.Parent;
            }
 
            library = GetPerformanceCounterLib(machineName, new CultureInfo(EnglishLCID));
            return library.GetCategories();
        }
 
        internal string[] GetCategories() {
            ICollection keys = CategoryTable.Keys;
            string[] categories = new string[keys.Count];
            keys.CopyTo(categories, 0);
            return categories;
        }
 
        internal static string GetCategoryHelp(string machine, string category) {
            PerformanceCounterLib library;
            string help;
 
            //First check the current culture for the category. This will allow 
            //PerformanceCounterCategory.CategoryHelp to return localized strings.
            if(CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                CultureInfo culture = CultureInfo.CurrentCulture;
               
                while (culture != CultureInfo.InvariantCulture) {
                    library = GetPerformanceCounterLib(machine, culture);
                    help = library.GetCategoryHelp(category);
                    if (help != null)
                        return help;
                    culture = culture.Parent;
                }
            }
 
            //We did not find the category walking up the culture hierarchy. Try looking
            // for the category in the default culture English.
            library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            help = library.GetCategoryHelp(category);
 
            if (help == null)
                throw new InvalidOperationException(SR.GetString(SR.MissingCategory));
 
            return help;
        }
 
        private string GetCategoryHelp(string category) {
            CategoryEntry entry = (CategoryEntry)this.CategoryTable[category];
            if (entry == null)
                return null;
 
            return (string)this.HelpTable[entry.HelpIndex];
        }
 
        internal static CategorySample GetCategorySample(string machine, string category) {
            PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            CategorySample sample = library.GetCategorySample(category);
            if (sample == null && CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                CultureInfo culture = CultureInfo.CurrentCulture;
                while (culture != CultureInfo.InvariantCulture) {
                    library = GetPerformanceCounterLib(machine, culture);
                    sample = library.GetCategorySample(category);
                    if (sample != null)
                        return sample;
                    culture = culture.Parent;
                }
            }
            if (sample == null)
                throw new InvalidOperationException(SR.GetString(SR.MissingCategory));
 
            return sample;
        }
 
        private CategorySample GetCategorySample(string category) {
            CategoryEntry entry = (CategoryEntry)this.CategoryTable[category];
            if (entry == null)
                return null;
 
            CategorySample sample = null;
            byte[] dataRef = GetPerformanceData(entry.NameIndex.ToString(CultureInfo.InvariantCulture));
            if (dataRef == null)
                throw new InvalidOperationException(SR.GetString(SR.CantReadCategory, category));
 
            sample = new CategorySample(dataRef, entry, this);
            return sample;
        }
 
        internal static string[] GetCounters(string machine, string category) {
            PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            bool categoryExists = false;
            string[] counters = library.GetCounters(category, ref categoryExists);
 
            if (!categoryExists && CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                CultureInfo culture = CultureInfo.CurrentCulture;
                while (culture != CultureInfo.InvariantCulture) {
                    library = GetPerformanceCounterLib(machine, culture);
                    counters = library.GetCounters(category, ref categoryExists);
                    if (categoryExists)
                        return counters;
 
                    culture = culture.Parent;
                }
            }
            
            if (!categoryExists)
                throw new InvalidOperationException(SR.GetString(SR.MissingCategory));
 
            return counters;
        }
 
        private string[] GetCounters(string category, ref bool categoryExists) {
            categoryExists = false;
            CategoryEntry entry = (CategoryEntry)this.CategoryTable[category];
            if (entry == null)
                return null;
            else
                categoryExists = true;
 
            int index2 = 0;
            string[] counters = new string[entry.CounterIndexes.Length];
            for (int index = 0; index < counters.Length; ++ index) {
                int counterIndex = entry.CounterIndexes[index];
                string counterName = (string)this.NameTable[counterIndex];
                if (counterName != null && counterName != String.Empty) {
                    counters[index2] = counterName;
                    ++index2;
                }
            }
 
            //Lets adjust the array in case there were null entries
            if (index2 < counters.Length) {
                string[] adjustedCounters = new string[index2];
                Array.Copy(counters, adjustedCounters, index2);
                counters = adjustedCounters;
            }
 
            return counters;
        }
 
        internal static string GetCounterHelp(string machine, string category, string counter) {
            PerformanceCounterLib library;
            bool categoryExists = false;
            string help;
 
            //First check the current culture for the counter. This will allow 
            //PerformanceCounter.CounterHelp to return localized strings.            
            if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                CultureInfo culture = CultureInfo.CurrentCulture;
                while (culture != CultureInfo.InvariantCulture) {
                    library = GetPerformanceCounterLib(machine, culture);
                    help = library.GetCounterHelp(category, counter, ref categoryExists);
                    if (categoryExists)
                        return help;
                    culture = culture.Parent;
                }
            }
 
            //We did not find the counter walking up the culture hierarchy. Try looking
            // for the counter in the default culture English.
            library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            help = library.GetCounterHelp(category, counter, ref categoryExists);
 
            if (!categoryExists)
                throw new InvalidOperationException(SR.GetString(SR.MissingCategoryDetail, category));
 
            return help;
        }
 
        private string GetCounterHelp(string category, string counter, ref bool categoryExists) {
            categoryExists = false;
            CategoryEntry entry = (CategoryEntry)this.CategoryTable[category];
            if (entry == null)
                return null;
            else
                categoryExists = true;
 
            int helpIndex = -1;
            for (int index = 0; index < entry.CounterIndexes.Length; ++ index) {
                int counterIndex = entry.CounterIndexes[index];
                string counterName = (string)this.NameTable[counterIndex];
                if (counterName == null)
                   counterName = String.Empty;
 
                if (String.Compare(counterName, counter, StringComparison.OrdinalIgnoreCase) == 0) {
                    helpIndex = entry.HelpIndexes[index];
                    break;
                }
            }
 
            if (helpIndex == -1)
                throw new InvalidOperationException(SR.GetString(SR.MissingCounter, counter));
 
            string help = (string)this.HelpTable[helpIndex];
            if (help == null)
                return String.Empty;
            else
                return help;
        }
 
        internal string GetCounterName(int index) {
            if (this.NameTable.ContainsKey(index))
                return (string)this.NameTable[index];
 
            return "";
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static string[] GetLanguageIds() {
            RegistryKey libraryParentKey = null;
            string[] ids = new string[0];
            new RegistryPermission(PermissionState.Unrestricted).Assert();
            try {
                libraryParentKey = Registry.LocalMachine.OpenSubKey(PerflibPath);
 
                if (libraryParentKey != null)
                    ids = libraryParentKey.GetSubKeyNames();
            }
            finally {
                if (libraryParentKey != null)
                    libraryParentKey.Close();
 
                RegistryPermission.RevertAssert();
            }
 
            return ids;
        }
 
        internal static PerformanceCounterLib GetPerformanceCounterLib(string machineName, CultureInfo culture) {
            SharedUtils.CheckEnvironment();
 
            string lcidString = culture.LCID.ToString("X3", CultureInfo.InvariantCulture);
            if (machineName.CompareTo(".") == 0)
                machineName = ComputerName.ToLower(CultureInfo.InvariantCulture);
            else
                machineName = machineName.ToLower(CultureInfo.InvariantCulture);
 
            if (PerformanceCounterLib.libraryTable == null) {
                lock (InternalSyncObject) {
                    if (PerformanceCounterLib.libraryTable == null)
                        PerformanceCounterLib.libraryTable = new Hashtable();
                }
            }
 
            string libraryKey = machineName + ":" + lcidString;
            if (PerformanceCounterLib.libraryTable.Contains(libraryKey))
                return (PerformanceCounterLib)PerformanceCounterLib.libraryTable[libraryKey];
            else {
                PerformanceCounterLib library = new PerformanceCounterLib(machineName, lcidString);
                PerformanceCounterLib.libraryTable[libraryKey] = library;
                return library;
            }
        }
 
        internal byte[] GetPerformanceData(string item) {
            if (this.performanceMonitor == null) {
                lock (InternalSyncObject) {
                    if (this.performanceMonitor == null)
                        this.performanceMonitor = new PerformanceMonitor(this.machineName);
                }
            }
 
           return this.performanceMonitor.GetData(item);
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private Hashtable GetStringTable(bool isHelp) {
            Hashtable stringTable;
            RegistryKey libraryKey;
 
            PermissionSet ps = new PermissionSet(PermissionState.None);
            ps.AddPermission(new RegistryPermission(PermissionState.Unrestricted));
            ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.UnmanagedCode));
            ps.Assert();
 
            if (String.Compare(this.machineName, ComputerName, StringComparison.OrdinalIgnoreCase) == 0)
                libraryKey = Registry.PerformanceData;
            else {
                libraryKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.PerformanceData, this.machineName);
            }
 
            try {
                string[] names = null;
                int waitRetries = 14;   //((2^13)-1)*10ms == approximately 1.4mins
                int waitSleep = 0;
 
                // In some stress situations, querying counter values from 
                // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009 
                // often returns null/empty data back. We should build fault-tolerance logic to 
                // make it more reliable because getting null back once doesn't necessarily mean 
                // that the data is corrupted, most of the time we would get the data just fine 
                // in subsequent tries.
                while (waitRetries > 0) {
                    try {
                        if (!isHelp)
                            names = (string[])libraryKey.GetValue("Counter " + perfLcid);
                        else
                            names = (string[])libraryKey.GetValue("Explain " + perfLcid);
 
                        if ((names == null) || (names.Length == 0)) {
                            --waitRetries;
                            if (waitSleep == 0)
                                waitSleep = 10;
                            else {
                                System.Threading.Thread.Sleep(waitSleep);
                                waitSleep *= 2;
                            }
                        }
                        else
                            break;
                    }
                    catch (IOException) {
                        // RegistryKey throws if it can't find the value.  We want to return an empty table
                        // and throw a different exception higher up the stack. 
                        names = null;
                        break;
                    }
                    catch (InvalidCastException) {
                        // Unable to cast object of type 'System.Byte[]' to type 'System.String[]'.
                        // this happens when the registry data store is corrupt and the type is not even REG_MULTI_SZ
                        names = null;
                        break;
                    }
                }
 
                if (names == null)
                    stringTable = new Hashtable();
                else {
                    stringTable = new Hashtable(names.Length/2);
                    
                    for (int index = 0; index < (names.Length/2); ++ index) {
                        string nameString =  names[(index *2) + 1];
                        if (nameString == null)
                            nameString = String.Empty;
                        
                        int key;
                        if (!Int32.TryParse(names[index * 2], NumberStyles.Integer, CultureInfo.InvariantCulture, out key)) {
                            if (isHelp) {
                                // Category Help Table
                                throw new InvalidOperationException(SR.GetString(SR.CategoryHelpCorrupt, names[index * 2]));
                            }
                            else {
                                // Counter Name Table 
                                throw new InvalidOperationException(SR.GetString(SR.CounterNameCorrupt, names[index * 2]));
                            }
                        }
 
                        stringTable[key] = nameString;
                    }
                }
            }
            finally {
                libraryKey.Close();
            }
 
            return stringTable;
        }
        
        internal static bool IsCustomCategory(string machine, string category) {
            PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            if (library.IsCustomCategory(category)) 
                return true;
 
            if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                CultureInfo culture = CultureInfo.CurrentCulture;
                while (culture != CultureInfo.InvariantCulture) {
                    library = GetPerformanceCounterLib(machine, culture);
                    if (library.IsCustomCategory(category))
                        return true;
                    culture = culture.Parent;
                }
            }
 
            return false;
        }
 
        internal static bool IsBaseCounter(int type) {
            return (type == NativeMethods.PERF_AVERAGE_BASE ||
                    type == NativeMethods.PERF_COUNTER_MULTI_BASE ||
                    type == NativeMethods.PERF_RAW_BASE  ||
                    type == NativeMethods.PERF_LARGE_RAW_BASE  ||
                    type == NativeMethods.PERF_SAMPLE_BASE);
        }
        
        private bool IsCustomCategory(string category) {
            PerformanceCounterCategoryType categoryType;
            
            return FindCustomCategory(category, out categoryType);
        }
 
        internal static PerformanceCounterCategoryType GetCategoryType(string machine, string category) {
            PerformanceCounterCategoryType categoryType = PerformanceCounterCategoryType.Unknown;
            
            PerformanceCounterLib library = GetPerformanceCounterLib(machine, new CultureInfo(EnglishLCID));
            if (!library.FindCustomCategory(category, out categoryType)) {
                if (CultureInfo.CurrentCulture.Parent.LCID != EnglishLCID) {
                    CultureInfo culture = CultureInfo.CurrentCulture;
                    while (culture != CultureInfo.InvariantCulture) {
                        library = GetPerformanceCounterLib(machine, culture);
                        if (library.FindCustomCategory(category, out categoryType))
                            return categoryType;
                        culture = culture.Parent;
                    }
                }
            }
            return categoryType;
        }
 
        /*
        // Creates a category with an ini file supplied by the user.  Currently this is not available.
        internal static void RegisterCategory(string machineName, string categoryName, string categoryHelp, CounterCreationDataCollection creationData, string localizedIniFilePath) {
            bool iniRegistered = false;
            CreateRegistryEntry(machineName, categoryName, creationData, ref iniRegistered);
            if (!iniRegistered)
                RegisterFiles(machineName, localizedIniFilePath, false);
 
            CloseAllTables();
        }
        */
 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        internal static void RegisterCategory(string categoryName, PerformanceCounterCategoryType categoryType, string categoryHelp, CounterCreationDataCollection creationData) {
            try {
                bool iniRegistered = false;
                CreateRegistryEntry(categoryName, categoryType, creationData, ref iniRegistered);
                if (!iniRegistered) {
                    string[] languageIds = GetLanguageIds();
                    CreateIniFile(categoryName, categoryHelp, creationData, languageIds);
                    CreateSymbolFile(creationData);
                    RegisterFiles(IniFilePath, false);
                }
                CloseAllTables();
                CloseAllLibraries();
            }
            finally {
                DeleteTemporaryFiles();
            }
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private static void RegisterFiles(string arg0, bool unregister) {
            Process p;
            ProcessStartInfo processStartInfo = new ProcessStartInfo();
            processStartInfo.UseShellExecute = false;
            processStartInfo.CreateNoWindow = true;
            processStartInfo.ErrorDialog = false;
            processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
            processStartInfo.WorkingDirectory = Environment.SystemDirectory;
 
            if (unregister)
                processStartInfo.FileName = Environment.SystemDirectory + "\\unlodctr.exe";
            else
                processStartInfo.FileName = Environment.SystemDirectory + "\\lodctr.exe";
 
            int res = 0;
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
            try {
                processStartInfo.Arguments = "\"" + arg0 + "\"";
                p = Process.Start(processStartInfo);
                p.WaitForExit();
 
                res = p.ExitCode;
            }
            finally {
                SecurityPermission.RevertAssert();
            }
            
 
            if (res == NativeMethods.ERROR_ACCESS_DENIED) {
                throw new UnauthorizedAccessException(SR.GetString(SR.CantChangeCategoryRegistration, arg0));
            }
 
            // Look at Q269225, unlodctr might return 2 when WMI is not installed.
            if (unregister && res == 2)
                res = 0;
 
            if (res != 0)
                throw SharedUtils.CreateSafeWin32Exception(res);
        }
 
        internal static void UnregisterCategory(string categoryName) {
            RegisterFiles(categoryName, true);
            DeleteRegistryEntry(categoryName);
            CloseAllTables();
            CloseAllLibraries();
        }
    }
 
    internal class PerformanceMonitor {
        private RegistryKey perfDataKey = null;
        private string machineName;
 
        internal PerformanceMonitor(string machineName) {
            this.machineName = machineName;
            Init();
        }
 
        [ResourceExposure(ResourceScope.None)]
        [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
        private void Init() {
            try {
                if (machineName != "." && String.Compare(machineName, PerformanceCounterLib.ComputerName, StringComparison.OrdinalIgnoreCase) != 0) {
                    new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
                    perfDataKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.PerformanceData, machineName);
                }
                else
                    perfDataKey = Registry.PerformanceData;
            }
            catch (UnauthorizedAccessException) {
                // we need to do this for compatibility with v1.1 and v1.0.
                throw new Win32Exception(NativeMethods.ERROR_ACCESS_DENIED);
            }
            catch (IOException e) {
                // we need to do this for compatibility with v1.1 and v1.0.
                throw new Win32Exception(Marshal.GetHRForException(e));
            }
        }
 
        internal void Close() {
            if( perfDataKey != null)
                perfDataKey.Close();
 
            perfDataKey = null;
        }
 
        // Win32 RegQueryValueEx for perf data could deadlock (for a Mutex) up to 2mins in some 
        // scenarios before they detect it and exit gracefully. In the mean time, ERROR_BUSY, 
        // ERROR_NOT_READY etc can be seen by other concurrent calls (which is the reason for the 
        // wait loop and switch case below). We want to wait most certainly more than a 2min window. 
        // The curent wait time of up to 10mins takes care of the known stress deadlock issues. In most 
        // cases we wouldn't wait for more than 2mins anyways but in worst cases how much ever time 
        // we wait may not be sufficient if the Win32 code keeps running into this deadlock again 
        // and again. A condition very rare but possible in theory. We would get back to the user 
        // in this case with InvalidOperationException after the wait time expires.
        internal byte[] GetData(string item) {
            int waitRetries = 17;   //2^16*10ms == approximately 10mins
            int waitSleep = 0;
            byte[] data = null;
            int error = 0;
 
            // no need to revert here since we'll fall off the end of the method
            new RegistryPermission(PermissionState.Unrestricted).Assert();
            while (waitRetries > 0) {
                try {
                    data = (byte[]) perfDataKey.GetValue(item);
                    return data;
                }
                catch (IOException e) {
                    error = Marshal.GetHRForException(e);
                    switch (error) {
                        case NativeMethods.RPC_S_CALL_FAILED:
                        case NativeMethods.ERROR_INVALID_HANDLE:
                        case NativeMethods.RPC_S_SERVER_UNAVAILABLE:
                            Init();
                            goto case NativeMethods.WAIT_TIMEOUT;
 
                        case NativeMethods.WAIT_TIMEOUT:
                        case NativeMethods.ERROR_NOT_READY:
                        case NativeMethods.ERROR_LOCK_FAILED:
                        case NativeMethods.ERROR_BUSY:
                            --waitRetries;
                            if (waitSleep == 0) {
                                waitSleep = 10;
                            }
                            else {
                                System.Threading.Thread.Sleep(waitSleep);
                                waitSleep *= 2;
                            }
                            break;
 
                        default:
                            throw SharedUtils.CreateSafeWin32Exception(error);
                    }
                }
                catch (InvalidCastException e) {
                    throw new InvalidOperationException(SR.GetString(SR.CounterDataCorrupt, perfDataKey.ToString()), e);
                }
            }
 
            throw SharedUtils.CreateSafeWin32Exception(error);
        }
 
    }
 
    internal class CategoryEntry {
        internal int NameIndex;
        internal int HelpIndex;
        internal int[] CounterIndexes;
        internal int[] HelpIndexes;
 
        internal CategoryEntry(NativeMethods.PERF_OBJECT_TYPE perfObject) {
            this.NameIndex = perfObject.ObjectNameTitleIndex;
            this.HelpIndex = perfObject.ObjectHelpTitleIndex;
            this.CounterIndexes = new int[perfObject.NumCounters];
            this.HelpIndexes = new int[perfObject.NumCounters];
        }
    }
 
    internal class CategorySample {
        internal readonly long SystemFrequency;
        internal readonly long TimeStamp;
        internal readonly long TimeStamp100nSec;
        internal readonly long CounterFrequency;
        internal readonly long CounterTimeStamp;
        internal Hashtable CounterTable;
        internal Hashtable InstanceNameTable;
        internal bool IsMultiInstance;
        private CategoryEntry entry;
        private PerformanceCounterLib library;
 
        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;
            }
        }
 
        internal CounterDefinitionSample GetCounterDefinitionSample(string counter) {
            for (int index = 0; index < this.entry.CounterIndexes.Length; ++ index) {
                int counterIndex = entry.CounterIndexes[index];
                string counterName = (string)this.library.NameTable[counterIndex];
                if (counterName != null) {
                    if (String.Compare(counterName, counter, StringComparison.OrdinalIgnoreCase) == 0) {
                        CounterDefinitionSample sample = (CounterDefinitionSample)this.CounterTable[counterIndex];
                        if (sample == null) {
                            //This is a base counter and has not been added to the table
                            foreach (CounterDefinitionSample multiSample in this.CounterTable.Values) {
                                if (multiSample.BaseCounterDefinitionSample != null &&
                                    multiSample.BaseCounterDefinitionSample.NameIndex == counterIndex)
                                    return multiSample.BaseCounterDefinitionSample;
                            }
 
                            throw new InvalidOperationException(SR.GetString(SR.CounterLayout));
                        }
                        return sample;
                    }
                }
            }
 
            throw new InvalidOperationException(SR.GetString(SR.CantReadCounter, counter));
        }
 
        internal InstanceDataCollectionCollection ReadCategory() {
 
#pragma warning disable 618
            InstanceDataCollectionCollection data = new InstanceDataCollectionCollection();
#pragma warning restore 618
            for (int index = 0; index < this.entry.CounterIndexes.Length; ++ index) {
                int counterIndex = entry.CounterIndexes[index];
 
                string name = (string)library.NameTable[counterIndex];
                if (name != null && name != String.Empty) {
                    CounterDefinitionSample sample = (CounterDefinitionSample)this.CounterTable[counterIndex];
                    if (sample != null)
                        //If the current index refers to a counter base,
                        //the sample will be null
                        data.Add(name, sample.ReadInstanceData(name));
                }
            }
 
            return data;
        }
    }
 
    internal class CounterDefinitionSample {
        internal readonly int NameIndex;
        internal readonly int CounterType;
        internal CounterDefinitionSample BaseCounterDefinitionSample;
 
        private readonly int size;
        private readonly int offset;
        private long[] instanceValues;
        private CategorySample categorySample;
 
        internal CounterDefinitionSample(NativeMethods.PERF_COUNTER_DEFINITION perfCounter, CategorySample categorySample, int instanceNumber) {
            this.NameIndex = perfCounter.CounterNameTitleIndex;
            this.CounterType = perfCounter.CounterType;
            this.offset = perfCounter.CounterOffset;
            this.size = perfCounter.CounterSize;
            if (instanceNumber == -1) {
                this.instanceValues = new long[1];
            }
            else
                this.instanceValues = new long[instanceNumber];
 
            this.categorySample = categorySample;
        }
 
        private long ReadValue(IntPtr pointer) {
            if (this.size == 4) {
                return (long)(uint)Marshal.ReadInt32((IntPtr)((long)pointer + this.offset));
            }
            else if (this.size == 8) {
                return (long)Marshal.ReadInt64((IntPtr)((long)pointer + this.offset));
            }
 
            return -1;
        }
 
        internal CounterSample GetInstanceValue(string instanceName) {
 
            if (!categorySample.InstanceNameTable.ContainsKey(instanceName)) { 
                // Our native dll truncates instance names to 128 characters.  If we can't find the instance
                // with the full name, try truncating to 128 characters. 
                if (instanceName.Length > SharedPerformanceCounter.InstanceNameMaxLength)
                    instanceName = instanceName.Substring(0, SharedPerformanceCounter.InstanceNameMaxLength);
                
                if (!categorySample.InstanceNameTable.ContainsKey(instanceName))
                    throw new InvalidOperationException(SR.GetString(SR.CantReadInstance, instanceName));
            }
 
            int index = (int)categorySample.InstanceNameTable[instanceName];
            long rawValue = this.instanceValues[index];
            long baseValue = 0;
            if (this.BaseCounterDefinitionSample != null) {
                CategorySample baseCategorySample = this.BaseCounterDefinitionSample.categorySample;
                int baseIndex = (int)baseCategorySample.InstanceNameTable[instanceName];
                baseValue = this.BaseCounterDefinitionSample.instanceValues[baseIndex];
            }
 
            return new CounterSample(rawValue,
                                                        baseValue,
                                                        categorySample.CounterFrequency,
                                                        categorySample.SystemFrequency,
                                                        categorySample.TimeStamp,
                                                        categorySample.TimeStamp100nSec,
                                                        (PerformanceCounterType)this.CounterType,
                                                        categorySample.CounterTimeStamp);
 
        }
 
        internal InstanceDataCollection ReadInstanceData(string counterName) {
#pragma warning disable 618
            InstanceDataCollection data = new InstanceDataCollection(counterName);
#pragma warning restore 618
 
            string[] keys = new string[categorySample.InstanceNameTable.Count];
            categorySample.InstanceNameTable.Keys.CopyTo(keys, 0);
            int[] indexes = new int[categorySample.InstanceNameTable.Count];
            categorySample.InstanceNameTable.Values.CopyTo(indexes, 0);
            for (int index = 0; index < keys.Length; ++ index) {
                long baseValue = 0;
                if (this.BaseCounterDefinitionSample != null) {
                    CategorySample baseCategorySample = this.BaseCounterDefinitionSample.categorySample;
                    int baseIndex = (int)baseCategorySample.InstanceNameTable[keys[index]];
                    baseValue = this.BaseCounterDefinitionSample.instanceValues[baseIndex];
                }
 
                CounterSample sample = new CounterSample(this.instanceValues[indexes[index]],
                                                        baseValue,
                                                        categorySample.CounterFrequency,
                                                        categorySample.SystemFrequency,
                                                        categorySample.TimeStamp,
                                                        categorySample.TimeStamp100nSec,
                                                        (PerformanceCounterType)this.CounterType,
                                                        categorySample.CounterTimeStamp);
 
                data.Add(keys[index], new InstanceData(keys[index], sample));
            }
 
            return data;
        }
 
        internal CounterSample GetSingleValue() {
            long rawValue = this.instanceValues[0];
            long baseValue = 0;
            if (this.BaseCounterDefinitionSample != null)
                baseValue = this.BaseCounterDefinitionSample.instanceValues[0];
 
            return new CounterSample(rawValue,
                                                        baseValue,
                                                        categorySample.CounterFrequency,
                                                        categorySample.SystemFrequency,
                                                        categorySample.TimeStamp,
                                                        categorySample.TimeStamp100nSec,
                                                        (PerformanceCounterType)this.CounterType,
                                                        categorySample.CounterTimeStamp);
        }
 
        internal void SetInstanceValue(int index, IntPtr dataRef) {
            long rawValue = ReadValue(dataRef);
            this.instanceValues[index] = rawValue;
        }
    }
}