Das Windows Management Instrumentarium

Wenn Sie ein Abonnement des Magazins 'Access im Unternehmen' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Das Windows Management Instrumentarium

Wenn Sie in Ihrer Datenbank externe Informationen über System, Hardware oder Software benötigen, so lassen sich diese unter VBA meist nur über umfangreiche API-Routinen gewinnen. Dabei gibt es mit dem Windows Management Instrumentarium, kurz WMI, eine Schnittstelle, die fast keine Wünsche offen lässt und Ihnen enorm viel Programmierarbeit abnehmen kann. Das ist Grund genug, diese Schnittstelle einmal genauer unter die Lupe zu nehmen.

Beispieldatenbank

Die Beispiele dieses Artikels finden Sie in der Datenbank 1409_WMI.mdb.

Wie funktioniert das WMI?

Windows bringt WMI als einen Service mit, der in der Systemverwaltung auf den Namen Windows-Verwaltungsinstrumentation hört und automatisch gestartet wird. Wirklich aktiv wird es allerdings erst, wenn eine Anfrage an den Service gestellt wird.

Dann findet man im Taskmanager den neuen Prozess WmiPrvSE.exe, eine Datei, die aus dem Unterverzeichnis wbem des Windows-Systemordners heraus gestartet wird. Der Prozess selbst verwaltet nur die Anfragen und hat deshalb den Namen WMI Provider Host.

Bei Anfragen entscheidet er, welche zusätzlichen Dlls zu laden sind, in denen die eigentlichen Funktionen zur Ermittlung der Systemkomponenten untergebracht sind. Diese Dlls sind die Provider, also Datenlieferanten, und ebenfalls im wbem-Verzeichnis beheimatet.

Sie finden dort außerdem zahlreiche Dateien mit der Endung .mof. Das sind Textdateien, die die Struktur jedes Providers beschreiben.

Wir kommen noch darauf zu sprechen, wie eine Anfrage an den Service gestellt wird. Der Ablauf sieht schematisch danach jedenfalls so aus:

  • Anfrage an WMI-Service
  • Der Service befragt den Provider Host.
  • Der Provider Host durchsucht beim ersten Start das Verzeichnis wbem nach allen Provider-Definitionen und baut daraus im Speicher eine Art Datenbank auf. Aus dieser wird je nach Anfrageschlüsselwörtern der geeignete Provider herausgesucht und die entsprechende Dll geladen.
  • Über eine COM-Schnittstelle wird ein Klassenobjekt aus der Dll erzeugt, das zu einem gesuchten Objekt, etwa einer Druckerinstanz, die Eigenschaften zurückliefert. Die Dll weiß selbst, welche weiteren Dlls sie zum Lösen der Aufgabe benötigt, und verwendet dazu normale API-Methoden.
  • Die Ergebnisse laufen den Weg zurück bis zum Auslöser der Anfrage

Der gesamte Vorgang ist standardisiert, sodass es keine Rolle spielt, ob Sie Hardware- oder Software-Komponenten abfragen wollen – der Ablauf ist immer identisch.

Da das WMI intern eine Datenbank über alle möglichen abfragbaren Objekte aufbaut, liegt es nahe, dass diese über einen SQL-Dialekt angesprochen werden kann. Das ist auch der Fall: Die Abfragesprache nennt sich WQL. Damit ist es möglich, ein Abbild der WMI-Datenbank in das Datenmodell einer Access-Datenbank zu überführen. Die Lösung dazu finden Sie in der Beispieldatenbank zum Beitrag. Das Datenmodell sieht aus wie in Bild 1 und zeigt eine hierarchische Struktur. Um es zu erläutern und die Entsprechungen zur WMI-Datenbank deutlich zu machen, nehmen wir ein einfaches WQL-Beispiel zur Hand:

Analogie des WMI-Datenmodells unter Access

Bild 1: Analogie des WMI-Datenmodells unter Access

SELECT Caption FROM Win32_Printer

Analog zu einer Access-Abfrage handelt sich bei Caption offenbar um ein Feld, das aus der Datenherkunft Win32_Printer stammt. Gesucht werden hier alle Bezeichnungen der im System installierten Drucker. Win32_Printer scheint demnach eine Tabelle oder selbst eine Abfrage zu sein. Tatsächlich trifft eher Letzteres zu. Was unter Access eine Tabelle oder Abfrage ist, nennt sich unter WMI allerdings Objektklasse.

WMI-Datenmodell

Welches ist aber die Datenbank, aus der die Objektabfrage stammt? Um bei der Analogie zu bleiben, nehmen wir an dieser Stelle das Ergebnis der Routinen der Beispieldatenbank vorweg und verweisen auf Bild 2.

Root-Provider-Liste des WMI

Bild 2: Root-Provider-Liste des WMI

Das ist der Inhalt der im vorherigen Bild dargestellten Tabelle tblWMIRoot. Was unter Access als die aktuelle Datenbank CurrentDb bekannt ist, wäre im WMI die Objektklasse root.

Sie besteht aus einer Auflistung von Grundklassen, bestimmten Standardkategorien, auch Namespaces genannt, von denen der Provider CIMV2 der wichtigste ist. CIMV2 ist eine Abkürzung für Common Information Model Version 2. root wäre der Name der Datenbank, CIMV2 eine Abfrage auf die Tabelle der Root-Provider. Also sähe die Syntax der Abfrage Win32_Printer eigentlich so aus:

SELECT * FROM root.CIMV2 _
   WHERE Name = 'Win32_Printer'

Dass diese Abfrage unter WQL so nicht funktioniert, werden wir noch sehen. Wichtig ist hier zunächst das Prinzip.

Ließe man die Bedingung in der Abfrage weg, so käme man auf eine Liste aller CIMV2-Klassen. In der Beispieldatenbank steht dafür die Tabelle tblWMIClasses. Ihr Inhalt ist auszugsweise in Bild 3 zu begutachten. IDNamespace ist ein Bezug zu ID der Root-Klasse; hier die 5 für CIMV2. Die Tabelle ist wiederum mit der Tabelle tblWMI­ClassesProps verlinkt, indem deren Feld IDClass mit der ID verknüpft und als Unterdatenblatt dargestellt wird. In tblWMIClassesProps stehen die Eigenschaften der jeweiligen Klasse und damit eigentlich deren Feldnamen. In der Abbildung sind für die Klasse Win32_CurrentTime (aktuelle Systemzeit) die Eigenschaftsfelder ausgeklappt. Wollte man etwa den Tag des aktuellen Datums abfragen, so wäre der Wert des Feldes Day der Abfrage Win32_CurrentTime zu ermitteln.

WMI-Objektklassen und die Eigenschaftsfelder einer Klasse

Bild 3: WMI-Objektklassen und die Eigenschaftsfelder einer Klasse

Das WMI abfragen

Schauen wir uns nun an, wie das Abfragen der Systemzeit unter VBA konkret realisiert werden kann. Grundsätzlich gibt es da zwei Wege, die beschritten werden können: eine Variante ohne und eine mit Verweis-Bibliothek. Zur verweislosen Lösung finden Sie nachfolgend zwei Routinen.

Sie können eine Instanz des WMI sehr einfach erhalten, indem Sie einen speziellen sogenannten Moniker-String an die VBA-Funktion GetObjekt übergeben:

Set objWMI = GetObject("winmgmts:")

Sie erhalten damit jedoch nicht etwa die oberste Ebene der WMI-Datenbank, also root, sondern bereits den CIMV2-Provider. Denn der voll ausgeschriebene Moniker-String müsste so lauten:

GetObject("winmgmts://./root/cimv2")

WMI verwendet, wenn bestimmte Parameter im String weggelassen werden, automatisch Defaults. Zu diesen Defaults gehören der angesprochene Computer, die root-Datenbank und der CIMV2-Provider. Die Syntax des Moniker-Strings lautet genau genommen

winmgmts://[Computer]/root/[Provider]

Für Computer können Sie einfach einen Punkt verwenden, wenn Sie das lokale System ansprechen wollen. Damit wird bereits deutlich, dass man über das WMI auch andere vernetzte Rechner abfragen kann, soweit die Berechtigungen dazu ausreichen! Für Computer kann man also ebenfalls die IP-Nummer oder den Netzwerknamen des Rechners angeben:

winmgmts://192.168.0.2/root/cimv2
winmgmts://Harry2/root/cimv2

WMI ist übrigens unsensibel, was Groß- und Kleinschreibung angeht. Meist ist es egal, wie Sie einen Ausdruck schreiben.

In der Routine TestWMIDate1 (s. Listing 1) aus dem Modul mdlWMI wird also zunächst ein Objekt objWMI gesetzt, das dem Klassenkatalog des Haupt-Providers CIMV2 entspricht. In Analogie zu Access entspräche dieses Objekt erst einer Datenbank. Und diese kann wie unter Access über OpenRecordset abgefragt werden, nur dass sich der Befehl hier ExecQuery nennt. Im WQL-String werden also die Felder Day, Month und Year des Providers Win32_CurrentTime abgefragt.

Sub TestWMIDate1()
     Dim objWMI As Object
     Dim objSet As Object
     Dim obj As Object
     Set objWMI = GetObject("winmgmts://./root/cimv2")
     Set objSet = objWMI.ExecQuery("SELECT Day, Month, Year FROM Win32_CurrentTime")
     Set obj = objSet.ItemIndex(0)
     Debug.Print obj.GetObjectText_
End Sub

Listing 1: Erste Variante zum Ermitteln von Datumsangaben über das WMI

Als Resultat bekommen Sie über diese Abfrage nicht ein Recordset, sondern ein WMI-ObjectSet, das in der Routine der Variablen objSet zugewiesen wird. Zwar kann ein Recordset viele Datensätze enthalten, doch in unserem Fall gibt es nun mal nur ein aktuelles Datum.

Das Resultat besteht also nur aus einem Datensatz (WMI-Objekt). Dieser Datensatz muss jetzt für die weitere Verwendung in einer weiteren Objektvariablen obj gespeichert werden. Man erhält ihn über die Funktion ItemIndex([Datensatznummer]) des ObjectSets. Und schließlich lässt sich der Inhalt des Datensatzobjekts extrem einfach über die Anweisung GetObjectText_ ausgeben. Heraus kommt dabei etwa dies:

instance of Win32_LocalTime
{
     Day = 21;
     Month = 09;
     Year = 2014;
};

GetObjectText_ entspricht damit dem Pendant GetString eines ADO-Recordsets.

Da sich das Ergebnis in dieser Form schlecht weiterverwenden lässt, zeigt die Prozedur TestWMIDate2 in Listing 2, wie man stattdessen die einzelnen Feldwerte des Datensatzes auslesen kann. Sie gleicht anfangs genau der besprochenen Routine, verwendet dann aber die Auflistung Properties_ des Datensatzobjekts, um die Eigenschaftswerte zurückzugeben. Sie werden als String zu einem Ergebnis zusammengesetzt, der das aktuelle Datum wiedergibt:

Sub TestWMIDate2()
     Dim objWMI As Object
     Dim objSet As Object
     Dim obj As Object
     Set objWMI = GetObject("winmgmts://./root/cimv2")
     Set objSet = objWMI.ExecQuery ("SELECT Day, Month, Year FROM Win32_CurrentTime")
     Set obj = objSet.ItemIndex(0)
     With obj.Properties_
         Debug.Print .Item("Day") & "." & .Item("Month") & "." & .Item("Year")
     End With
End Sub

Listing 2: Zweite Variante zum Ermitteln von Datumsangaben über das WMI

21.09.2014

Warum erst hier, werden Sie fragen? Hätte man, wie in einer Access-Abfrage, nicht gleich den Datums-String zusammensetzen können?

SELECT Day & '.' & Month & '.' &  Year
AS DatumAktuell FROM Win32_CurrentTime

Aber das lässt WQL eben nicht zu. Im SQL-String können nur einzelne Felder angegeben, aber nicht weiteren Operationen unterzogen werden, und die Zuweisung an einen Alias-Namen ist ebenso unmöglich.

WQL-Syntax

Die anderen relevanten Unterschiede von WQL zu JET-SQL sollen nicht verschwiegen werden. Was etwa auch nicht geht, ist die Verknüpfung mehrerer Tabellen in der Abfrage per Joins.

Zwar lassen sich Verknüpfungen zwischen Provider-Klassen auf etwas kompliziertere Weise auch herstellen, Erläuterungen dazu würden jedoch den Rahmen des Beitrags sprengen. Forschen Sie bei Interesse nach den WQL-Stichwörtern Associators Of oder References Of.

Möglich hingegen sind Bedingungen per WHERE-Statement und die Zusammensetzung von Bedingungen über OR und AND. Für unser Datumsbeispiel mit nur einem Ergebnisdatensatz macht eine bedingte Filterung keinen Sinn. Daher hier ein anderes Beispiel:

SELECT Caption FROM Win32_Printer _
WHERE Shared=True _
AND Name LIKE '%Epson%'

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.