Hey, Scripting Guy!Erkunden der Tiefen von WMI

Die Scripting Guys von Microsoft

Inhalt

Namespaces
WMI-Klassen
Eigenschaften
Methoden

Als einer der Scripting Guys jünger war, hatte er zwei Lieblingshobbys. Eines davon war das Trinken fermentierter Getränke und das andere Wintercamping. Er vermutet, dass beides zusammenhing. Sein Begleiter aus dieser Zeit schlug gern vor, „tief in die Materie einzutauchen“. In diesem Monat werden wir nun dem Rat des alten Freunds folgen. Wir werden tief in die Materie von WMI (Windows Management Instrumentation, Windows-Verwaltungsinstrumentation) eintauchen.

Glücklicherweise müssen wir in unserem Fall nirgendwohin wandern, außer vielleicht zur Kaffeemaschine und zurück. Stattdessen werden wir die Tiefen von WMI mithilfe von Skripts erkunden.

Die Scripting Guys sind bekannt dafür, sehr praktisch veranlagt zu sein. Wir versuchen, Lösungen für tatsächliche Probleme vorzustellen und nicht nur saloppe Erklärungen, die dem Leser nicht mitteilen, wie das Problem eigentlich behoben werden kann. Wir werden nicht auf diesen Zug aufspringen. Auch wenn sich dieser Artikel nicht auf das Ausführen bestimmter Systemverwaltungsaufgaben konzentriert, hat er doch ein praktisches Ziel. Das Hauptziel besteht darin, Sie über die WMI-Infrastruktur aufzuklären. Außerdem möchten wir Ihnen einige nützliche Untersuchungsskripts zur Verfügung stellen. Starten Sie Ihren Editor – wir sind bereit, die Tiefen von WMI zu erkunden!

Namespaces

Beim WMI-Repository handelt es sich um eine Datenbank, die für die Speicherung des Common Information Model (CIM) verwendet wird. Das Modell ist objektorientiert, was bedeutet, dass es einen Satz an Beschreibungen (WMI-Klassen) enthält, die das repräsentieren, was von WMI verwaltet werden kann. Die WMI-Klasse „Win32_Process“ repräsentiert z. B. Prozesse. Die WMI-Klassen werden in verschiedenen Abschnitten des WMI-Repositorys gespeichert. Ein Abschnitt des WMI-Repositorys ist als Namespace bekannt. Bei einem ersten Blick auf ein WMI-Repository sehen Sie, dass es in zahlreiche grundlegende Namespaces aufgeteilt ist. Das Skript in Abbildung 1

Abbildung 1 Anzeigen von Namespaces

strComputer = "."
Call EnumNameSpaces("root")

Sub EnumNameSpaces(strNameSpace)
    On Error Resume Next
    WScript.Echo strNameSpace
    Set objWMIService=GetObject _
        ("winmgmts:{impersonationLevel=impersonate}\\" & _ 
            strComputer & "\" & strNameSpace)

    Set colNameSpaces = objWMIService.InstancesOf("__NAMESPACE")

    For Each objNameSpace in colNameSpaces
        Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name)
    Next
End Sub

zeigt die Namen aller Namespaces im WMI-Repository auf dem Computer an, die in strComputer angegeben sind (ein Punkt steht für den lokalen Computer).

Namespaces können Unternamespaces enthalten. Man kann sich das gesamte Objekt als eine Art Verzeichnisstruktur vorstellen. Wenn wir nur nach allen Namespaces fragen (angefangen mit dem root-Namespace oberster Ebene), würden wir lediglich die Namespaces der ersten Ebene innerhalb von root erhalten und alle Unternamespaces würden ausgelassen. Stattdessen verwenden wir den Trick der Rekursion. Wir erstellen eine Unterroutine namens „EnumNameSpaces“, die einen Namespace als Parameter verwendet und alle Unternamespaces zurückgibt.

Wir starten, indem wir EnumNameSpaces mit root als Parameter aufrufen. Hiermit werden alle Namespaces innerhalb des root-Namespace zurückgegeben. Dann führen wir die Rekursion aus. Beachten Sie, dass EnumNameSpaces tatsächlich sich selbst aufruft und dann an jeden Unternamespace weitergeben wird, der identifiziert wird. Trinken Sie einen Schluck Kaffee, und denken Sie kurz darüber nach. Dies bedeutet, dass jeder Namespace verarbeitet wird, und alle Unternamespaces, sofern vorhanden, angezeigt werden.

Beachten Sie, dass wir am Anfang der Unterroutine die Anweisung „On Error Resume Next“ eingefügt haben. Dies ist für den Fall vorgesehen, dass Sie das Skript in einem Sicherheitskontext ausführen, der keinen Zugriff auf alle Namespaces hat. Wenn dies der Fall ist, wird das Skript immer noch ausgeführt, kann jedoch etwas langsam sein, da es auf Timeouts wartet.

Sie könnten hierfür natürlich lediglich wbemtest.exe (auf jedem Computer vorhanden, auf dem WMI installiert ist) oder Scriptomatic (go.microsoft.com/fwlink/?LinkId=125976) verwenden. Unter Verwendung eines Startskripts und Ihrer hervorragenden Skripterstellungsfähigkeiten können Sie diese Namespaces filtern oder in Excel ausgeben oder die Namespaces auf zwei Computern vergleichen.

Da wir nun wissen, wie das Repository aufgeteilt ist, können wir ein Skript entwickeln, mit dem wir untersuchen können, was sich in den einzelnen Abschnitten befindet. Wir wissen, dass WMI-Klassen in jedem dieser Namespaces gespeichert werden. Erstellen wir also zunächst eine Liste.

WMI-Klassen

Wir haben bereits erwähnt, dass das WMI-Repository über das Common Interformation Model verfügt. Dieses Modell ist im CIMV2-Namespace (V2 für Version 2) gespeichert. Wenn Sie sich den CIMV2-Namespace anschauen, sollten Sie alle WMI-Klassen sehen, aus denen das Modell besteht. Dies kann über folgendes Skript ausgeführt werden:

strComputer = "."
Set objWMIService=GetObject("winmgmts: _
    {impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")

For Each objclass in 
    objWMIService.SubclassesOf()
    Wscript.Echo objClass.Path_.Class
Next

Sehen wir uns genau an, wie dieses Skript funktioniert. Das Aufrufen von GetObject gibt ein SWbemServices-Objekt zurück. GetObject ist eine VBScript-Funktion, die Verweise auf skriptfähige COM-Objekte zurückgibt. In diesem Fall gibt GetObject ein Objekt aus der WMI-Skriptingbibliothek zurück, da wir die Zeichenfolge „winmgmts:…“ übergeben haben. Beachten Sie, dass die Zeichenfolge, die wir an GetObject übergeben, den Namespace enthält, mit dem wir eine Verbindung aufbauen. In diesem Fall root\cimv2. Das SWbemServices-Objekt, mit dem wir arbeiten, ist an den Namespace gebunden, der angegeben wurde. In der SWbemServices-Dokumentation finden Sie Informationen zu möglichen Aktionen, die Sie in dem Skript ausführen können.

Eine Aktion kommt Ihnen wahrscheinlich sehr bekannt vor: ExecQuery. Hierbei handelt es sich um die Methode, mit der Sie eine WQL-Abfrage (Windows Management Instrumentation Query Language, WMI-Abfragesprache) für den Namespace ausführen können, zu dem eine Verbindung besteht. Es gibt jedoch zahlreiche andere Aktionen, die Sie ausführen können, wenn ein SWbemServices-Objekt einem Namespace zugeordnet ist.

Wir möchten alle WMI-Klassen im Namespace anzeigen, und dies ist mit SubClassesOf möglich. Die Dokumentation gibt an, dass SWbemObjectSet zurückgegeben wird. Das hört sich nicht gerade nach etwas an, dem Sie allein im Wald begegnen möchten! Konzentrieren Sie sich nur auf die letzten drei Buchstaben, die besagen, dass es sich um ein Set handelt. Wie WMI-Skripterstellern bekannt ist, können Sie ein Set mit „For Each“ aufschlüsseln.

Bei jedem Member eines SWbemObjectSet handelt es sich um ein SWbemObject, was nicht überraschend ist. Jedes SWbemObject repräsentiert eine der WMI-Klassen im CIMV2-Namespace. Informationen finden Sie in der Dokumentation für das SWbem­Objekt. Hier werden alle Informationen aufgeführt, die wir für diese Klassen ausgeben können.

Für unser Skript haben wir lediglich die Anzeige des Klassennamens gewählt. Hierfür haben wir die Eigenschaft „Path_“ aufgerufen. Es stellt sich heraus, dass es sich bei der Eigenschaft „Path_“ um ein Objekt handelt. Dieses Objekt ist SWbemObjectPath und verfügt über mehrere eigene Eigenschaften. In unserem Fall verwenden wir Class, wobei es sich um den Namen der Klasse handelt.

Sie verfügen also nicht nur über ein Skript, dass alle WMI-Klassen in einem Namespace anzeigen kann, sondern auch über ein Skript, das problemlos aktualisiert werden kann, um verschiedene andere Informationen bezüglich dieser Klassen anzuzeigen. So kann es sich z. B. bei WMI-Klassen um Erweiterungen anderer WMI-Klassen handeln. Stellen Sie sich vor, Sie verfügen über ein Modell für ein Auto (Win32_Auto), müssen aber Kombis verwalten. Alle Elemente des Automodells gelten auch für Kombis.

Sie benötigen aber einige zusätzlichen Dinge, wie z. B. einen booleschen Wert, der angibt, ob der Kombi über die schicke Holzverkleidung verfügt. Hierfür möchten Sie nun nicht die gesamte Win32_Car-Funktionalität neu erstellen. Vielmehr benötigen wir hierfür einen Mechanismus zum Erweitern der Win32_Atuo-Klasse um alle neuen Eigenschaften. WMI enthält einen solchen Mechanismus.

Um zu überprüfen, ob eine WMI-Klasse Eigenschaften einer anderen WMI-Klasse erbt, prüfen Sie die Eigenschaft „Derivation_“ der SWbemObject-Klasse, die dieser WMI-Klasse zugeordnet ist. Das Skript in Abbildung 2 zeigt die WMI-Klassen im CIMV2-Namespace zusammen mit einer Liste von Klassen an, aus denen sie abgeleitet wurden.

Abbildung 2 Ableitungen von CIMV2-Klassen

strComputer = "."
Set objWMIService=GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
    strComputer & "\root\cimv2")

For Each objclass in objWMIService.SubclassesOf()
    WScript.StdOut.Write objclass.Path_.Class
    arrDerivativeClasses = objClass.Derivation_ 
    For Each strDerivativeClass in arrDerivativeClasses 
       WScript.StdOut.Write " <- " & strDerivativeClass
    Next
    WScript.StdOut.Write vbNewLine
Next

    For Each objNameSpace in colNameSpaces
        Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name)
    Next
End Sub

Das Skript beginnt genauso wie das erste Skript. Das Skript verwendet jedoch WScript.StdOut.Write statt WScript.Echo, um zu verhindern, dass nach der angezeigten Zeichenfolge automatisch ein Neue-Zeile-Zeichen hinzugefügt wird. (Hinweis: Sie müssen das Skript mit Cscript.exe statt mit Wscript.exe ausführen, wenn StdOut.Write funktionieren soll.)

In der Dokumentation für SWbemObject sehen Sie, dass das Objekt über die Eigenschaft „Derivation_“ verfügt. Bei dieser Eigenschaft handelt es sich um ein Array von Zeichenfolgen, das die Namen der Klassen enthält, von denen die aktuelle Klasse abgeleitet wurde. In unserem Skript entschlüsseln wir das Array mit „For Each“ und zeigen alle Klassen getrennt durch einen ASCII-Pfeil an. Wenn Sie sich daran gewöhnt haben, mit dem SWbemServices-Objekt zu starten, das vom GetObject zurückgegeben wird und die Möglichkeiten in der Dokumentation für die WMI-Skriptingbibliothek erkunden, können Sie die Eigenschaften und Methoden für diese Objekte testen, um herauszufinden, was möglich ist.

Wenn Sie verstanden haben, mit welchem Objekt der WMI-Skriptingbibliothek Sie in verschiedenen Abschnitten im Skript arbeiten, können Sie zur nächsten Ebene in der WMI-Skripterstellung weitergehen. Sie werden unsere Skripts nicht nur verwenden, sondern auch verstehen, warum wir ExecQuery aufrufen oder auf die Eigenschaft „Properties_“ verweisen können. Anschließend können Sie noch weitergehen.

Das Scriptomatic-Tool erfüllt Ihre Erwartungen nicht? Kein Problem. Ändern Sie das Tool beliebig ab, sodass es Ihren Anforderungen entspricht. Vielleicht können Sie Ihre Entwicklung ja an andere verkaufen. Schreiben Sie in diesem Fall einfach eine E-Mail an scripter@microsoft.com, in der Sie uns mitteilen, wie wir unsere Tantiemen erhalten.

Eigenschaften

Jede WMI-Klasse modelliert Elemente, Sie mit einem Set von Eigenschaften und Methoden verwalten können. Bei den Eigenschaften handelt es sich um die Merkmale des Elements. Ein Prozess verfügt z. B. über eine ID und eine Priorität und verwendet eine bestimmte Speichermenge. Diese Eigenschaften sind in der WMI-Klasse „Win32_Process“ enthalten.

Nachdem Sie die Klasse zur Verwaltung einer Entität identifiziert haben, sehen Sie sich die verfügbaren Eigenschaften an, um festzustellen, ob sich die Entität im Verwaltungsmodell befindet. Die SWbemObject-Klasse enthält eine Eigenschaft namens „Properties_“. Lustig, nicht? Der Wert dieser Eigenschaft ist ein SWbemPropertySet-Objekt, das eine Sammlung von SWbemProperty-Objekten enthält. Jedes dieser SWbemProperty-Objekte entspricht einer Eigenschaft in der WMI-Klasse, die dem SWbem­Objekt zugeordnet ist. Ich weiß, diese ganzen SWbem*-Namen hören sich alle sehr kompliziert an. Tatsächlich ist es aber gar nicht so schlimm. Sehen Sie sich Abbildung 3 an.

fig03.gif

Abbildung 3 SWbemObject macht die Eigenschaften der WMI-Klasse verfügbar, an die das Objekt gebunden ist (zum Vergrößern auf das Bild klicken)

Bedenken Sie, dass die Klassen, die mit SWbem* beginnen, Member der WMI-Skriptingobjektbibliothek sind. Hierbei handelt es sich um die Objekte für das Arbeiten mit WMI. Diese gehören nicht zu dem WMI-Modell, das Sie verwalten können.

In Abbildung 3 repräsentiert SWbemObject eine WMI-Klasse, Win32_SomeClass, die über Eigenschaften verfügt: Property_1, Property_2 und Property_3. Diese werden über die eigene Eigenschaft „Properties_“ verfügbar gemacht. Wenn das Objekt an eine andere WMI-Klasse, Win32_SomeOtherClass, gebunden ist, ändert sich der Name der Eigenschaft nicht. Der Name lautet dann immer noch Properties_. Die Eigenschaften der Klasse, an die das Objekt gebunden ist, sind aber wahrscheinlich verschieden.

Im Grunde genommen verwendet SWbemObject die Eigenschaften der WMI-Klasse, an die es gebunden ist, aber Sie können mithilfe des gleichen Properties_-Mechanismus auf die unterschiedlichen Eigenschaften zugreifen. Haben Sie alles verstanden? Trinken Sie noch einen Schluck Kaffee, und denken Sie über das Diagramm nach. Dann wird Ihnen alles klar werden.

Das Skript in Abbildung 4 nutzt SWbemObject und dessen Eigenschaft „Properties_“ zum Abrufen und Anzeigen aller Eigenschaften der WMI-Klasse „Win32_Service“. Der Anfang des Skripts sollte Ihnen bekannt vorkommen. Die primäre Änderung besteht darin, dass wir den Namespace und die WMI-Klasse isoliert haben, sodass diese einfacher geändert werden können. Sie können z. B. einfach den Wert „strClass“ in „Win32_BIOS“ ändern, um die Eigenschaften dieser Klasse, statt der Eigenschaften von Win32_Service anzuzeigen. In der For Each-Schleife durchlaufen wir die SWbemPropertySet-Sammlung (objClass.Properties), wobei der Name jedes SWbemProperty angezeigt wird.

Abbildung 4 Abrufen der Eigenschaften von Win32_Service

strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"

Set objClass = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\" & strNameSpace & ":" & strClass)

WScript.Echo strClass & " Class Properties"
WScript.Echo "------------------------------"

For Each objClassProperty in objClass.Properties_
    WScript.Echo objClassProperty.Name
Next

Methoden

Einige WMI-Klassen gehen über Modelliereigenschaften oder Merkmale einer verwaltbaren Entität hinaus und enthalten Methoden, die Zugriff auf Verhaltensweisen oder Aktionen bieten, die eine Entität verwenden oder verwendet haben kann.

Das Format eines Skripts, das alle Methoden einer WMI-Klasse zurückgibt (in Abbildung 5 gezeigt), entspricht dem Skript, das Eigenschaften zurückgibt.

Abbildung 5 Abrufen der Methoden für eine WMI-Klasse

strComputer = "."
strNameSpace = "root\cimv2"
strClass = "Win32_Service"

Set objClass = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _ 
    strComputer & "\" & strNameSpace & ":" & strClass)

WScript.Echo strClass & " Class Methods"
WScript.Echo "---------------------------"

For Each objClassMethod in objClass.Methods_
    WScript.Echo objClassMethod.Name
Next

Der Unterschied besteht darin, dass die Eigenschaft „Methods_“ statt der Eigenschaft „Properties_“ verwendet wird. Ist es also auch möglich, die Typen der Parameter anzuzeigen, die von dieser Methode verwendet werden? Wie können wir lediglich jene WMI-Klassen anzeigen, die tatsächlich über Methoden verfügen? Versuchen Sie nach dem Lesen dieses Artikels, die entsprechenden Skripts zu erstellen, um diese Fragen zu beantworten.

Hoffentlich haben wir Ihnen einen hilfreichen ersten Einblick in die Tiefen von WMI geboten. Sie müssen sich durch einen ziemlich dichten Wald an SWbem*s schlagen. Skripts stellen jedoch eine praktische und einfache Methode hierfür dar. Die zwei Wintercamper hatten leider nicht so viel Glück. Sie haben es nicht wirklich bis in die Tiefen geschafft. Es stellte sich heraus, dass die angemessene Menge an fermentierten Getränken nicht mitten im Winter in die Tiefen transportiert werden konnte.

Die Scripting Guys arbeiten für Microsoft (oder sind zumindest dort angestellt). Wenn sie nicht gerade ihrem Hobby, dem Baseball (oder verschiedenen anderen Aktivitäten) nachgehen, betreiben sie das TechNet-Skriptcenter. Besuchen Sie es unter www.scriptingguys.com.