2015-11-19 6 views
5

Я хочу получить доступ к счетчику «Processor Time%» в приложении, которое работает в системах с различными локализациями.Получить PerformanceCounter по индексу

Для этого я хочу получить доступ к счетчику по его индексу, который гарантированно будет уникальным (см. https://support.microsoft.com/en-us/kb/287159).

Следующий код работает и дает мне правильный результат для данной местности, но, чтобы открыть счетчик производительности я также нужно имя категории счетчика (см конструкторов для PerformanceCounter класса), а также имя экземпляра:

[DllImport("pdh.dll", SetLastError = true, CharSet = CharSet.Unicode)] 
internal static extern UInt32 PdhLookupPerfNameByIndex(string szMachineName, uint dwNameIndex, StringBuilder szNameBuffer, ref uint pcchNameBufferSize); 

void Main() 
{ 
    var buffer = new StringBuilder(1024); 
    var bufSize = (uint)buffer.Capacity; 
    PdhLookupPerfNameByIndex(null, 6, buffer, ref bufSize); 
    Console.WriteLine(buffer.ToString()); 

    var counter = new PerformanceCounter(/* category??? */, buffer.ToString(), /* instance??? */); 
} 

Как я могу получить эту категорию и имя экземпляра?

См. Также: Retrieve performance counter value in a language-independent way, что описывает ту же проблему, но не дает решения.

ответ

6

Вы неверно истолковываете, как работает PdhLookupPerfNameByIndex(). Его задача заключается не в том, чтобы сопоставить счетчик производительности, а с отображением строки . Он должен использоваться как для категории счетчика, так и для его имени. Не для экземпляра счетчика, если применимо, он не локализован.

Лучший способ увидеть, что он делает, используя Regedit.exe. Перейдите в HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Perflib. Обратите внимание на клавишу «009», ее значение «Счетчик» имеет индекс для сопоставления строк на английском языке. Дважды нажмите «Счетчик» и скопируйте содержимое окна в текстовый редактор, чтобы лучше видеть. Ключ «CurrentLanguage» является одним и тем же отображением, но использует локализованные имена.

Так что PdhLookupPerfNameByIndex() использует ключ CurrentLanguage, используйте список, полученный на предыдущем шаге, чтобы узнать номер индекса строки. Другой способ сделать это, как указано (в замешательстве) в нижней части статьи KB, - сначала посмотреть номер индекса из раздела реестра «009». Это позволяет перевести с английской строки на локализованную строку. Обратите внимание, что статья в КБ неправильно документирует расположение раздела реестра, не знаю, почему.

Помните, что это не так идеально, как указано в статье в KB, эти сопоставления существуют только для «базовых» счетчиков, а ключ «009» неоднозначен, потому что некоторые индексы относятся к одной и той же строке. Тестирование на локализованной версии Windows очень важно.

Некоторый код, который делает это в обоих направлениях: использование

using System; 
using System.Collections.Generic; 
using System.Text; 
using Microsoft.Win32; 
using System.Diagnostics; 
using System.Runtime.InteropServices; 

public static class PerfMapper { 
    private static Dictionary<string, int> English; 
    private static Dictionary<int, string> Localized; 

    public static PerformanceCounter FromEnglish(string category, string name, string instance = null) { 
     return new PerformanceCounter(Map(category), Map(name), instance); 
    } 

    public static PerformanceCounter FromIndices(int category, int name, string instance = null) { 
     return new PerformanceCounter(PdhMap(category), PdhMap(name), instance); 
    } 

    public static bool HasName(string name) { 
     if (English == null) LoadNames(); 
     if (!English.ContainsKey(name)) return false; 
     var index = English[name]; 
     return !Localized.ContainsKey(index); 
    } 

    public static string Map(string text) { 
     if (HasName(text)) return Localized[English[text]]; 
     else return text; 
    } 

    private static string PdhMap(int index) { 
     int size = 0; 
     uint ret = PdhLookupPerfNameByIndex(null, index, null, ref size); 
     if (ret == 0x800007D2) { 
      var buffer = new StringBuilder(size); 
      ret = PdhLookupPerfNameByIndex(null, index, buffer, ref size); 
      if (ret == 0) return buffer.ToString(); 
     } 
     throw new System.ComponentModel.Win32Exception((int)ret, "PDH lookup failed"); 
    } 

    private static void LoadNames() { 
     string[] english; 
     string[] local; 
     // Retrieve English and localized strings 
     using (var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64)) { 
      using (var key = hklm.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009")) { 
       english = (string[])key.GetValue("Counter"); 
      } 
      using (var key = hklm.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage")) { 
       local = (string[])key.GetValue("Counter"); 
      } 
     } 
     // Create English lookup table 
     English = new Dictionary<string, int>(english.Length/2, StringComparer.InvariantCultureIgnoreCase); 
     for (int ix = 0; ix < english.Length - 1; ix += 2) { 
      int index = int.Parse(english[ix]); 
      if (!English.ContainsKey(english[ix + 1])) English.Add(english[ix + 1], index); 
     } 
     // Create localized lookup table 
     Localized = new Dictionary<int, string>(local.Length/2); 
     for (int ix = 0; ix < local.Length - 1; ix += 2) { 
      int index = int.Parse(local[ix]); 
      Localized.Add(index, local[ix + 1]); 
     } 
    } 

    [DllImport("pdh.dll", CharSet = CharSet.Auto)] 
    private static extern uint PdhLookupPerfNameByIndex(string machine, int index, StringBuilder buffer, ref int bufsize); 
} 

Пример:

class Program { 
    static void Main(string[] args) { 
     var ctr1 = PerfMapper.FromEnglish("Processor", "% Processor Time"); 
     var ctr2 = PerfMapper.FromIndices(238, 6); 
    } 
} 

Я только иметь доступ к английской версии Windows, поэтому он не может ручаться за точность на локализованной версии , Пожалуйста, исправьте все ошибки, с которыми вы сталкиваетесь, отредактировав это сообщение.

0

Попробуйте это:

var counter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); 

Это работает для меня на ПК с немецкой локализации.

UPDATE

Это пример, который вы могли бы использовать, чтобы понять, как организованы эти категории, экземпляры и счетчики. Не забывайте проверять Монитор производительности под Административные инструменты в Панель управления где вы можете добавить счетчик или найти существующие счетчики.

string counterName = buffer.ToString(); 
PerformanceCounter counter = null;    
foreach (var category in PerformanceCounterCategory.GetCategories()) 
{ 
    // Get all possible instances for the current category 
    var instanceNames = category.GetInstanceNames(); 
    if (instanceNames.Length == 0) 
     continue; 
    // Get all counters in the category. 
    // We want to find an instance with underscores first, for example, "_Total"    
    var counters = category.GetCounters(
     category.GetInstanceNames().OrderBy(i => i).First()); 
    foreach (var currentCounter in counters) 
    {           
     if (currentCounter.CounterName == counterName) 
     { 
      // Hurray! Here it is! 
      counter = currentCounter; 
     } 
    } 
} 
+0

Кажется, что прохождение английских имен работает большую часть времени (также проверено на немецких системах). Но у нас были некоторые швейцарские системы, которые не смогли найти эти английские счетчики. – thomai