Get a single-instance PerformanceCounter
Each counter has its own category and name (sometimes also an instance name). To read performance data, we can create an instance like this:
var counter = new PerformanceCounter("IPv4", "Datagrams/sec");
Unfortunately those names in parameters depend on system language. However, there is a way to get the PerformanceCounter knowing only the English name.
Get localized names
WinAPI function PdhLookupPerfNameByIndex gets a localized name, although we need to know first what’s the PerformanceCounter’s ID (which can be different on each computer).
Fortunately the list with English names and IDs is stored in registry value:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009\Counter
It looks like this:
ID ENGLISH_NAME ID ENGLISH_NAME ...
One more issue
While listing counters names I noticed, that for some categories they are localized and for some they are in English. This means we can’t rely on localized names – just in case we have to check all possible combinations of localized and non-localized names.
Source code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
[DllImport("pdh.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern UInt32 PdhLookupPerfNameByIndex(string szMachineName, uint dwNameIndex, StringBuilder szNameBuffer, ref uint pcchNameBufferSize); PerformanceCounter GetSingleInstanceCounter(string englishCategoryName, string englishCounterName) { // Try first with english names try { return new PerformanceCounter(englishCategoryName, englishCounterName); } catch { } // Get list of counters const string perfCountersKey = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009"; var englishNames = Registry.GetValue(perfCountersKey, "Counter", null) as string[]; // Get localized category name var localizedCategoryId = FindNameId(englishNames, englishCategoryName); var localizedCategoryName = GetNameByIndex(localizedCategoryId); // Get localized counter name var localizedCounterId = FindNameId(englishNames, englishCounterName); var localizedCounterName = GetNameByIndex(localizedCounterId); return GetCounterIfExists(localizedCategoryName, localizedCounterName) ?? GetCounterIfExists(localizedCategoryName, englishCounterName) ?? GetCounterIfExists(englishCategoryName, localizedCounterName); } PerformanceCounter GetCounterIfExists(string categoryName, string counterName) { try { return new PerformanceCounter(categoryName, counterName); } catch { return null; } } int FindNameId(string[] englishNames, string name) { // englishNames contains alternately id and name, that's why I check only odd lines for (int i = 1; i < englishNames.Length; i += 2) { if (englishNames[i] == name) { return Int32.Parse(englishNames[i - 1]); } } return -1; } string GetNameByIndex(int id) { if (id < 0) { return null; } var buffer = new StringBuilder(1024); var bufSize = (uint)buffer.Capacity; var ret = PdhLookupPerfNameByIndex(null, (uint)id, buffer, ref bufSize); return ret == 0 && buffer.Length != 0 ? buffer.ToString() : null; } |
More information: MSDN – Using PDH APIs correctly in a localized language