2015-11-19 11 views
5

voglio accedere al "% Tempo processore" contatore in un'applicazione che gira su sistemi con diverse localizzazioni.Get PerformanceCounter da Index

Per farlo, voglio accedere al banco con il suo indice, che è garantito per essere unico (vedi https://support.microsoft.com/en-us/kb/287159).

Il seguente codice funziona e mi dà il risultato corretto per il locale corrente, ma per aprire il contatore delle prestazioni ho anche bisogno nome della categoria del contatore (vedi costruttori per la classe PerformanceCounter), così come il nome dell'istanza:

[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??? */); 
} 

Come posso ottenere quella categoria e l'istanza nome?

Vedere anche: Retrieve performance counter value in a language-independent way, che descrive lo stesso problema ma non fornisce una soluzione.

risposta

6

Si interpreta erroneamente come funziona PdhLookupPerfNameByIndex(). Il suo compito non è quello di mappa un contatore delle prestazioni, ma per mappare una stringa. Dovrebbe essere usato sia per la categoria del contatore sia per il suo nome. Non per l'istanza del contatore, se applicabile, non è localizzato.

miglior modo per vedere ciò che fa è quello di utilizzare Regedit.exe. Passare a HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ Perflib. Nota il tasto "009", il suo valore Counter ha l'indice per la mappatura delle stringhe in inglese. Fai doppio clic su Contatore e copia e incolla il contenuto della scatola in un editor di testo per avere un aspetto migliore. La chiave "CurrentLanguage" ha lo stesso mapping ma utilizza i nomi localizzati.

Quindi PdhLookupPerfNameByIndex() utilizza la chiave CurrentLanguage, utilizzare l'elenco ottenuto nel passaggio precedente per conoscere il numero di indice della stringa. L'altro modo per farlo come indicato (in modo confuso) nella parte inferiore dell'articolo della Knowledge Base è innanzitutto la ricerca del numero di indice dalla chiave di registro "009". Questo ti permette di tradurre dalla stringa inglese alla stringa localizzata. Si noti che l'articolo della Knowledge Base riporta la posizione della chiave del Registro di sistema errata, nessuna idea del perché.

Ricordare che non è perfetto, come indicato nell'articolo della Knowledge Base, queste mappature esistono solo per i contatori "di base" e il tasto "009" è ambiguo perché alcuni indici si associano alla stessa stringa. Il test su una versione localizzata di Windows è molto importante.

Alcuni codice che fa entrambe le cose: l'utilizzo

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); 
} 

Esempio:

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

ho solo l'accesso a una versione inglese di Windows in modo non può garantire per la precisione in una versione localizzata . Correggi qualsiasi bug che incontri modificando questo post.

0

provare questo:

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

Funziona per me sul PC con la localizzazione tedesca.

UPDATE

Questo è un esempio si potrebbe utilizzare per capire come sono organizzate queste categorie, istanze e contatori. Non dimenticare di controllare il Performance Monitor sotto Strumenti di amministrazione nel pannello di controllo in cui è possibile aggiungere un contatore o trovare contatori esistenti.

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

Sembra che il passaggio dei nomi inglesi funzioni la maggior parte del tempo (anche testato su sistemi tedeschi). Ma avevamo alcuni sistemi svizzeri che non erano in grado di trovare quei segnalini inglesi. – thomai