Spis treści:

Kategoria:C#PowerShell


Pobieranie informacji o procesorze

Wysokopoziomowy dostęp do niskopoziomowych informacji

Na egzaminie na prawo jazdy egzaminator pyta podstępnie:
- Co pani zrobi, jeżeli po przejechaniu pięciu kilometrów stwierdzi pani, że zapomniała pani kluczyków?
- Zjadę na pobocze, włączę światła awaryjne, wysiądę z samochodu i sprawdzę, co za matoł pchał mnie taki kawał drogi.

Dawno, dawno temu, za siedmioma górami, za siedmioma morzami, lasami, bagnami i pustyniami pozbawionymi jakiejkolwiek roślinności, byli sobie programiści piszący w asemblerze. Ewolucja sprawiła, że powstał C. Widzieli programiści, że było to dobre i pisze się łatwiej. Zapragnęli czegoś więcej. W trudzie i mozole powstały języki obiektowe. Znów programiści zauważyli, że jest to dobre i postanowili iść dalej tą ścieżką. Majstrowali, kombinowali, myśleli, spożywali hektolitry kawy i wymyślili - .NET. Powstał z miłości do zmniejszania i upraszczania sobie pracy. Z każdym takim etapem ewolucji programiści oddalali się od sprzętu, tracili z nim kontakt posługując się pośrednikiem w postaci maszyny wirtualnej. Ten sprzęt, ten zwykły pracownik, ten robotnik, wykonujący czarną robotę, ten, na którego ta praca tak czy inaczej spływała, stał się obcy.

Tak jak w życiu, zanikają pewne zawody, na które znika popyt. Trudno dziś spotkać zduna, skotnika, łagiewnika, korabnika. Do tego stopnia, że nawet sprawdzanie pisowni podkreśla niektóre z tych wyrazów. Podobnie jest z tymi, którzy na domowych komputerach dobierają się do sprzętu za pomocą instrukcji maszynowych. Większość tego robić nie musi.

Moduły informacyjne WMI w C#

System Windows wyposażony jest w wiele różnych bibliotek, modułów i komponentów. Te moduły tworzą swego rodzaju rozszerzenia w systemie operacyjnym. Jednym z nich jest WMI (ang. Windows Management Instrumentation), który służy jako warstwa informacyjna i umożliwia automatyzację wielu zadań. Dziś zajmę się tylko pobieraniem informacji, odkładając automatyzację na później. Obsługa klas WMI na podstawowym poziomie nie jest, wbrew pozorom, skomplikowana. Popatrzmy na przykład:

//Przestrzeń obiektów WMI
ManagementScope scope = new ManagementScope("root\\CIMV2");
//Zapytanie pobierające informacje o obiekcie
SelectQuery query = new SelectQuery("SELECT ProcessorID, Name FROM Win32_Processor");
//Klasa przetwarzająca zapytanie
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
                    
foreach (var processor in searcher.Get())
{
    Console.WriteLine("{0} (ID={1})", processor["Name"], processor["ProcessorID"]);
}

Nie jest to najprostsze wywołanie i może być trudne do zapamiętania. Za chwilę postaram się wszystko wyjaśnić. Na ten moment przyjrzyjmy się wynikom uzyskanym po wykonaniu takiego fragmentu kodu:

Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz (ID=BFEBFBFF000306A9)

Przyjrzyjmy się teraz poszczególnym linijkom kodu w szczegółach.

Przestrzeń nazw komponentu WMI

W pokazanym przykładzie można zauważyć pewien magiczny ciąg znaków w postaci root\\CIMV2. Co to oznacza i skąd się to bierze? WMI korzysta ze standardowego mechanizmu zabezpieczeń obiektów i weryfikuje te zabezpieczenia w oparciu właśnie o wspomniane przestrzenie nazw. Użytkownik wywołujący komponent WMI przesyła swój token, rozumiany w uproszczeniu jako poświadczenia, a system, w oparciu o kontrolę list dostępu, pozwala na wykonanie operacji lub odrzuca żądanie. Trzeba wiedzieć, że dostęp do komponentów WMI możliwy jest również z poziomu innego systemu, innego komputera, zdalnie. O ile w przypadku własnego komputera zabezpieczenia można w większości przypadków ignorować, wywołania z innych maszyn lepiej śledzić i kontrolować. Przestrzenie nazw definiuje się dla komponentu. Dla potrzeb przykładu wystarczy zapamiętać, że większość podstawowych komponentów znajduje się w przestrzeni root\\CIMV2. Nazwa przestrzeni jest też zawsze podawana w dokumentacji komponentu.

Zapytanie obiektowe WQL

Powszechna znajomość języka SQL wśród projektantów systemu WMI odcisnęła piętno na sposobie pobierania danych z tychże komponentów. Składnia jest bardzo zbliżona do podstawowej, obecnej i używanej w serwerach baz danych od dawna. Możemy korzystać z instrukcji SELECT, FROM, nakładać warunki w sekcji WHERE stosując różne operatory logiczne, operator LIKE. Można nawet grupować wyniki korzystając z operatora GROUP, ale jego działanie może trochę mylić przeciętnego znawcę tradycyjnego języka SQL. W pokazanym przykładzie wybieramy tylko dwa atrybuty z komponentu, który w maszynach jednoprocesorowych zwróci jeden rezultat. Można oczywiście użyć operatora * i pobrać wszystkie dostępne informacje, ale będzie to miało swoje konsekwencje w prędkości pobierania danych. Tak jak w SQL, tak i tutaj, w WQL, należy pobierać tylko te wartości, których potrzebujemy.

Iteracja po wynikach zapytania

W klasycznym ADO.NET do odczytywania rekordów zwracanych z SQL Server wykorzystywało się klasę SqlDataReader. Obiekt ten pozwalał na sekwencyjne przetwarzanie strumienia danych pobranych z bazy. W WMI odpowiednikiem takiej klasy jest ManagementObjectSearcher. W pokazanym przykładzie przyjmuje on w postaci argumentów obiekty reprezentujące przestrzeń nazw oraz zapytanie. Najważniejsze jest jednak wywołanie metody Get(), która zwraca kolekcję wyników. Dostęp do poszczególnych wartości (atrybutów) odbywa się poprzez indekser - nazwę atrybutu przekazujemy w postaci tekstowej. Lista wszystkich atrybutów oraz ich wartości dostępna jest również przez zwykłą właściwość Properties.

Metadane - jakie klasy WMI związane z procesorem są dostępne

Sam obsługa WMI nie jest tak trudna, jak odnalezienie odpowiednich klas, które mogą zawierać pożądane przez nas dane. Jednym ze sposobów uzyskania tego typu informacji jest przeglądnięcie bogatej dokumentacji. Nie muszę chyba przekonywać, że jest to żmudne i czasochłonne zadanie. Na szczęście, znów wzorem baz danych, istnieją odpowiednie wirtualne widoki. Mam tu na myśli takie obiekty, które zawierają informacje o innych obiektach. Tak jak w SQL Server możemy odpytać widok sys.tables i pobrać dany o wszystkich tabelach w bazie, tak w WMI możemy odpytać obiekt meta_class i pobrać wszystkie komponenty w przestrzeni nazw. W pokazanym poniżej przykładzie nałożony został odpowiedni filtr - wszystko po to, aby zawęzić listę obiektów tylko do tych, które mają w swojej nazwie słowo processor.

ManagementScope scope = new ManagementScope("root\\CIMV2");
SelectQuery query = new SelectQuery("SELECT * FROM meta_class WHERE __CLASS LIKE '%processor%'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
foreach (var processor in searcher.Get())
{
    Console.WriteLine("{0}\r\n    {1}", processor["__CLASS"], processor);
}

Po wykonaniu się tego fragmentu kodu na ekranie mogą wyświetlić się dane podobne do pokazanych poniżej:

CIM_Processor
    \\USR\ROOT\CIMV2:CIM_Processor
Win32_Processor
    \\USR\ROOT\CIMV2:Win32_Processor
Win32_ComputerSystemProcessor
    \\USR\ROOT\CIMV2:Win32_ComputerSystemProcessor
CIM_AssociatedProcessorMemory
    \\USR\ROOT\CIMV2:CIM_AssociatedProcessorMemory
Win32_AssociatedProcessorMemory
    \\USR\ROOT\CIMV2:Win32_AssociatedProcessorMemory
Win32_PerfFormattedData_Counters_PerProcessorNetworkActivityCycles
    \\USR\root\CIMV2:Win32_PerfFormattedData_Counters_PerProcessorNetworkActivityCycles
Win32_PerfRawData_Counters_PerProcessorNetworkActivityCycles
    \\USR\root\CIMV2:Win32_PerfRawData_Counters_PerProcessorNetworkActivityCycles
Win32_PerfFormattedData_Counters_PerProcessorNetworkInterfaceCardActivity
    \\USR\root\CIMV2:Win32_PerfFormattedData_Counters_PerProcessorNetworkInterfaceCardActivity
Win32_PerfRawData_Counters_PerProcessorNetworkInterfaceCardActivity
    \\USR\root\CIMV2:Win32_PerfRawData_Counters_PerProcessorNetworkInterfaceCardActivity
Win32_PerfFormattedData_Counters_ProcessorInformation
    \\USR\root\CIMV2:Win32_PerfFormattedData_Counters_ProcessorInformation
Win32_PerfRawData_Counters_ProcessorInformation
    \\USR\root\CIMV2:Win32_PerfRawData_Counters_ProcessorInformation
Win32_PerfFormattedData_PerfOS_Processor
    \\USR\root\CIMV2:Win32_PerfFormattedData_PerfOS_Processor
Win32_PerfRawData_PerfOS_Processor
    \\USR\root\CIMV2:Win32_PerfRawData_PerfOS_Processor

Wszystkich klas jest dużo i, o ile mi wiadomo, nie istnieje jakaś oficjalna lista. Tym bardziej, że taką klasę można napisać samemu! Być może kiedyś zajmę się tym zagadnieniem, ale z pewnością w oddzielnym wpisie. Wspomniałem wcześniej, że większość klas z informacjami sprzętowymi znajduje się w przestrzeni root\\CIMV2. Co z innymi przestrzeniami? Gdzie one są i jak się nazywają?

Metadane o przestrzeniach

Pobieranie metadanych o przestrzeniach jest równie proste, co pobieranie informacji o samych komponentach. Tym razem sięgamy do widoku __NAMESPACE w przestrzeni root. Kod będzie wyglądał następująco:

ManagementScope scope = new ManagementScope("root");
SelectQuery query = new SelectQuery("SELECT * FROM __NAMESPACE");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
foreach (var processor in searcher.Get())
{
    Console.WriteLine("{0}", processor["Name"]);
}

Tym razem, wykonując kod, moglibyśmy otrzymać na przykład takie rezultaty:

subscription
DEFAULT
CIMV2
Cli
nap
SECURITY
SecurityCenter2
RSOP
WebAdministration
WMI
directory
Policy
Interop
ServiceModel
SecurityCenter
Microsoft
aspnet

Wyposażeni w takie informacje możemy z łatwością uzyskać listę wszystkich komponentów z wszystkich przestrzeni nazw.

PowerShell jako pomoc programisty

Operacje, które w poprzednich częściach wykonywane były w .NET, można wykonać bezpośrednio w oknie PowerShell. Popatrzmy na przykładowy skrypt pobierający obiekty z processor w nazwie:

PS C:\> Get-WmiObject -namespace "root\CIMV2"
  -Query "SELECT * FROM meta_class WHERE __CLASS LIKE '%processor%'"
  | Format-Table -Property __CLASS

__CLASS
-------
CIM_Processor
Win32_Processor
Win32_ComputerSystemProcessor
CIM_AssociatedProcessorMemory
Win32_AssociatedProcessorMemory
Win32_PerfFormattedData_Counters_PerProcessorNetworkActivityCycles
Win32_PerfRawData_Counters_PerProcessorNetworkActivityCycles
Win32_PerfFormattedData_Counters_PerProcessorNetworkInterfaceCardActivity
Win32_PerfRawData_Counters_PerProcessorNetworkInterfaceCardActivity
Win32_PerfFormattedData_Counters_ProcessorInformation
Win32_PerfRawData_Counters_ProcessorInformation
Win32_PerfFormattedData_PerfOS_Processor
Win32_PerfRawData_PerfOS_Processor

Bardzo podobnie będzie wyglądało zapytanie pobierające przestrzenie:

PS C:\> Get-WmiObject -namespace "root"
  -Query "SELECT * FROM __NAMESPACE"
  | Format-Table -Property Name

Name
----
subscription
DEFAULT
CIMV2
Cli
nap
SECURITY
SecurityCenter2
RSOP
WebAdministration
WMI
directory
Policy
Interop
ServiceModel
SecurityCenter
Microsoft
aspnet

Samo pobieranie informacji o procesorze to już formalność:

PS C:\> Get-WmiObject -namespace "root/CIMV2"
  -Query "SELECT ProcessorID, Name FROM Win32_Processor"
  | Format-Table -Property Name, ProcessorID

Name                                                        ProcessorID
----                                                        -----------
Intel(R) Core(TM) i7-3630QM CPU @ 2.40GHz                   BFEBFBFF000306A9

Podsumowanie

Zacząłem od historii, asemblera, bezpośredniego kontaktu ze sprzętem. Bezpośredni kontakt może jednych przerażać, u drugich natomiast przywoływać piękne wspomnienia starych, pięknych czasów. Czasów, które pewnie już nie wrócą. Analiza artykułu tylko to potwierdza. Tak jak szkutnictwo, rzemiosło zajmujące się ręczną budową łodzi, stało się mocno niszowe, tak i programowanie niskopoziomowe komputerów domowych do podobnej niszy ucieka. Zajmują się tym już chyba tylko prawdziwi hobbyści lub garstka specjalistów. Dzięki tej garstce osób, cała reszta może się radować. Radować i tworzyć nowe, jeszcze lepsze rzeczy. Tego wszystkim i sobie życzę.

Kategoria:C#PowerShell

, 2013-12-20

Brak komentarzy - bądź pierwszy

Dodaj komentarz
Wyślij
Ostatnie komentarze
Dzieki za te informacje. były kluczowe
Dobrze wyjaśnione, dzięki !
a z innej strony - co gdybym ciąg znaków chciał mieć rozbity nie na wiersze a na kolumny? Czyli ciąg ABCD: 1. kolumna: A, 2. kolumna: B, 3. kolumna: C, 4 kolumna: D?