Microsoft Windows 2000 - ScriptingHandbuch (Teil 1)
Seite 1 von 394
Microsoft Windows 2000 - Scripting-Handbuch (Teil 1).................................................... 1 Scripting-Konzepte und -Technologien zur Systemadministration: Kapitel 1 - Einführung in die Windows-Scripting-Technologien..................................................................................... 8 Wodurch hat sich Scripting einen so schlechten Ruf erworben?...............................10 Über dieses Buch............................................................................................................12 Woher weiß ich, ob das Buch für mich geeignet ist?...................................................13 Aufbau des Buches ........................................................................................................13 Die in diesem Buch verwendeten Scripte..................................................................14 Wo lassen sich die fehlenden Teile finden?..............................................................14 Ein Hinweis zu VBScript .............................................................................................15 Systemanforderungen....................................................................................................16 Scripting-Konzepte und -Technologien zur Systemadministration:Kapitel 2 - VBScript.........16 Übersicht zu VBScript .......................................................................................................17 Arbeiten mit Objekten .......................................................................................................18 Mit Objekten verbinden..................................................................................................19 Eine Objektreferenz erstellen ........................................................................................20 Methoden aufrufen ........................................................................................................20 Attribute Abfragen .........................................................................................................21 Variablen ..........................................................................................................................22 Konstanten .......................................................................................................................23 Zeichenketten ...................................................................................................................24 Zeichenketten als Variablen ..........................................................................................26 Verketten von Zeichenketten.........................................................................................27 Collections (Sammlungen) ................................................................................................28 For Each .......................................................................................................................29 Collections ohne Elemente............................................................................................30 Schleifen...........................................................................................................................31 For Next ........................................................................................................................31 Entscheidungen treffen .....................................................................................................33 Mehre Aktionen mit If Then Else durchführen................................................................34 Arrays (Felder)..................................................................................................................35 Eingabe ............................................................................................................................36 Fehlerbehandlung.............................................................................................................38 Das Err-Objekt ..............................................................................................................38 Fehler löschen...............................................................................................................41 VBScript-Referenz ............................................................................................................41 Arbeiten mit Variablen.......................................................................................................43 Variablen unter VBScript deklarieren.............................................................................44 Initialisierung von Variablen...........................................................................................44 Verwendung von Konstanten ............................................................................................46 Definieren von Konstanten ............................................................................................47 Vordefinierte Konstanten verwenden.............................................................................47 Datentypen unter VBScript................................................................................................49 Mit Datum- und Zeitinformationen arbeiten .......................................................................52 Abfragen der aktuellen Uhrzeit und des Datums............................................................52 Prüfen, ob ein Wert ein gültiges Datum ist.....................................................................52 Teile eines Datums oder eines Zeitwertes abfragen ......................................................54 Formatierung von Datum- und Zeitwerten .....................................................................60 Arbeiten mit Strings (Zeichenketten) .................................................................................62 Manipulation von Strings und String-Längen .................................................................63 In einem String nach Text suchen .................................................................................67 Groß- und Kleinbuchstaben...........................................................................................68 Arbeiten mit Zahlen...........................................................................................................69 Rechenreihenfolge ........................................................................................................69 Formatierung von Zahlen ..............................................................................................70 Prozentwerte formatieren ..............................................................................................72 Seite 2 von 394
Befehle mehrfach ausführen .............................................................................................73 Do Loop ........................................................................................................................73 Prüfen der Schleifenbedingung .....................................................................................74 Eine Schleife verlassen .................................................................................................75 Entscheidungen treffen .....................................................................................................76 Mehrere Bedingungen prüfen ........................................................................................77 If Then ElseIf .................................................................................................................78 Select Case...................................................................................................................80 Arrays ...............................................................................................................................81 Erstellen von Arrays ......................................................................................................81 Dynamische Arrays erstellen .........................................................................................82 Einen String mit Trennzeichen in ein Array konvertieren ...............................................84 Alternativen zur Verwendung von Arrays.......................................................................85 Fehlerbehandlung.............................................................................................................85 Handhabung von Laufzeitfehlern...................................................................................87 Aktivieren der Fehlerbehandlung...................................................................................89 Fehlerbehandlung in COM-Objekten .............................................................................90 Prozeduren .......................................................................................................................91 Aufrufen einer Prozedur ................................................................................................92 Funktionen ....................................................................................................................94 Parameter an Funktionen übergeben ............................................................................95 Rekursion ......................................................................................................................98 COM-Objekte....................................................................................................................99 Der COM-Prozess .......................................................................................................100 Bindung.......................................................................................................................101 Eine Methode für die Bindung eines Automatisationsobjekts auswählen.....................102 Überprüfen von Objektreferenzen ...............................................................................103 Objekte aus dem Speicher entfernen ..........................................................................103 Scripting-Konzepte und -Technologien zur Systemadministration: Kapitel 3 - Der WSH.....105 WSH-Übersicht ...............................................................................................................106 Standardmäßige Durchführung von Administrationsaufgaben .....................................106 Verwendung von COM-Objekten.................................................................................107 Nutzung von Standard-Features einer WSH-kompatiblen Scriptsprache.....................107 Verwendung von Kommandozeilentools......................................................................108 WSH vs. Cmd.exe .......................................................................................................108 Ein Hinweise zu WSH-Versionen ................................................................................109 Die WSH-Architektur.......................................................................................................109 Komponenten der WSH-Umgebung................................................................................110 Scriptdateien ...............................................................................................................111 Script Hosts.................................................................................................................112 Scripting Language Engines........................................................................................112 COM-Objekte ..............................................................................................................113 Zusammenarbeit der einzelnen Komponenten der WSH-Umgebung ..............................113 Das WSH-Objektmodell ..................................................................................................114 WSH-Script ausführen ....................................................................................................115 Scripte über die Kommandozeile ausführen ................................................................116 Script Host-Optionen ...................................................................................................116 Die Scriptausgaben in eine Textdatei umleiten............................................................118 Ausführung von Scripten planen..................................................................................119 Andere Verfahren zum Starten eines Scripts...............................................................119 WSH-Objekte..................................................................................................................120 Das Objekt WScript.........................................................................................................120 COM-Objekte verwenden ............................................................................................122 Eingaben und Ausgaben .............................................................................................125 Texteingaben und -ausgaben ......................................................................................127 Verwenden von Kommandozeilenargumenten ............................................................132 Seite 3 von 394
Die Scriptausführung steuern ......................................................................................142 WSH-Umgebungsvariablen abfragen ..........................................................................144 Auf Ereignisse reagieren .............................................................................................146 Das Objekt WshShell ......................................................................................................146 Programme ausführen.................................................................................................148 Arbeiten mit Verknüpfungen ........................................................................................154 Arbeiten mit Spezialordnern ........................................................................................157 Umgebungsvariablen ..................................................................................................158 Einträge im Ereignisprotokoll erzeugen .......................................................................162 Schreiben und Lesen in der lokalen Registrierungsdatenbank ....................................163 Tastatureingaben an ein Programm schicken..............................................................165 Das aktuelle Arbeitsverzeichnis eines Scripts abfragen und ändern ............................169 Zeitgesteuerte Nachrichtenfenster anzeigen ...............................................................169 Das Objekt WshNetwork .................................................................................................174 Verwalten von Netzlaufwerken ....................................................................................176 Verwalten von Netzwerkdruckern ................................................................................178 Informationen über den Benutzer und den Computer abfragen ...................................180 Das Objekt WshController...............................................................................................180 Scripte auf einem Remotecomputer ausführen............................................................183 Den Status von Remotescripten überwachen..............................................................183 Die vom Remotescript ausgelösten Fehler genauer untersuchen................................185 Beschränkungen im Zusammenhang mit Remotescripten...........................................186 Absichern von Scripten ...................................................................................................186 Signieren von Scripten....................................................................................................187 Die Verwendung von signierten Scripten erzwingen....................................................187 Scripte über ein mit Hilfe eines Scripts signieren.........................................................188 Eine Signatur mit Hilfe eines Scripts überprüfen..........................................................188 Die Ausführung von Scripten einschränken ....................................................................189 Deaktivieren des Windows Script Host ........................................................................190 Scripting-Konzepte und -Technologien zur Systemadministration - Kapitel 4 - Die ScriptLaufzeitbibliothek................................................................................................................190 Script Laufzeitbibliothek-Übersicht ..................................................................................191 Das Objekt FileSystemObject .........................................................................................192 Verwalten von Laufwerken..............................................................................................192 Eine Collection mit Laufwerken abrufen ......................................................................193 Binden an ein bestimmtes Laufwerk ............................................................................193 Auflisten der Laufwerkseigenschaften .........................................................................194 Prüfen, ob ein Laufwerk bereit ist ................................................................................196 Verwalten von Ordnern ...................................................................................................196 Eine Referenz auf einen Ordner erstellen....................................................................197 Prüfen, ob ein Ordner vorhanden ist............................................................................197 Einen Ordner erstellen.................................................................................................198 Löschen eines Ordners ...............................................................................................198 Ordner und deren Inhalte kopieren..............................................................................199 Verschieben von Ordnern und deren Inhalten .............................................................200 Ordner umbenennen ...................................................................................................201 Verwenden von Ordnereigenschaften.............................................................................201 Auflisten von Ordnereigenschaften..............................................................................202 Verwalten von Ordnerattributen ......................................................................................203 Ändern von Ordnerattributen .......................................................................................206 Auflisten der Dateien in einem Ordner.........................................................................206 Auflisten von Unterordnern..........................................................................................207 Verwalten von Dateien....................................................................................................209 Eine Referenz auf eine Datei erstellen ........................................................................209 Prüfen, ob eine Datei vorhanden ist.............................................................................210 Löschen einer Datei ....................................................................................................211 Seite 4 von 394
Kopieren einer Datei....................................................................................................212 Verschieben einer Datei ..............................................................................................213 Eine Datei umbenennen ..............................................................................................213 Abfragen von Dateieigenschaften ...................................................................................214 Auflisten der Dateiattribute ..........................................................................................215 Konfigurieren von Dateiattributen ................................................................................216 Den Pfad einer Datei verarbeiten ................................................................................217 Abfragen der Dateiversion...........................................................................................218 Textdateien lesen und schreiben ....................................................................................218 Textdateien erstellen ...................................................................................................219 Lesen von Textdateien ................................................................................................223 In Textdateien schreiben .............................................................................................227 Das Dictionary-Objekt .................................................................................................230 Ein Dictionary-Objekt erstellen ....................................................................................231 Einträge zu einem Dictionary-Objekt hinzufügen .........................................................232 Bearbeiten von Schlüsseln und Werten in einem Dictionary-Objekt.............................233 Die Zahl der Einträge in einem Dictionary-Objekt abfragen .........................................233 Die Elemente eines Dictionary-Objekts aufzählen .......................................................233 Die Existenz eines Schlüssels prüfen ..........................................................................234 Ein Element in einem Dictionary-Objekt ändern...........................................................235 Elemente aus einem Dictionary-Objekt entfernen........................................................235 Scripting-Konzepte und -Technologien zur Systemadministration: Kapitel 5 - ADSI-Scripting ...........................................................................................................................................237 ADSI-Überblick ...............................................................................................................237 Ein einführendes Beispiel ............................................................................................238 Verwaltung des Verzeichnisdienstes ...........................................................................238 ADSI-Scripting-Grundlagen.............................................................................................239 Primäre ADSI-Scripting-Kategorien .............................................................................239 Erstellen von Active Directory-Objekten ......................................................................239 Bearbeiten von Active Directory-Objekten ...................................................................241 Attribute eines Active Directory-Objekts lesen .............................................................243 Löschen von Active Directory-Objekten.......................................................................244 Vergleich der primären Aufgaben eines ADSI-Scripts .................................................246 Das Erstellen von ADSI-Scripten.................................................................................247 Schritt 1: Eine Verbindung aufbauen ...........................................................................247 Schritt 2: Eine Aufgabe ausführen ...............................................................................251 Schritt 3: Übermitteln an Active Directory ....................................................................254 Mehrere Aufgaben über ein Script durchführen ...........................................................255 Fortgeschrittene ADSI-Scripting-Techniken ....................................................................257 Administration von Multiwert-Attributen .......................................................................258 Ändern von Multiwert-Attributen ..................................................................................258 Lesen von Multiwert-Attributen ....................................................................................263 Das Cachen von Daten ...............................................................................................264 Die Methode GetInfo explizit aufrufen..........................................................................266 Die Methode GetInfoEx ...............................................................................................266 Kopieren, Verschieben und Umbenennen von Objekten .............................................268 Kopieren von Objekten................................................................................................268 Verschieben und Umbenennen von Objekten .............................................................270 Suchen........................................................................................................................273 Suchen in Active Directory ..........................................................................................273 Optimieren der Suchleistung .......................................................................................287 Administrative Aufgaben über einen Ergebnissatz ausführen......................................289 Auflisten der Active Directory-Objekte in Containern ...................................................291 Auflisten von Containerinhalten ...................................................................................292 Root Directory Service Entry .......................................................................................295 Verwendung des rootDSE ...........................................................................................295 Seite 5 von 394
Die Active Directory-Architektur ......................................................................................296 Die physikalische Architektur.......................................................................................297 Die logische Struktur ...................................................................................................297 Klassen und Attribute ..................................................................................................298 Active Directory-Replikation und -Indizierung ..............................................................305 Attribute, die zum globalen Katalog-Server repliziert werden.......................................305 Operative Attribute ......................................................................................................309 Die ADSI-Architektur.......................................................................................................310 ADSI-Schichten...........................................................................................................311 ADSI-Schnittstellen .....................................................................................................315 Scripting-Konzepte und -Technologien zur Systemadministration - Kapitel 6 - WMI-Scripting ...........................................................................................................................................317 WMI-Überblick ................................................................................................................318 Die WMI-Architektur........................................................................................................322 Verwaltete Ressourcen ...............................................................................................323 Die WMI-Infrastruktur ..................................................................................................324 WMI-Provider ..............................................................................................................324 CIMOM........................................................................................................................326 Das CIM-Repository ....................................................................................................327 WMI-Konsumenten......................................................................................................328 WMI-Sicherheit............................................................................................................328 WMI-Namespace-Sicherheit........................................................................................329 DCOM-Sicherheit ........................................................................................................330 Windows-Betriebssystem-Standardsicherheit..............................................................330 Das Common Information Model (CIM)...........................................................................331 Blaupausen .................................................................................................................333 Namespaces ...............................................................................................................334 Klassenkategorien.......................................................................................................338 CIM-Klassentypen .......................................................................................................342 Komponenten einer Klasse .........................................................................................344 Erkunden des CIM-Repository.....................................................................................352 Die WMI-Scripting-Bibliothek ..........................................................................................353 Das Objektmodell der WMI-Scripting-Bibliothek ..........................................................353 SWbemLocator ...........................................................................................................356 SWbemServices..........................................................................................................358 Schreiben von WMI-Scripten ..........................................................................................363 Eine Verbindung zu WMI über den WMI-Moniker aufbauen ........................................363 Das Prefix 'WinMgmts:'................................................................................................363 WMI-Sicherheitseinstellungen .....................................................................................364 Verwenden von WMI-Objektpfaden.............................................................................369 Abfragen von verwalteten Ressourcen über die WMI-Abfragesprache ........................370 Abfragen aller Eigenschaften aller Instanzen einer Klasse ..........................................372 Abfragen ausgewählter Eigenschaften aller Instanzen einer Klasse ............................372 Abfragen aller Eigenschaften für ausgewählten Instanzen einer Klasse ......................373 Zielgerichtete Abfragen über AND oder OR erstellen ..................................................376 Ausgewählte Eigenschaften für ausgewählte Instanzen einer Klasse zurückgeben.....377 Schnellere Abfragen über Forward-only erzeugen.......................................................377 Arbeiten mit Datum- und Zeitwerten ............................................................................378 WMI-Datumwerte in das Standardformat konvertieren ................................................380 Ein Standarddatum in ein WMI-Datum konvertieren ....................................................381 Scripte auf Basis von WMI-Vorlagen erstellen.............................................................383 Abfragen und Anzeigen der einzelnen Eigenschaften verwalteter Ressourcen ...........383 Abfragen und Anzeigen aller Eigenschaften einer verwalteten Ressource ..................384 Schreiben von Ressourceneigenschaften ...................................................................385 Aufrufen von Methoden ...............................................................................................385 Erstellen von Ressourcen............................................................................................386 Seite 6 von 394
Löschen von Ressourcen ............................................................................................387 Überwachen von Ressourcen über WMI-Ereignisbenachrichtigungen.........................387 Die drei Schritte eines Überwachungsscripts...............................................................389 Wie die Ereignisbenachrichtigung arbeitet...................................................................390 Erweiterte Überwachung .............................................................................................392
Seite 7 von 394
Scripting-Konzepte und -Technologien zur Systemadministration: Kapitel 1 Einführung in die Windows-ScriptingTechnologien Veröffentlicht: 26. Apr 2004 (Engl. Originaltitel: Introduction to Windows Script Technologies) Dieses Kapitel des "Microsoft Windows 2000 - Scripting-Handbuchs" bietet eine Einführung in das Thema Scripting im Allgemeinen und in die Windows-Scripting-Technologien im Speziellen. Zudem erläutert es Aufbau, Inhalt und Zielsetzung des Handbuchs sowie die Systemanforderungen, die für die im Buch beschriebenen Muster-Scripte erfüllt sein müssen. Dies ist ein Buch über das Scripting für Systemadministratoren. Wie die meisten Systemadministratoren wundern Sie sich möglicherweise, warum sich dieses Buch an Sie wendet. Im Allgemeinen gehört Scripting nicht zu den Dingen, mit denen sich Systemadministratoren beschäftigen. Jeder weiß: Scripting ist schwer, Scripting ist zeitaufwendig und für Scripting müssen Sie alle möglichen Abkürzungen lernen - WSH, WMI, ADSI, CDO, ADO, COM. Systemadministratoren verfügen weder über die Zeit noch über den Hintergrund, um Scripte schreiben zu können. Oder doch? Eines der Hauptziele dieses Buches ist es, Missverständnisse wie diese zu beseitigen. Ist Scripting schwer? Möglicherweise. Andererseits - werfen Sie doch einmal einen Blick auf das folgende Script. Es führt eine häufig auftretende administrative Aufgabe durch. Set objNetwork = CreateObject("WScript.Network") objNetwork.MapNetworkDrive "X:", "\\atl-fs-01\public"
Auch wenn Sie nichts über Scripting wissen und fassungslos auf die erste Zeile blicken, werden Sie trotzdem erkennen, dass das Script die Freigabe \\atl-fs-01\public zu Laufwerk X zuordnet. Wenn Sie sich mit der Systemadministration auskennen - das bedeutet, Sie wissen was eine Freigabe, ein gemapptes Netzlaufwerk und ein UNC-Pfad (Universal Naming Convention) ist - dann ist der Schritt vom Mappen eines Laufwerks über die grafische Benutzeroberfläche oder die Kommandozeile zum Mappen eines Laufwerks über ein Script nicht mehr sehr groß. Anmerkung: •Wenn Sie jetzt schon den Faden verloren haben - zum Beispiel, weil Sie sich nicht sicher sind, was Scripting überhaupt sein soll - dann stellen Sie sich Scripte so vor: •Stellen Sie manchmal fest, dass Sie immer dieselben Befehle für dieselben Aufgaben verwenden? Erwischen Sie sich dabei, wie Sie oft auf die gleichen Schalter in den gleichen Assistenten klicken, um einen Vorgang auf vielen Computern oder für viele Benutzer durchzuführen? •Scripte helfen Ihnen dabei, solche wiederholenden Arbeiten zu automatisieren. Ein Script ist eine Datei, die die zur Durchführung einer Aufgabe notwendigen Schritte beschreibt. Nachdem Sie das Script erstellt haben, können Sie es "ausführen" - es führt die Schritte dann für Sie durch und spart Ihnen viel Zeit und Energie. Sie müssen das Script nur einmal erstellen und können es dann so oft wie nötig benutzen. Seite 8 von 394
Zugegebenermaßen sind nicht alle Scripte so einfach und intuitiv, wie das eben gezeigte. Wenn sie dieses Buch durcharbeiten, werden Sie feststellen, dass die meisten der gezeigten Scripte nicht mehr als 15 bis 20 Zeilen lang sind - und sie führen alle nützlichen administrativen Aufgaben aus. Viele der Scripte können Sie untersuchen, und über den Scriptcode können Sie herausfinden, was das Script macht - hierzu ist es ganz egal, wie groß Ihre Scripting-Erfahrung ist. Erfordert Scripting viel Zeit? Kann sein. Wenn Sie ein Script schreiben, dass 500 Zeilen lang ist (das könnte durchaus passieren), dann wird schon das Eingeben des Scriptes eine Menge Zeit kosten. Wichtiger ist es aber, die Entwicklungszeit des Scriptes und die Zeiteinsparung durch die Verwendung des Scriptes zu berücksichtigen. Unten finden Sie beispielsweise ein Script, das alle Ereignisprotokolle eines Computers sichert und löscht. strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate, (Backup, Security)}!\\" _ & strComputer & "\root\cimv2") Set colLogFiles = objWMIService.ExecQuery _ ("Select * from Win32_NTEventLogFile") For Each objLogfile in colLogFiles strBackupLog = objLogFile.BackupEventLog _ ("c:\scripts\" & objLogFile.LogFileName & ".evt") objLogFile.ClearEventLog() Next
Zugegeben, dieses Script ist nicht so intuitiv wie das erste Script. Außerdem werden Sie zur Entwicklung eines solchen Scripts etwas mehr über Scripting und über WMI lernen müssen. Und Sie müssen das Script ja auch noch in Notepad eingeben - die ganzen 11 Zeilen. Aber überlegen Sie Folgendes: Wie lange würde es dauern, die Ereignisprotokolle eines Computers manuell zu sichern und zu löschen? (Wenn Sie dies überhaupt manuell durchführen - da das manuelle Löschen und Sichern so mühsam und zeitaufwändig ist, wird der Vorgang oft einfach vergessen.) Mit einem Script können Sie diese Aufgabe in wenigen Minuten durchführen. Und wie wäre es, wenn Sie eine weitere halbe Stunde investieren, um das Script so zu ändern, das es die Ereignisprotokolle auf allen Computern sichert und löscht? Möglicherweise müssen Sie etwas Zeit und Energie investieren, aber es wird nicht lange dauern, bis sich diese Investition auszahlen wird. Noch nicht überzeugt? Auch wenn Scripting nicht schwer und zeitaufwändig ist, so müssen Sie doch noch immer den ganzen technischen Krimskrams lernen? Sicherlich - wenn Sie ein Experte im Scripting werden wollen. Schauen Sie sich doch einmal das folgende Script an. Es gibt die Namen aller auf einem Computer installierten Dienste zurück. strComputer = "." Set objWMIService = GetObject("winmgmts:" & _ "{impersonationLevel=Impersonate}!\\" & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery("Select * from Win32_Service") For Each objItem in colItems Wscript.Echo objItem.Name Next
Das ist ein recht kompliziertes Script. Neben anderen Dingen verwendet es: •Objekt-Methoden und -Attribute •Sprachelemente von Microsoft® Visual Basic® Scripting Edition (VBScript), wie zum Beispiel eine For-Each-Loop-Schleife um die Elemente einer Collection aufzulisten
Seite 9 von 394
•Einen COM-Moniker (Component Object Model) •WMI-Objektpfade, -Namensräume und -Klassen •Eine WMI-Abfrage Um dieses sieben Zeilen lange Script schreiben zu können, müssen Sie also über eine ziemlich große Menge an Wissen verfügen. Kein Wunder, dass viele Leute denken, dass Scripting schwer ist. Die Wahrheit ist aber, um ein Script wie dieses schreiben zu können, müssen Sie COM und Automatisation nicht vollständig verstehen. Nehmen Sie an, dass, was Sie wirklich möchten, ist ein Script, dass die Namen aller im Moment auf einem Computer ausgeführten Prozesse zurückgibt statt die Namen aller installierten Dienste. Ein solches Script sieht so aus: strComputer = "." Set objWMIService = GetObject("winmgmts:" & _ "{impersonationLevel=Impersonate}!\\" & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery("Select * from Win32_Process") For Each objItem in colItems Wscript.Echo objItem.Name Next
Was hat sich im Vergleich zum vorherigen Script geändert? Nichts - und genau darum geht es. Schauen Sie genau hin - es gibt einen fett gedruckten Eintrag (Win32_Process). Das ist der einzige Teil, der sich gegenüber dem vorherigen Script geändert hat. Haben Sie schon mehr über COM-Monikers oder WMI-Objektpfade gelernt? Wahrscheinlich nicht. Sie können jetzt aber dieses grundlegende Script nehmen und es so verändern, dass es die von Ihnen gewünschten Informationen zurückgibt. Wie wäre es mit den installierten Grafikkarten? Versuchen Sie dieses Script: strComputer = "." Set objWMIService = GetObject("winmgmts:" & _ "{impersonationLevel=Impersonate}!\\" & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery("Select * from Win32_VideoController") For Each objItem in colItems Wscript.Echo objItem.Name Next
Sind alle Scripte so einfach? Nein, leider nicht. Und in diesen Beispielen werden einige Probleme einfach nicht beachtet (zum Beispiel "Woher weiß ich, dass es Win32_VideoController und nicht Win32_VideoCard heißt?' oder 'Was ist, wenn ich mehr als nur den Namen der Grafikkarte wissen möchte?'). Der Punkt ist nicht, dass Sie Scripte schreiben können ohne irgendetwas zu wissen. Der Punkt ist, Sie können Scripte schreiben ohne alles wissen zu müssen. Wenn Sie sich mit COM-Monikern und WMI-Objektpfaden auskennen, bevor Sie Ihr erstes Script schreiben, dann ist das super. Wenn Sie es vorziehen, sich einfach mit dem Schreiben von Scripten beschäftigen möchten - zum Beispiel indem Sie auf den Beispielen dieses Buches aufbauen - dann ist das ebenso in Ordnung.
Wodurch hat sich Scripting einen so schlechten Ruf erworben? Wenn Scripting so einfach ist, warum hat es dann einen so schlechten Ruf? Und wenn es so nützlich ist, warum nutzen es nicht mehr Systemadministratoren? Schließlich würden die meisten Systemadministratoren etwas, dass ihnen das Leben einfacher macht, nicht wissentlich ignorieren. Seite 10 von 394
Es gibt wahrscheinlich viele Gründe für diesen schlechten Ruf - die meisten von ihnen gehen aber auf die ersten Versionen der Microsoft® Windows® Script-Technologie zurück. VBScript und Microsoft® JScript® (die zwei Scriptsprachen, die mit Microsoft® Windows® zur Verfügung stehen) wurden für clientseitiges Scripting für Webseiten entwickelt. Für Internet-Entwickler waren sie sehr nützlich - für Administratoren eher nicht. Daher wurde Scripting oft eher mit der Entwicklung von Webseiten in Verbindung gebracht (auch heute ist noch eine Menge des Bespielcodes aus der offiziellen Microsoft VBScript-Dokumentation in eine Webseite eingebettet). Später wurde der Windows Script Host (WSH) entwickelt. Der WSH ermöglichte die Verwendung von Scriptsprachen und -Technologien außerhalb von Internet Explorer; tatsächlich wurde der WSH sogar für Systemadministratoren entwickelt. Trotzdem konnte sich Scripting in der Systemadministration nicht umfassenden durchsetzen. Dies lag möglicherweise anfangs an der fehlenden Dokumentation. Es war schwer, Informationen zur Verwendung von VBScript oder JScript als Systemadministrationstool zu finden, und Informationen zu Technologien wie WMI oder Active Directory Service Interfaces (ADSI) zu finden war so gut wie unmöglich. Auch als diese endlich dokumentiert waren (typischerweise über Software Development Kits), war diese Dokumentation für Programmierer gedacht. Codebeispiele waren typischerweise in C++ statt in einer Scriptsprache geschrieben. Stellen Sie sich vor, Sie sind ein typischer Systemadministrator (mit guten Kenntnissen zu Windows und minimalen Kenntnissen zur Programmierung). Stellen Sie sich nun vor, Sie suchen auf der Microsoft-Website nach Informationen über Script und finden den folgenden Beispielcode: int main(int argc, char **argv) { HRESULT hres; hres = CoInitializeEx(0, COINIT_MULTITHREADED); // Initialize COM. if (FAILED(hres)) { cout << "Failed to initialize COM library. Error code = 0x" << hex << hres << endl; return 1; // Program has failed. } hres = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IDENTIFY, NULL, EOAC_NONE, 0 );
Mit einem solchen Beispiel gibt es wohl kaum sehr viele Systemadministratoren, die glauben, dass WMI oder ADSI praktische Werkzeuge sind. Heutzutage besteht natürlich kein Mangel mehr an Literatur zum Thema Scripting. Eine kürzliche Suche bei einem großen Online-Buchversender nach dem Schlüsselwort 'VBScript' ergab 339 Treffer. Das sind die guten Nachrichten. Die schlechten Nachrichten sind, dass die meisten dieser Titel nach einem von zwei möglichen Ansätzen arbeiten: Entweder betrachten sie Scripting weiterhin als Werkzeug für Webentwickler, oder sie konzentrieren sich fast ausschließlich auf VBScript und WSH. Natürlich sind VBScript und WSH wichtige Scripting-Technologien, aber für sich alleine sind dieses beiden Technologien nicht in der Lage, sehr viele nützliche Administrationsaufgaben zu erledigen. Von den 339 gefunden Scripting-Büchern behandelten nur eine Handvoll Scripting als Administrationswerkzeug, und nur einige von diesen deckten die Schlüsseltechnologien - WMI und ADSI - umfassend ab. Ein Systemadministrator, der sich ein oder zwei zufällige Scripting-Bücher anschaut, wird
Seite 11 von 394
wahrscheinlich nicht erkennen, dass Scripting zur Verwaltung von Windows-basierten Computern extrem nützlich sein kann.
Über dieses Buch Ist das Microsoft Windows 2000 - Scripting-Handbuch also nur Buch Nummer 340, oder unterscheidet es sich irgendwie von seinen Vorgängern? Dieses Buch stellt in vielerlei Art einen neuen Ansatz zum Scripting für die Systemadministration dar. Tatsächlich gibt es vier eindeutige Punkte, durch die dieses Buch sich von vielen anderen Büchern auf dem Markt unterscheidet: •Es konzentriert sich auf Scripting aus der Sicht eines Systemadministrators. In diesem Buch finden Sie viele Kapitel, die es auch in anderen Scripting-Büchern gibt; Es gibt zum Beispiel ein Kapitel über VBScript. Der Unterschied besteht darin, dass sich das Kapitel in diesem Buch auf die VBScript-Elemente konzentriert, die für Systemadministratoren wichtig sind. Systemadministratoren müssen viel mit COM arbeiten - daher erfahren Sie eine Menge über die Verwendung von COM-Objekten in einem Script. Systemadministratoren haben aber zum Beispiel wenig Verwendung für die Berechnung von Kosinus-Werten. Daher werden solche Themen gar nicht angesprochen - auch wenn es möglich ist, solche Berechungen mit VBScript durchzuführen. •Dieses Buch ist aufgabenorientiert und nicht scriptbezogen. In vielen Fällen wurden die Scripte in diesem Buch nachträglich erstellt. Manchmal erstellt ein Buchautor eine Menge Scripte und produziert dann einen Text um diese Scripte herum. Diese Buch verwendet einen ganz anderen Ansatz: Statt mit Scripten zu beginnen, stellen die Autoren erst einmal fest, welche Schlüsselaufgaben täglich von einem Systemadministrator erledig werden müssen. Erst dann stellen wir fest, wie diese Aufgaben über Scripte erledig werden können. Daher ist dieses Buch nicht unbedingt ein Buch über Scripting, sondern ein Buch über die effiziente Verwaltung von Windows-Computern. •In diesem Buch werden Tutorials mit praktischen Elementen kombiniert. Einige Bücher versuchen, Ihnen Scripting beizubringen - daher konzentrieren sie sich eher auf die Konzepte hinter Scripting. Oft beschränkt sich der praktische Teil auf reine Lippenbekenntnisse. Andere Bücher verfolgen einen komplett anderen Ansatz: Sie konzentrieren sich auf den praktischen Teil. Diese Bücher stellen Ihnen eine Menge nützlicher Scripte zur Verfügung, helfen Ihnen aber nicht dabei diese Scripte auch zu verstehen. Sie sind also nicht in der Lage, die Scripte zu verändern. In dem vorliegenden Buch versuchen wir das Beste aus beiden Ansätzen zu kombinieren. Wenn zum Beispiel ein nützliches Script zur Systemadministration präsentiert wird, dann wird dieses Script auch Schritt für Schritt erklärt. Sie erfahren, wie dieses Script arbeitet und wie Sie es an Ihre persönlichen Bedürfnisse anpassen können. •Dieses Buch berücksichtigt, dass mit der Größe einer Organisation auch der Bedarf an der Automatisierung von Prozessen wächst. Die Scripte in diesem Buch können sogar dann für Sie von Nutzen sein, wenn Sie Systemadministrator in einer Organisation mit nur einem Computer sind. Um ehrlich zu sein, werden Sie es aber wohl einfacher finden, Ihren einzelnen Computer über die Benutzeroberfläche zu verwalten. Wenn Sie 100 oder 1000 Computer verwalten, dann wird der Nutzen von Scripten und Scripting jedoch auf einmal dramatisch ansteigen. Um dies zu berücksichtigen, gibt es in diesem Buch extra ein eigenes Kapitel - Creating Enterprise Scripts - in dem besprochen wird, wie Sie die Beispiele für eine Organisation mit vielen Computern anpassen können. Seite 12 von 394
Woher weiß ich, ob das Buch für mich geeignet ist? Offiziell wurde dieses Buch für Systemadministratoren in mittleren bis großen Organisationen geschrieben, die sich mit der Verwaltung von Windows-Computern beschäftigen. Diese Gruppe wird wahrscheinlich den Großteil der Leser darstellen, ganz einfach, weil sich das Buch mit der Systemadministration beschäftigt und weil Systemadministratoren in mittleren und großen Organisationen die Personen sind, die die gezeigten Scripte nutzen. Das Buch sollte jedoch auch für alle Anderen nützlich sein, die das Entwickeln von Scripten lernen möchten. Obwohl sich die in diesem Buch besprochenen Techniken auf mittlere bis große Organisationen konzentrieren, können Sie in den meisten Fällen auch in kleinen Organisationen effektiv eingesetzt werden. Bei diesen Techniken handelt es sich typischerweise um administrative Aufgaben, sie können jedoch auch von Anwendungsprogrammierern und Web-Entwicklern umgesetzt werden. Die Verwaltung von Microsoft Exchange Server über Scripting wird in diesem Buch nicht besprochen; Microsoft Exchange Server kann allerdings über WMI verwaltet werden. Daher könnte für ExchangeAdministratoren nicht nur das Kapitel WMI Scripting interessant sein, sondern auch das Kapitel VBScript - in diesem werden grundlegende Techniken zur Arbeit mit Automatisationsobjekten besprochen. Auch Personen, die bereits über unterschiedliche Erfahrungsstufen im Scripting-Bereich verfügen, werden das Buch nützlich finden. Es wird jedoch keinerlei Erfahrung im ScriptingBereich vorausgesetzt. Wenn Sie das Buch von Anfang bis Ende durchlesen, werden Sie mit den fundamentalen Grundlagen des Scripting beginnen und sich dann zu den komplexeren Szenarien hocharbeiten. Was ist, wenn Sie sich bereits mit VBScript auskennen, jedoch nicht viel über ADSI wissen? Springen Sie direkt zum Kapitel ADSI-Scripting. Sie kennen die grundlegenden Prinzipien von WMI, möchten aber wissen, wie Sie über WMI Prozesse erstellen und beenden? Springen Sie direkt zum Abschnitt Processes. In diesem Buch gibt es für jeden interessante Informationen. Es wird kein Wissen vorausgesetzt. Dies heißt jedoch nicht, dass nicht gelegentlich eine Aufgabe oder eine Technik besprochen wird, die fortgeschrittene Kenntnisse erfordern.
Aufbau des Buches Das Microsoft Windows 2000 - Scripting-Handbuch ist in drei Teile aufgeteilt: •Konzeptuelle Kapitel. Diese Kapitel geben Ihnen einen umfassenden Überblick zu den primären Microsoft-Scripting-Technologien, inklusive Windows Script Host (WSH), VBScript, WMI, ADSI und der Script-Laufzeitbibliothek (Runtime library). Die Kapitel haben Tutorial-Charakter, und sind alle vom Standpunkt eines Systemadministrators aus geschrieben. Sie gehen alle davon aus, dass der Leser über keine oder nur geringe Erfahrung mit Scripting verfügt. •Aufgabenbasierte Kapitel. In diesem Kapitel werden die Kernbereiche der Systemadministration identifiziert, inklusive solcher Dinge, wie dem Verwalten von Diensten, Druckern und Ereignisprotokollen. Für jeden Kernbereich werden ca. 25 häufige Aufgaben identifiziert, zum Beispiel das Starten und Anhalten von Diensten, die Änderung von Dienstkonten-Passwörtern und das Feststellen der ausgeführten Dienste. Zu jeder Aufgabe erhalten Sie eine genaue Beschreibung der Aufgabe und warum diese wichtig ist, Seite 13 von 394
ein Beispielscript, das diese Aufgabe ausführt, und eine Schritt-für-Schritt-Anleitung zur Arbeitsweise des Scriptes. Außerdem erfahren Sie, wie Sie das Script an Ihre eigenen Bedürfnisse anpassen können. •Unternehmens-Kapitel. Diese Kapitel decken einen großen Themenbereich ab, inklusive der Einrichtung einer Scripting-Infrastruktur und der Scripterstellung in einem administrativen Team. Außerdem erfahren Sie mehr dazu, wie Sie ein Script in ein Unternehmens-Script umwandeln können - zum Beispiel ein Script, dass eine bestimmte Aktion auf allen Domänencontrollern oder für alle Benutzerkonten ausführt, oder ein Script, das Argumente aus einer Textdatei oder Datenbank entgegennimmt. Sie müssen nicht auf Seite Eins beginnen und das gesamte Buch bis zu Ende durchlesen. Es ist so gestaltet, dass Sie die Möglichkeit haben, nur die Teile zu lesen, die Sie interessieren. Wenn Sie jedoch noch nicht über Erfahrung im Scripting-Bereich verfügen, dann sollten Sie als Erstes die Kapitel über VBScript und WMI lesen. Wenn Sie sich mehr für die Verwendung von Scripten als für deren Entwicklung interessieren, dann können Sie mit den aufgabenbasierten Kapiteln beginnen. Lesen Sie ein Kapitel, kopieren Sie die Scripte und führen Sie diese aus, um zu sehen, was passiert. Wenn Sie genauer verstehen möchten, wie diese Scripte arbeiten, dann springen Sie zu den konzeptionellen Kapiteln zurück.
Die in diesem Buch verwendeten Scripte Die meisten Personen, die eine Vorab-Kopie dieses Buches gesehen haben, stellten überrascht - und dankbar - fest, dass die Scripte sehr kurz sind. Viele hatten Scripting-Bücher gelesen, in denen die Beispielscripte über zwei oder drei Seiten gingen, und waren überrascht, dass Scripting so einfach sein kann. Es gab allerdings auch Personen, die darüber schockiert waren, dass die Scripte so einfach gehalten sind. Nur sehr wenige der Beispielscripte verwenden zum Beispiel eine Fehlerbehandlung. Warum verwenden wir also keine Beispielscripte, wie sie auch in einer echten Produktionsumgebung vorkommen würden? Die Antwort ist einfach: Die Scripte aus diesem Buch sind nicht für eine Verwendung in einem Produktionssystem vorgesehen. Stattdessen sind sie zu Lernzwecken gedacht. Sie sollen Ihnen zeigen, wie Sie mit den unterschiedlichen Scripting-Technologien und Techniken umgehen. Die meisten der Scripte können zwar durchaus zur Systemadministration verwendet werden, dabei handelt es sich aber eher um einen glücklichen Zufall; dieses Buch und die enthaltenen Scripte wurden entwickelt, um Ihnen das Schreiben von Scripten beizubringen. Sie selbst sind nicht zu Verwaltung gedacht.
Wo lassen sich die fehlenden Teile finden? Nur weil die Scripte einfach gehalten wurden, heißt das nicht, dass Konzepte, wie zum Beispiel die Fehlerbehandlung oder die Verarbeitung von Parametern, ignoriert werden. Solche Techniken werden in den Kapiteln Creating Enterprise Scripts und Scripting Guidelines dieses Buches besprochen. Auch wenn Sie in diesem Buch keine FünfhundertZeilen-Scripte finden, die jede nur denkbare Scripting-Technik verwenden, werden doch alle diese Techniken irgendwo in diesem Buch beschrieben. Indem solche Dinge wie eine Fehlerbehandlung weggelassen werden, werden die Scripte aus diesem Buch so kurz wie möglich. Sie konzentrieren sich auf die jeweilige Aufgabe. Nehmen wir zum Beispiel einmal das erste Script aus diesem Kapitel, das zum Mappen eines Netzlaufwerkes verwendet wird: Seite 14 von 394
Set objNetwork = CreateObject("WScript.Network") objNetwork.MapNetworkDrive "X:", "\\atl-fs-01\public"
Dieses Script ist so einfach gehalten, wie nur möglich. Sie brauchen es nicht lange studieren, bevor Sie sagen können: 'Oh, so werden also Netzlaufwerke über ein Script gemappt". In einer Produktionsumgebung werden Sie das Script zugegebenermaßen anpassen wollen. Zum Beispiel so, dass der Benutzer einen Laufwerksbuchstaben und eine Freigabe angeben kann. Dies ist kein Problem, Sie benötigen jedoch einigen zusätzlichen Script-Code. So würde aus einem einfachen zweizeiligen Script ein Script mit mindestens 22 Zeilen. Die Grundidee - ein einfache Script, das das Mappen von Laufwerken demonstriert - wäre so verloren.
Ein Hinweis zu VBScript Alle Scripte in diesem Buch sind in VBScript geschrieben. Die Einscheidung für VBScript und gegen andere Scriptsprachen wurde aufgrund von drei Faktoren getroffen: •Mit Ausnahme von Perl, ist VBScript die populärste Scriptsprache zur Erstellung von administrativen Scripten. Es macht Sinn, eine Sprache auszuwählen, mit der die Leser bereits möglichst vertraut sind. •Im Gegensatz zu Perl wird VBScript auf allen Windows 2000-Computern automatisch installiert (Jscript übrigens auch). Daher sind mit VBScript keine Anschaffungen oder Installationen notwendig. •VBScript ist einfacher zu lernen als Jscript. Als kleiner Bonus ist VBScript Visual Basics sehr ähnlich. Visual Basic ist eine Programmiersprache, mit der einige Systemadministratoren möglicherweise bereits erste Erfahrungen gesammelt haben. In anderen Worten: VBScript ist einfach zu verwenden, erfordert keine zusätzlichen Investitionen und es muss nichts heruntergeladen oder installiert werden. Um ehrlich zu sein, ist in vielen Fällen die verwendete Scriptsprache irrelevant. VBScript selbst bietet relativ wenig Unterstützung zur Systemadministration. Es bietet die meisten Möglichkeiten, wenn es mit WSH, WMI, ADSI und anderen Scripting-Technologien eingesetzt wird. Hier unterscheidet sich VBScript nicht von anderen Scriptsprachen. Der allergrößte Teil der Scripte aus diesem Buch verwendet WMI oder ADSI - die Scriptsprache ist hier eigentlich irrelevant. Möchten Sie lieber mit JScript oder ActiveState ActivePerl arbeiten? Kein Problem - alles, was Sie lernen müssen, ist, wie Sie sich mit diesen Sprachen mit WMI oder ADSI verbinden. Das nächste Script ist ein WMI-Script. Es fragt den Namen des installierten BIOS ab, und zeigt diesen an. Das Script ist in VBScript geschrieben. strComputer = "." Set objWMIService = GetObject("winmgmts:\\ " _ & strComputer & "\root\cimv2") Set colItems = objWMIService.ExecQuery _ ("Select * from Win32_BIOS") For Each objItem in colItems Wscript.Echo objItem.Name Next
Das nächste Script entspricht dem vorherigen. Es ist nur in JScript geschrieben. Wie Sie sehen können, sind Syntax und Sprachkonventionen unterschiedlich, aber die Schlüsselelemente (fett dargestellt) bleiben gleich - Verbinden mit WMI, Abfragen der Informationen über die Klasse Win32_BIOS, Ausgabe des BIOS-Namens. Sie sehen also, die Auswahl der Sprache ist mehr eine Frage des persönlichen Geschmacks. Seite 15 von 394
var strComputer = "."; var objWMIService = GetObject("winmgmts:\\\\ " + strComputer + "\\root\\cimv2"); var colItems = objWMIService.ExecQuery ("Select * from Win32_BIOS"); var e = new Enumerator(colItems); for (;!e.atEnd();e.moveNext()) { var objItem = e.item(); WScript.Echo(objItem.Name); }
Anmerkung: •In der Realität gibt es einige kleine Unterschiede zwischen den Scriptsprachen. Diese wirken sich auf das aus, was Sie mit den Scripten durchführen können oder nicht durchführen können. Diese Unterschiede sind jedoch nicht wirklich einer Diskussion wert.
Systemanforderungen Dieses Buch setzt Computer voraus, die unter einem der Microsoft® Windows® 2000Betriebssysteme oder höher ausgeführt werden. Zusätzlich zu Windows 2000 sollte die Windows Script Host-Version 5.6 installiert werden. Diese wurde nach Windows 2000 veröffentlicht. Einige Scripte dieses Buches benötigen Features der Version 5.6. Weitere Informationen zur Version 5.6 des WSH finden Sie im Kapitel DerWSH dieses Buches. Anmerkung: •Wenn Sie nicht die Version 5.6 des WSH installiert haben, finden Sie die Installationsdateien unter http://www.microsoft.com/windows/reskits/webresources. Wenn Sie nicht sicher sind, welche Version installiert ist, dann finden Sie weitere Informationen im Kapitel Der WSH. Wenn Sie mit mehreren Betriebssystemen arbeiten - zum Beispiel Windows 2000 und Windows XP - sollten Sie außerdem Windows 2000 Service Pack 2 installieren. Ohne dieses Server Pack könnten Scripte unter Windows 2000 keine Informationen von Windows XPComputern abfragen. Außerdem ist es für die meisten der Scripte erforderlich, dass Sie mit administrativen Rechten angemeldet sind - zum Beispiel für die meisten WMI- und ADSI-Scripte. Wenn Sie mit einem Script einen Remotecomputer abfragen möchten, dann benötigen Sie auch auf diesem administrative Rechte. Außer diesen Voraussetzungen benötigen Sie keine phantasievollen Scripting-Tools, Editoren oder Entwicklungsumgebungen (IDE - Integrated Development Environment). Wenn Sie Notepad installiert haben, dann reicht dies schon aus, um Scripte zu schreiben.
Scripting-Konzepte und -Technologien zur Systemadministration:Kapitel 2 - VBScript Veröffentlicht: 26. Apr 2004
(Engl. Originaltitel: VBScript Overview) Microsoft Visual Basic Scripting Edition (VBScript) ist eine einfach zu verwendende Scriptsprache, die es Systemadministratoren ermöglicht, hervorragende Tools zur Verwaltung von Windows-Computern zu entwickeln. In der ersten Hälfte dieses Kapitels werden die Seite 16 von 394
grundlegenden Prinzipien von VBScript beschrieben. Hierbei wird ein Script erstellt, das den freien Festplattenplatz von Laufwerk C anzeigt. Im Laufe des Kapitels wird das Script zu einem verfeinerten Tool weiter entwickelt, das den freien Festplattenplatz von beliebigen Laufwerken anzeigen kann. Der zweite Teil des Kapitels bespricht die grundlegenden Prinzipien genauer und stellt andere VBScript-Konstrukte dar. Microsoft® Visual Basic® Scripting Edition (VBScript) ist eine einfach zu verwendende Scriptsprache, die es Systemadministratoren ermöglicht, hervorragende Tools zur Verwaltung von Windows-Computern zu entwickeln. In der ersten Hälfte dieses Kapitels werden die grundlegenden Prinzipien von VBScript beschrieben. Hierbei wird ein Script erstellt, das den freien Festplattenplatz von Laufwerk C anzeigt. Im Laufe des Kapitels wird das Script zu einem verfeinerten Tool weiter entwickelt, das den freien Festplattenplatz von beliebigen Laufwerken anzeigen kann. Der zweite Teil des Kapitels bespricht die grundlegenden Prinzipien genauer und stellt andere VBScript-Konstrukte dar.
Übersicht zu VBScript Microsoft® Visual Basic® Scripting Edition (VBScript) wird oftmals als "bloße" Scriptsprache missverstanden. Dies impliziert, dass eine Scriptsprache Systemadministratoren bei der Verwaltung von Hunderten oder Tausenden von Computern wenig nützt. Die Realität sieht jedoch ganz anders aus. In Kombination mit weiteren Technologien - zum Beispiel Windows Script Host (WSH), Windows Management Instrumentation (WMI) und Active Directory Service Interfaces (ASDI) - wird VBScript zu einer mächtigen Sprache zur Erstellung von Werkzeugen zur Systemadministration. Mit VBScript, WMI und ADSI können Sie ein Script von ca. 10.000 Zeilen schreiben, mit dem Sie einen Großteil eines Computers verwalten können. Was VBScript aber wirklich nützlich macht, ist die Möglichkeit ein Script in wenigen Minuten zu schreiben statt eine komplizierte und umfangreiche Lösung zu entwickeln. Das dreizeilige Script 2.1 zeigt Ihnen zum Beispiel, wie viel freier Platz noch unter Laufwerk C vorhanden ist. Script 2.1: Abfragen des freien Speicherplatzes unter einem Laufwerk über VBScript 1Set objWMIService = GetObject("winmgmts:") 2Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") 3Wscript.Echo objLogicalDisk.FreeSpace
Es kann natürlich sein, dass das Script nicht alle Ihre Wünsche erfüllt. Das Script zeigt Ihnen beispielsweise nur den freien Plattenplatz des lokalen Computers - und hier auch nur für Laufwerk C. Das Script kann jedoch einfach angepasst werden, ohne neu anfangen zu müssen. Dies ist ein weiterer Vorteil von Scriptsprachen und VBScript: Sie können mit einem eher einfachen Script anfangen und dies dann Ihren Bedürfnissen anpassen. Dieses Kapitel beschreibt einen solchen Vorgang. Sie fangen mit Script 2.1 an, und in den folgenden Abschnitten werden Sie dann zu diesem einfachen dreizeiligen Script weitere Funktionalitäten hinzufügen. Nach diesen Erweiterungen haben Sie dann ein Script, das die folgenden Funktionen zur Verfügung stellt: •
Abfragen des freien Plattenplatzes von jedem Computer in Ihrer Organisation - auch von Remotecomputern. Seite 17 von 394
• • • •
Abfragen des freien Plattenplatzes von mehreren Computern. Abfragen des freien Plattenplatzes aller Laufwerke eines Computers. Anzeigen einer Benachrichtigung, wenn nur noch wenig Platz auf einen Laufwerk verfügbar ist. Kein Fehler oder Abbruch, wenn der Benutzer einen ungültigen Computernamen angibt, oder wenn auf einen Computer über das Netzwerk nicht zugegriffen werden kann.
Jedes Mal, wenn neue Features zum Script hinzugefügt werden, werden die verwendeten VBScript-Konstrukte ausführlich erklärt. Nachdem das Script vollständig ist, finden Sie einen Referenzabschnitt, der diese Konstrukte (und andere) detaillierter abdeckt.
Arbeiten mit Objekten Mit VBScript können Sie über Programmiertechniken wie Verzweigungen, Schleifen, Fehlerbehandlung und Funktionsaufrufen sehr komplexe Scripte erstellen. Was Sie jedoch nicht finden werden, sind eingebaute Funktionen zu Systemadministration. VBScript verfügt über Funktionen, mit denen Sie die Wurzel einer beliebigen Zahl ermitteln oder den ASCIIWert eines Zeichens anzeigen können - es gibt aber keine Funktionen um diesen anzuhalten, Einträge aus dem Systemprotokoll abzurufen oder andere Administrationsaufgaben durchzuführen. Glücklicherweise gibt es andere Möglichkeiten solche Aufgaben durchzuführen - und zwar primär über Automatisationsobjekte. Automatisationsobjekte sind eine Untermenge von COM (Component Object Model). COM ist eine Möglichkeit für Anwendungen (exe-Dateien) oder Programmbibliotheken (dll-Dateien) ihre Funktionalitäten als Objekte zur Verfügung zu stellen. Diese Objekte und die von ihnen zu Verfügung gestellten Funktionalitäten können dann von Programmierern (oder Scriptautoren) in eigenen Anwendungen oder Scripten verwendet werden. Ein Textverarbeitungsprogramm könnte zum Beispiel seine Rechtschreibprüfung als Automatisationsobjekt zur Verfügung stellen. Ein Scriptautor kann dieses Objekt dann zur Rechtschreibprüfung in seinem Script verwenden. Die Möglichkeit mit Automatisationsobjekten zu arbeiten, und die Methoden (auch Funktionen genannt) und Attributen (Eigenschaften) dieses Objekte verwenden zu können, macht VBScript zu einem mächtigen Tool in der Systemadministration. Alleine ist VBScript nicht in der Lage auf das Ereignisprotokoll zuzugreifen. Mit der Funktionalität von WMI wird dies jedoch auf einmal möglich. VBScript verfügt über keine eigenen Verfahren, um Benutzerkonten zu erstellen. Sie können jedoch die Funktionalität von ADSI für diese Aufgaben verwenden. Statt also eine riesige Menge von eigenen Funktion zur Verfügung zu stellen, bietet VBScript eher die Rahmenbedingungen, um die Methoden und Attribute von Automatisationsobjekten zu nutzen. Script 2.2 zeigt die Bedeutung von Automatisationsobjekten in VBScript. Dieses Script gibt wiederum den freien Festplattenplatz von Laufwerk C zurück. Es verwendet nur sehr wenig VBScript-Code. Stattdessen macht es folgendes: 1.Es baut über die VBScript-Methode GetObject eine Verbindung zu WMI auf (WMI ist ein Automatisationsobjekt). 2.Es verwendet die Methode Get von WMI, um die Informationen über Laufwerk C Seite 18 von 394
abzurufen. 3.Es verwendet die Methode Echo von WSH (ein weiteres Objekt), um diese Informationen anzuzeigen. Wie gesagt, es gibt relativ wenig VBScript-Code in diesem Script. Stattdessen wird VBScript primär dazu verwendet, die Funktionalitäten von WMI und WSH zu verbinden. Script 2.2: Objekte mit VBScript verwenden 1Set objWMIService = GetObject("winmgmts:") 2Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") 3Wscript.Echo objLogicalDisk.FreeSpace
Mit Objekten verbinden Bevor Sie die Daten einer Datenbank verwenden können, müssen Sie sich irgendwie mit dieser Datenbank verbinden. Bei Objekten ist das genauso - bevor Sie die Methoden (Funktionen) und Attribute (Eigenschaften) eines Automatisationsobjektes verwenden können, müssen Sie eine Verbindung zu diesem Objekt aufbauen. Dieses Verfahren wird auch als Einbinden von Objekten in ein Script bezeichnet. Das Einbinden von Objekten kann ein wenig verwirrend sein, da sowohl die Sprache VBScript als auch der WSH (Windows Scripting Host) über eine Methode GetObject und CreateObject für den Zugriff auf Objekte verfügen. Auch wenn die beiden Methoden bei VBScript und bei WSH fast gleich arbeiten, so gibt es doch kleine Unterschiede - und diese werden immer wichtiger, je geübter Sie im Schreiben von Scripten werden. Diese Unterschiede werden später in diesem Kapitel besprochen. Im Moment stellen Sie sich einfach dumm und kümmern sich nicht darum, ob Sie die beiden Methoden über VBScript oder den WSH verwenden (in dem meisten Fällen werden Sie die VBScript-Varianten dieser beiden Methoden verwenden). •
•
WMI oder ADSI über GetObject einbinden. Sowohl WMI als auch ADSI ermöglichen Ihnen die Verwendung eines Monikers bei der Einbindung eines Scripts. Ein Moniker ist ein Zwischenobjekt, das die Nutzung von Objekten vereinfacht (das Thema wird weiter unten in diesem Kapitel genauer besprochen). Auch wenn es möglich (und manchmal erforderlich) ist, WMI oder ADSI über die Methode CreateObject einzubinden, ist die Verwendung von GetObject und eines Monikers in dem meisten Fällen schneller und einfacher. WMI oder ADSI über CreateObject einbinden. Im Allgemeinen benötigen Sie die Methode CreateObject, um neue Instanzen eines Objektes zu erstellen (zum Beispiel Instanzen der Objekte FileSystem, Dictionary oder Internet Explorer).
Eine Verbindung zu WSH aufbauen In Zeile 1 von Script 2.2 wird über den folgenden Ausdruck WMI in das Script eingebunden: Set objWMIService = GetObject("winmgmts:")
Mit dieser Zeile wird das Script mit dem SWbemServices-Objekt von WMI verbunden. Um eine Verbindung zu WSH aufzubauen, ist so etwas nicht nötig. Sie sind bereits mit WSH verbunden, da WSH zur Ausführung eines Scriptes benötigt wird. Das WSH-Objekt steht somit also in jedem Script ohne Ihr Zutun zur Verfügung.
Seite 19 von 394
Eine Objektreferenz erstellen In der Automatisation arbeiten Sie nicht direkt mit den Objekten selbst. Stattdessen erstellen Sie eine Referenz mit Hilfe von GetObject oder CreateObject auf das Objekt, und weisen diese Referenz dann einer Variable zu. Nachdem Sie eine solche Referenz erstellt haben, können Sie dann über die Variable statt über das Objekt selbst auf die Methoden und Attribute des Objektes zugreifen. In Script 2.2 wird die Methode GetObject verwendet, um der Variable objWMIService einen Verweis auf das WMI-Objekt SWbemServices zuzuweisen. Nach der Zuweisung können alle Attribute und Methoden des SWbemServices-Objektesüber die Variable objWMIService abgefragt und aufgerufen werden. In Zeile Zwei des Scripts wird zum Beispiel die Methode Get des Objektes zur Abfrage der Eigenschaften von Laufwerk C aufgerufen. Jedes Mal, wenn Sie einer Variable eine Referenz (einen Verweis) auf ein Objekt zuweisen, dann müssen Sie das VBScript-Schlüsselwort Set verwenden. Der folgende Code würde zum Beispiel zu einem Laufzeitfehler (einen Fehler bei der Ausführung des Scripts) führen: objWMIService = GetObject("winmgmts:")
Stattdessen müssen Sie zur Erstellung einer Objektreferenz das Schlüsselwort Set verwenden: Set objWMIService = GetObject("winmgmts:")
Das Schlüsselwort Set wird in VBScript nur zur Erstellung einer Objektreferenz verwendet. Wenn Sie es für andere Zwecke verwenden, zum Beispiel zum Zuweisen eines Wertes zu einer Variable, dann wird ein Laufzeitfehler auftreten. Der folgende Code wird zum Beispiel zu einem Fehler führen: Set x = 5
Warum? Set kann nur zum Zuweisen von Objekten zu Variablen verwendet werden. Da 5 jedoch kein Objekt ist, tritt ein Fehler auf.
Methoden aufrufen Sie können die Funktionalitäten von Automatisationsobjekten in Ihren eigenen Scripten verwenden. So sind Sie in der Lage, viel mächtigere Scripte zu schreiben, als wenn Sie nur auf die Funktionalität einer Scriptsprache eingeschränkt wären. Es ist zum Beispiel mit VBScript nicht möglich, ein Diagramm zu erstellen. Über die Automatisation können Sie sich eine solche Fähigkeit jedoch von Microsoft Excel ausleihen. Automatisationsobjekte stellen normalerweise sowohl Methoden als auch Attribute zur Verfügung (das muss jedoch nicht immer so sein). Über Methoden können die möglichen Aktionen eines Objektes durchgeführt werden. Script 2.2 verwendet zum Beispiel die Automatisation, um auf die Methoden von zwei unterschiedlichen COM-Objekten zuzugreifen - so führt es zwei unterschiedlichen Aktionen aus. Diese zwei Methoden sind: • •
Die Methode Get - sie steht über das WMI-Objekt SWbemServices zur Verfügung. Diese Methode ruft Informationen über ein bestimmtes Objekt ab. Die Methode Echo - sie steht über das WSH-Objekt zur Verfügung. Diese Methode zeigt Informationen auf dem Monitor an. Wenn ein Script in einer Eingabeaufforderung ausgeführt wird (wenn es also mit CScript.exe ausgeführt wird), dann werden diese Informationen in der Eingabeaufforderung ausgegeben. Wenn das Script mit Wscript.exe ausgeführt wird, dann werden die Informationen in einem eigenen Nachrichtenfenster ausgegeben.
Seite 20 von 394
Nachdem Sie eine Referenz auf ein Objekt erstellt haben, können Sie über die PunktSchreibweise (Notation) die Methoden des Objektes aufrufen. Die Punkt-Notation wird so genannt, da Sie beim Aufruf einer Methode den Namen der Variable schreiben, die den Verweis (die Referenz) auf das Objekt enthält, und dann einen Punkt und den Namen der gewünschten Methode des Objektes schreiben (abhängig von der Methode müssen Sie möglicherweise noch Parameter anhängen). Im Allgemeinen wird bei der Punkt-Notation die folgende Syntax verwendet: Objektreferenz.MethodenName
In der folgenden Codezeile sehen Sie ein Beispiel für die Punkt-Notation. Sie ruft die Methode Get des Objektes SWbemServices auf. Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'")
Die einzelnen Teile des Aufrufes der Methode Get des Objektes SWbemServices sind in Tabelle 2.1 genauer beschrieben. Tabelle 2.1: Die einzelnen Teile des Methodenaufrufes Teil
Beschreibung
ObjWMIService
Die Objektreferenz
.
Der Punkt trennt die Objektreferenz vom Namen der aufgerufenen Methode des Objektes.
Get
Der Name der Methode
('Win32_LogicalDisk.DeviceID='c:'')Parameter der Methode. Im Fall unserer Methode Get bedeutet dieser Parameter so viel wie "gib mir eine Referenz auf die Klasse Win32_LogicalDisk, bei der die DeviceID C: ist'. Anmerkung: • • •
Statt Wscript.Echo kann zur Anzeige von Informationen auch die VBScript-Funktion Msgbox verwendet werden: Msgbox objLogicalDisk.FreeSpace In diesem Buch wird jedoch immer Wscript.Echo statt Msgbox verwendet. Dies liegt daran, dass die Funktion Msgbox die Informationen immer als grafisches Nachrichtenfenster ausgibt. In diesem Nachrichtenfenster muss der Benutzer immer auf den Schalter OK klicken, bevor das Script weiter ausgeführt wird. Bei Scripten zur Systemadministration, die möglicherweise eine große Menge an Informationen anzeigen, könnte dieses Verfahren recht mühsam werden - außerdem könnte das Script nicht automatisch ausgeführt werden. Im Gegensatz dazu zeigt Wscript.Echo die Informationen zeilenweise in der Eingabeaufforderung an (vorausgesetzt, das Script wird unter CScript ausgeführt).
Attribute Abfragen Die Attribute eines Objektes sind für administrative Scripte sehr wichtig, denn viele Objekte stellen tatsächliche Objekte dar. In Zeile 3 von Script 2.2 wird zum Beispiel das Attribut FreeSpace abgefragt - und zwar über dieselbe Punkt-Notation, wie beim Aufrufen von Methoden. objLogicalDisk.FreeSpace
Seite 21 von 394
Bei WMI verweist das Objekt objLogicalDisk in diesem Fall nicht auf irgendein formloses Programmkonstrukt, sondern auf die tatsächliche Festplatte des Computers. Beim Attribut FreeSpace handelt es daher auch nicht einfach um eine Eigenschaft des Automatisationsobjektes, sondern um eine Eigenschaft von Laufwerk C. In gewissem Sinn erstellt WMI ein virtuelles Abbild eines physikalischen Objektes. Wenn Sie die Attribute dieses Abbilds abfragen, dann fragen Sie tatsächlich die Attribute des physikalischen Objektes ab.
Variablen Script 2.2 funktioniert exakt so wie erwartet. Wenn es ausgeführt wird, dann zeigt es den freien Speicherplatz von Laufwerk C an. Das heißt jedoch nicht, dass das Script nicht noch verbessert werden kann. Das Attribut FreeSpace gibt den freien Speicherplatz zum Beispiel in Byte zurück. Da Festplattenplatz jedoch typischerweise in Gigabyte angegeben wird, ist die Ausgabe oft schwer zu interpretieren. In Abbildung 2.1 sehen Sie ein Beispiel zur Ausgabe des Scripts, bei dem noch ca. 2,88 GB freier Speicherplatz zur Verfügung steht.
Abbildung 2.1: Freier Festplattenplatz in Byte Auch wenn mit einem Blick zu sehen ist, dass auf Laufwerk C noch genügend Patz zur Verfügung steht, so ist es doch schwer festzustellen, wie viel Platz noch verfügbar ist. Die meisten Systemadministratoren würden es wohl einfacher finden, wenn das Script den Speicherplatz in MB oder GB statt in Byte ausgibt. VBScript stellt eine große Menge mathematischer Funktionen zur Verfügung, über die Sie solche Aufgaben wie die Konvertierung von Byte in MB durchführen können. Außerdem steht Ihnen mit VBScript ein Konstrukt zur Verfügung, in dem Sie das Ergebnis einer solchen mathematischen Funktion speichern können: die Variable. In Variablen können Sie während der Laufzeit des Scripts jede Form von Daten speichern. Variablen sind Speichereinheiten, die dem Script während seiner Laufzeit zur Verfügung stehen. Sie können sich den Hauptspeicher des Computers in diesem Zusammenhang als eine Ansammlung von vielen kleinen Fächern vorstellen, die alle mit einem Etikett versehen sind. Eine Variable ist eines von diesen Fächern. Das Etikett dient zur Identifizierung dieses Fachs/der Variable. Sie können beliebige Daten in diesem Fach ablegen, und VBScript kann diese dann bei Bedarf wieder aus dem Fach abholen. Wenn Sie auf die Variable zugreifen (also auf die in ihr enthaltenen Daten), dann sucht VBScript einfach nach der entsprechen Speicheradresse und gibt die unter dieser Adresse gespeicherten Daten an Sie zurück. In Zeile 3 von Script 2.3 wird die Variable mit dem Namen FreeMegabytes zur Speicherung des Ergebnisses einer Division des Attributes FreeSpace durch 10484576 verwendet (ein MB enthält 10484576 Byte). Nach Zeile 3 enthält die Variable FreeMegabytes also das Ergebnis dieser Division. Wenn Sie nun die freien Megabyte irgendwo anders in Ihrem Script benötigen, dann können Sie auf die Variable zugreifen - Sie müssen die Berechnung nicht neu durchführen. Dies sehen Sie in Zeile 4, hier wird der Inhalt der Variable auf dem Monitor ausgegeben. Seite 22 von 394
Script 2.3: Verwendung von Variablen 1Set objWMIService = GetObject("winmgmts:") 2Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") 3FreeMegaBytes = objLogicalDisk.FreeSpace / 1048576 4Wscript.Echo FreeMegaBytes
Die Ausgabe von Script 2.3 sehen Sie in Abbildung 2.2.
Abbildung 2.2: Freier Plattenplatz in MB konvertiert Anmerkung: Beachten Sie, dass der Wert als 2954 statt als 2.954 ausgegeben wird (also ohne Tausender-Trennzeichen). Auch wenn es möglich ist, dieses in der Script-Ausgabe hinzuzufügen, so können Sie dieses intern in VBScript nicht verwenden. Formatierung der Script-Ausgabe Der Wert 2954,1328125 (er bedeutet, es gibt ca. 2954 MB freien Plattenplatz) ist für einen Systemadministrator wahrscheinlich viel Aussagekräftiger als der Wert 3098144768 aus unserem vorherigen Script. Die Nachkommastellen verwirren jedoch wahrscheinlich mehr, als das Sie nützen. Glücklicherweise bietet VBScript mehrere verschiedene Wege, um die Ausgabe eines Scripts anzupassen. Die Funktion Int zwingt VBScript zum Beispiel dazu, die Zahl als Integer (Ganzzahl ohne Nachkommastellen) anzuzeigen. Bitte beachten Sie, dass die Nachkommastellen der Varabile FreeMegaBytes in dem folgenden Beispiel nicht verworfen werden - sie werden nur nicht mit angezeigt. Script 2.4: Formatierung der Ausgabe 1Set objWMIService = GetObject("winmgmts:") 2Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") 3FreeMegaBytes = objLogicalDisk.FreeSpace / 1048576 4Wscript.Echo Int(FreeMegaBytes)
In Abbildung 2.3 sehen Sie die Ausgabe von Script 2.4. Die Funktion Int schneidet die Nachkommastellen des Originalwertes ab. Weitere Formatierungsmöglichkeiten, mit denen Sie zum Beispiel ein Tausender-Trennzeichen zur Ausgabe hinzufügen können, lernen Sie später in diesem Kapitel kennen.
Abbildung 2.3: die Ausgabe mit der Funktion Int formatieren
Konstanten Seite 23 von 394
In Script 2.3 wird die MB-Zahl durch die Division des Attributes FreeSpace durch den hardcodierten Wert 1048576 errechnet. "Hardcodierter"-Wert bedeutet, dass der Wert direkt im Script enthalten ist (und nicht zum Beispiel dynamisch berechnet wird). HardcodierteWerte werden oft auch als Literale bezeichnet, da Sie nichts anderes als diesen Wert repräsentieren. In einem kleinen Script wie diesem sind hardcodierte Literale kein Problem. In einem großen Script kann es jedoch zu zwei Problemen kommen: Zum einen mag es in einem kleinen Script klar sein, dass 1048576 der Wert ist, der zur Konvertierung von Byte in MB verwendet wird. In einem größeren Script - zum Beispiel in einem, das viele nicht ganz so eindeutige Berechnungen verwendet - könnte dies weniger klar ersichtlich sein. Und zwar ganz besonders, wenn das Script von mehreren Administratoren verwendet und verändert wird. Sie wissen vielleicht, wofür die Zahl 1048576 steht, aber ein anderer Administrator weiß das möglicherweise nicht. Wenn ein Script oft geändert werden muss, dann führt dies zu einem zweiten Problem. Literal-Werte können dazu führen, dass eine Änderung mehr Aufwand erfordert. Nehmen wir an, unser Scriptcode zur Konvertierung von Byte in MB wird an fünf oder sechs unterschiedlichen Stellen im Script verwendet. Wenn Sie sich nun später dazu entscheiden eine Konvertierung in GB statt in MB durchzuführen, dann müssen Sie alle fünf oder sechs Stellen im Script ändern. Ein Weg, um solche Probleme zu umgehen, sind Konstanten. Konstanten speichern ebenso wie Variablen Daten. Im Gegensatz zu Variablen kann eine Konstante jedoch nach ihrer Definition (also nachdem ein Wert zur Konstante zugewiesen wurde) nicht mehr verändert werden. So wird vermieden, dass der Wert der Konstante zum Beispiel versehentlich verändert wird. In 2.5 wird in Zeile 1 eine Konstante mit dem Namen CONVERSION_FACTOR definiert. Ihr wird der Wert 1048576 zugewiesen. Später in Zeile 4 wird die Bytezahl in MB konvertiert. Statt des Literalwertes 1048576 wird jedoch die Konstante CONVERSION_FACTOR verwendet. Das Ergebnis ist das gleiche wie vorher. Script 2.5 ist jedoch viel einfacher zu lesen und zu verstehen. Script 2.5: Verwendung von Konstanten 1Const CONVERSION_FACTOR = 1048576 2Set objWMIService = GetObject("winmgmts:") 3Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") 4FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 5Wscript.Echo Int(FreeMegaBytes)
Ein weiterer Vorteil von Konstanten ist deren Wiederverwendbarkeit. Sie werden einmal erstellt, und können dann im gesamten Script so oft wie nötig verwendet werden. In einer erweiterten Version von Script 2.5 könnte es zum Beispiel notwendig sein, mehrmals eine Konvertierung von Byte zu MB durchzuführen. Hierzu können Sie dann jedes Mal die Konstante verwenden. Wenn Sie sich später entscheiden in GB statt in MB zu konvertieren, dann müssen Sie nur noch die Konstante statt jedes einzelnen Literalwertes verändern.
Zeichenketten Seite 24 von 394
Wenn Sie anspruchsvollere Scripte schreiben, dann werden Sie feststellen, dass es unterschiedliche Datentypen gibt (wir werden dies später in diesem Kapitel genauer besprechen). In Zeile 1 von Script 2.5 wird der Konstante CONVERSION_FACTOR zum Beispiel der numerische Wert 1048576 zugewiesen: Const CONVERSION_FACTOR = 1048576
Diese Zuweisung funktioniert deshalb, weil es sich um einen numerischen Wert handelt. Wenn Sie einen solchen Wert zuweisen möchten, dann reicht es, ein Gleichheitszeichen, gefolgt vom Wert, zu verwenden. Wenn Sie allerdings versuchen, auf diese Art einen alphanumerischen Wert (eine Zeichenkette, auch String genannt) zuzuweisen, dann kann es zu unvorhergesehenen Problemen kommen. Die folgende Zeile versucht zum Beispiel der Variable Computer die Zeichenkette atl-dc-01 zuzuweisen und den Inhalt der Variable dann auszugeben: Computer = atl-dc-01 Wscript.Echo Computer
Die Ausgabe des Scripts sehen Sie in Abbildung 2.4.
Abbildung 2.4: Falsche Zuweisung einer Zeichenkette zu einer Variable Wieso wurde der Variable Computer der Wert -1 zugewiesen? Wenn VBScript eine alphanumerische Zeichenkette ohne Anführungszeichen findet, geht es davon aus, dass dieses Zeichenkette einen Variablennamen darstellt. Wenn irgendwo in der Zeichenkette in Bindestrich vorkommt, dann interpretiert VBScript dieses als Minuszeichen. Als Ergebnis interpretiert VBSCript die Zeile Computer = atl-dc-01 als 'Der Variable Computer sollt das Ergebnis der Berechnung Variable atl minus der Variable dc minus 01 zugewiesen werden'. Da atl und dc als neue und nicht initialisierte Variablen (ohne zugewiesenen Wert) angesehen werden, enthalten Sie den Wert 0. VBScript interpretiert die Codezeile also so: Computer = 0 - 0 - 1
Als Ergebnis steht natürlich in der Variable Computer der falsche Wert-1. Wenn Sie eine Zeichenkette zu einer Variable oder Konstante zuweisen, dann müssen Sie die Zeichenkette in Anführungszeichen einschließen. So können Sie sicherstellen, dass VBScript die Zeichenkette auch als Zeichenkette und nicht als Zahl oder Variable interpretiert. Die folgende Beispielzeile weist der Variable Computer zum Beispiel korrekt die Zeichenkette atl-dc-01 zu und gibt den Inhalt der Variable dann aus: Computer = "atl-dc-01" Wscript.Echo Computer
Die Ausgabe des Scripts sehen Sie in Abbildung 2.5.
Seite 25 von 394
Abbildung 2.5: Korrekte Zuweisung einer Zeichenkette zu einer Variable
Zeichenketten als Variablen Zeichenketten werden oft zur Zuweisung von Werten zu Variablen verwendet. Das Beispielscript in diesem Kapitel verwendet zum Beispiel die folgende Zeile zur Bindung an WMI: Set objWMIService = GetObject("winmgmts:")
Mit dieser Zeile verbinden Sie sich mit dem lokalen Computer. Wenn Sie jedoch für viele Computer zuständig sind, dann möchten Sie wahrscheinlich auch Informationen von den Remotecomputern abfragen. Wenn Sie WMI verwenden, dann können Sie sich mit einem Remotecomputer verbinden, indem Sie einfach den Computernamen in den an GetObject übergebenen Moniker mit aufnehmen. Die folgende Codezeile bindet den WMI-Dienst zum Beispiel an den Remotecomputer atl-dc-01: Set objWMIService = GetObject("winmgmts://atl-dc-01")
Diese Codezeile können Sie zwar für ein Script verwenden, das einen Remotecomputer abfragt, in einer Unternehmensumgebung benötigen Sie jedoch möglicherweise eine flexiblere Lösung. Ein Weg für eine Abfrage mehrerer Computer wäre das Script vor jeder Ausführung zu ändern. Sie ersetzen den hardcodierten Computernamen durch einen anderen. Ein weit besserer Ansatz ist jedoch das Script so zu ändern, dass es bei seinem Start einen Computernamen entgegennimmt - zum Beispiel durch einen Kommandozeilenparameter. Methoden zur Benutzereingabe werden später in diesem Kapitel besprochen. Vorher sollten Sie jedoch wissen, wie Sie Zeichenketten (zum Beispiel Computernamen) zu Variablen zuweisen und diese dann im Scriptcode verwenden. In Zeile 2 von Script 2.6 wird zum Beispiel die Zeichenkette 'atl-dc-01' zur Variable Computer zugewiesen. In Zeile 3 wird dieses Variable dann zur Bindung des WMI-Dienstes an Computer atl-dc-01 verwendet. Dies wird hier jedoch nicht über die Hardcodierung der Zeichenkette atl-dc-01 erreicht, sondern indem der Wert in der Variable Computer verwendet wird. Script 2.6: Verwendung von Zeichenketten 1Const CONVERSION_FACTOR = 1048576 2Computer = "atl-dc-01" 3Set objWMIService = GetObject("winmgmts://" & Computer) 4Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") 5FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 6Wscript.Echo Int(FreeMegaBytes)
In einem kleinen Beispielscript wie diesem erfordert das Zuweisen einer Zeichenkette zu einer Variable mehr Aufwand als eine Hardcodierung dieser Zeichenkette. Das Script zeigt trotzdem ein wichtiges Konzept: Sie können einen Wert zu einer Variable zuweisen und dieses Variable dann an Stelle einer Hardcodierung dieses Wertes verwenden. Warum ist das wichtig? Stellen Sie sich vor, dass Script wurde zur Abfrage des freien Festplattenplatzes von 100 statt von einem Computer entwickelt. Statt separate Zeichenketten zur WMI-Bindung für jeden Computer fest einzutragen, verwenden Sie jedoch nur eine Seite 26 von 394
Zeichenkette mit der Variable Computer. Ihr Script könnte dann die gleiche Codezeile 100 Mal ausführen - und jedes Mal würde die Variable Computer einfach einen unterschiedlichen Computernamen enthalten. Im Moment müssen Sie sich allerdings nur auf Zeile 3 von Script 2.6 konzentrieren: Set objWMIService = GetObject("winmgmts://" & Computer)
So interpretiert VBScript diese Codezeile: 1.Es liest alle bis zum zweiten Anführungszeichen. Mit anderen Worten: Set objWMIService = GetObject("winmgmts://"
2.Es erkennt das kaufmännische Und (&) - dieses bedeutet "Hänge alles Folgende an die Zeichenkette an". Dem & folgt in diesem Fall die Variable Computer, der der Wert atl-dc01 zugewiesen wurde. Für VBScript sieht die Codezeile damit also so aus: Set objWMIService = GetObject("winmgmts://atl-dc-01"
3.Es liest die schließende Klammer. In VBScript muss es genauso viele schließende Klammern wie geöffnete Klammern geben. Wenn die schließende Klammer fehlt, gibt VBScript eine Fehlermeldung aus. Die von VBScript gelesene Codezeile sieht nun so aus: Set objWMIService = GetObject("winmgmts://atl-dc-01")
4.Da nun das Ende der Codezeile erreicht ist, wird die Anweisung ausgeführt. Das Script verbindet sich mit dem WMI-Dienst von Computer atl-dc-01. Um sich mit dem WMIDienst eines anderen Computers zu verbinden, reicht es, den Wert in der Variable Computer zu ändern.
Verketten von Zeichenketten Verketten heißt zwei oder mehr Zeichenketten zu einer einzelnen Zeichenkette zu verbinden (Sie können auch Zeichenketten mit numerischen Werten und Datumswerten verketten). Mit einer Verkettung erreichen Sie oft eine besser lesbare oder deutlichere Ausgabe. Script 2.4 gibt zum Beispiel den Wert 2953 zurück. Wenn Sie wissen, dass das Script den freien Speicherplatz von Laufwerk C in MB zurückgibt, ist dies eine sehr nützliche Information. Wenn Sie jedoch nicht wissen, was das Script macht, dann ist die Ausgabe völlig sinnfrei. Neben anderen Dingen kann Ihnen eine Verkettung bei einer besseren Script-Ausgabe helfen. Statt zum Beispiel nur den Wert 2953 zurückzugeben, könnten Sie eine Nachricht wie "Es stehen 2953 MB freier Festplattenplatz zur Verfügung" zurückgeben. Hierzu müssen Sie die folgenden drei Teile kombinieren: • • •
'Es stehen ' - eine einfache Zeichenkette mit dem Anfang der Ausgabe. FreeMegabytes - die Variable, die den freien Speicherplatz in MB enthält. ' MB freier Festplattenplatz zur Verfügung" - eine zweite Zeichenkette mit dem Ende der Ausgabe.
Wie Sie den Zeilen 6 und 7 von Script 2.7 sehen, können Sie in VBScript mehrere Zeichenketten mit dem kaufmännischen Und (&) verketten. Script 2.7: Verketten von Zeichenketten 1Const CONVERSION_FACTOR = 1048576 2Computer = "atl-dc-01" 3Set objWMIService = GetObject("winmgmts://" & Computer)
Seite 27 von 394
4Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") 5FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 6Wscript.Echo "Es stehen " & Int(FreeMegaBytes) & _ " MB freier Festplattenplatz zur Verfügung." 7
Anmerkung: •
Der Unterstrich (_) am Ende von Zeile 6 wird zum Fortsetzen einer Zeile in der nächsten Zeile verwendet. Das bedeutet für VBScript, dass es die Zeile 6 und 7 wie eine einzige Zeile behandeln soll. Die beiden Zeilen passten nur einfach nicht in eine einzige Zeile.
Alternativ können Sie den Wert "Es stehen" einer Variable mit dem Namen MessageStart und den Wert "MB freier Festplattenplatz zur Verfügung" einer Variable mit dem Namen MessageEnd zuweisen. Sie können die drei Variablen dann so verketten: Wscript.Echo MessageStart & Int(FreeMegabytes) & MessageEnd
Wenn Sie sich die Zeilen 6 und 7 genau anschauen, dann werden Sie feststellen, dass die Zeichenketten Leerzeichen enthalten. Dies ist notwendig, da vom kaufmännischen Und keine Leerzeichen beim Verketten eingefügt werden. Nehmen wir einmal an, Sie würden die Leerzeichen weglassen: Wscript.Echo "Es stehen" & Int(FreeMegaBytes) & "MB freier Festplattenplatz zur Verfügung."
In diesem Fall würde die Ausgabe so aussehen:
Abbildung 2.6: Falsch verkettete Zeichenketten Bei einfacheren Verkettungen können Sie dieses Problem umgehen, indem Sie statt des kaufmännischen Und ein Komma zur Verkettung verwenden: Wscript.Echo "Es stehen", Int(FreeMegaBytes), "MB freier Festplattenplatz zur Verfügung."
Wenn die Zeichenketten durch ein Komma verkettet werden, dann wird jeweils ein Leerzeichen eingefügt. Das Ergebnis sollte dann so wie in Abbildung 2.7 aussehen:
Abbildung 2.7: Korrekt verkettete Zeichenketten
Collections (Sammlungen) Bis zu diesem Punkt haben die Scripte nur den freien Festplattenplatz von Laufwerk C auf einem Computer abgefragt. Hierzu wurde das Attribut DeviceID (eine Eigenschaft von der Klasse Win32_LogicalDisk) im Script hardcodiert. Seite 28 von 394
Einige Computer, zum Beispiel Server, verfügen jedoch über mehrere Laufwerke. In einem solchen Fall gibt Ihnen das Script nur einen Teil der benötigten Informationen zurück, da Sie möglicherweise den freien Speicherplatz für alle Laufwerke abfragen möchten. Hierbei gibt es jedoch ein Problem: Wie viele Laufwerke sind auf dem abgefragten Computer installiert? Theoretisch könnten Sie versuchen, den freien Speicherplatz aller Laufwerke von C bis Z abzufragen. Wenn der Computer aber zum Beispiel kein Laufwerk E verwendet, dann wird das Script fehlschlagen. Auch wenn Sie einen solchen Fehler über eine Fehlerbehandlung abfangen könnten, wäre das Script lang und schwer zu lesen und zu pflegen. Ein solches Script wäre außerdem extrem ineffizient; denn auch wenn ein Computer nur über ein einzelnes Laufwerk verfügt, würde es trotzdem versuchen, alle Laufwerke von C bis Z abzufragen. Glücklicherweise geben Automatisationsobjekte Informationen oftmals in Form von Collections (Sammlungen) zurück. Wie Briefmarken- oder Münzsammlungen sind Automatisationssammlungen einfach eine Gruppe von Objekten. Script 2.8 verwendet zum Beispiel die WMI-Methode InstancesOf (Zeile 4), um nicht nur ein einzelnes Laufwerk zurückzugeben, sondern eine Sammlung von allen auf dem Computer installierten Laufwerken. Wenn der Computer über vier Laufwerke verfügt (C, D, E und F), dann enthält die Collection vier Einträge - einen für jedes Laufwerk. Script 2.8: Verwendung von Collections 1Const CONVERSION_FACTOR = 1048576 2Computer = "atl-dc-01" 3Set objWMIService = GetObject("winmgmts://" & Computer) 4Set colLogicalDisk = objWMIService.InstancesOf("Win32_LogicalDisk") 5For Each objLogicalDisk In colLogicalDisk 6 FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 7 Wscript.Echo objLogicalDisk.DeviceID & " " & Int(FreeMegaBytes) 8Next
Wenn Informationen als Collections zurückgegeben werden, dann bedeutet das für Sie, dass Sie nicht raten müssen, wie viele Laufwerke auf einem Computer installiert sind. Stattdessen rufen Sie einfach die Collection ab (alle auf einem Computer installierten Laufwerke). Danach können Sie eine For- Each-Schleife verwenden, um auf jedes einzelne Objekt in der Collection zuzugreifen.
For Each Der Ausdruck For Each bietet Ihnen einen einfachen Weg, alle Objekte in einer Collection durchzugehen. Im Gegensatz zum Ausdruck For Next, der später besprochen wird, müssen Sie für For Each nicht wissen, wie viele Elemente in der Collection enthalten sind. Stattdessen beginnt For Each einfach mit dem ersten Element der Collection und geht diese durch, bis das letzte Element erreicht ist. Eine typische For-Each-Schleife sieht so aus: For Each objLogicalDisk In colLogicalDisk Wscript.Echo objLogicalDisk.DeviceID Next
Die einzelnen Teile einer solchen Schleife werden in Tabelle 2.2 beschrieben. Tabelle 2.2: Teile von For Each Teil
Beschreibung Seite 29 von 394
Teil
Beschreibung
objLogicalDisk
Diese Variable repräsentiert die einzelnen in der Collection enthaltenen Laufwerke
colLogicalDisk
Diese Variable enthält die Collection
For Each objLogicalDisk in colLogicalDisk
Startet die Schleife. Bedeutet so viel wie 'mache etwas mit jedem Element der Collection'.
Wscript.Echo objLogicalDisk.DeviceID
Befehle die in der Schleife für jedes Element der Collection ausgeführt werden (in diesem Fall nur einer).
Next
Das Ende der Schleife
Bei der Arbeit mit Collections gehen Sie normalerweise die gesamte Collection durch, statt nur auf ein einzelnes Element der Collection zuzugreifen. Wenn wir zum Beispiel annehmen, dass die Collection aus den Laufwerken C, D, E, F und G besteht, und Sie möchten nur den verfügbaren Platz von Laufwerk G ausgeben, dann gehen Sie trotzdem die gesamte Collection durch. Bei jedem Durchlauf fragen Sie ab, ob das Element den Laufwerkbuchstaben G verwendet, und wenn dies der Fall ist, dann geben Sie den verfügbaren Plattenplatz für dieses Element aus. Tipp: Es gibt keinen einfachen Weg, nicht die gesamte Collection durchgehen zu müssen. Sie können Ihre Scripte allerdings effizienter machen, indem Sie die Menge von Elementen in einer Collection einschränken. Ihre WMI-Abfrage könnte zum Beispiel nur die Instanzen der Klasse Win32_DiskDrive zurückgeben, bei denen der Laufwerksbuchstabe G ist. In diesem Fall enthält die Collection nur ein Element. Somit wird das Durchgehen der Collection schneller und effizienter.
Collections ohne Elemente Es ist möglich, dass eine Collection keine Elemente enthält. Das folgende Bespielscript fragt zum Beispiel alle auf einem Computer installierten Bandlaufwerke ab: Set objWMIService = GetObject("winmgmts:") Set colTapeDrives = objWMIService.InstancesOf("Win32_TapeDrive") For Each objTapeDrive In colTapeDrives Wscript.Echo objTapeDrive.Name Next
Wenn das Script auf einem Computer ohne Bandlaufwerke ausgeführt wird, dann scheint es, als ob nichts passiert. In Wahrheit wird das Script sehr wohl ausgeführt. Da der Computer jedoch über kein Bandlaufwerk verfügt, enthält auch die Collection keine Elemente. Wenn das Script auf einem Computer ohne Bandlaufwerke ausgeführt wird, passiert folgendes: 1.Verbindung mit dem WMI-Dienst 2.Abfrage der Collection mit den installierten Bandlaufwerken 3.Einrichtung einer For Each-Schleife um die gesamte Collection durchzugehen. Für jedes Element wird der Name ausgegeben.
Seite 30 von 394
Da es jedoch keine Elemente in der Collection gibt, wird die For Each-Schleife kein einziges Mal ausgeführt. Stattdessen macht das Script gleich mit der Zeile nach Next weiter. In unserem Beispielscript gibt es allerdings nach Next keine Zeile mehr. Damit ist das Script zu Ende. Es gibt keinen einfachen Weg um festzustellen, ob ein Script ausgeführt wurde. Ein Weg zur Verbesserung des Scripts wäre die Verwendung des Attributes Count. Mit diesem können Sie feststellen, wie viele Elemente eine Collection enthält. Das folgende Beispielscript verwendet das Attribut Count, um die Zahl der Bandlaufwerke des Computers zurückzugeben: Set objWMIService = GetObject("winmgmts:") Set colTapeDrives = objWMIService.InstancesOf("Win32_TapeDrive") Wscript.Echo colTapeDrives.Count
Wenn Sie die Zahl der Elemente einer Collection über das Attribut Count abgefragt haben, dann können Sie die beiden folgenden Dinge durchführen: • •
Sie können die Eigenschaften der Elemente der Collection ausgeben (wenn es ein oder mehrere Elemente gibt). Wenn es keine Elemente in der Collection gibt, dann können Sie eine Nachricht wie "Es gibt keine Bandlaufwerke" ausgeben.
Ein solches Script könnte wie folgt aussehen (der Ausdruck If-Then-Else wird später in diesem Kapitel besprochen): Set objWMIService = GetObject("winmgmts:") Set colTapeDrives = objWMIService.InstancesOf("Win32_TapeDrive") If colTapeDrives.Count = 0 Then Wscript.Echo "Es gibt keine Bandlaufwerke." Else For Each objTapeDrive In colTapeDrives Wscript.Echo objTapeDrive.Name Next End If
Schleifen Scripte, die Systemressourcen überwachen, müssen typischerweise in bestimmten Intervallen Daten sammeln. Den freien Festplattenplatz möchten Sie zum Beispiel sicher in regelmäßigen Intervallen überwachen - zum Beispiel einmal pro Woche, einmal pro Tag oder einmal pro Stunde. Wenn der Zeitraum zwischen den Datensammlungen relativ groß ist, können Sie das Script als geplanten Task ausführen. Wenn Sie aber zum Beispiel alle 10 Sekunden die Prozessorauslastung messen möchten, und zwar so lange, bis Sie 500 Messungen vorgenommen haben, dann möchten Sie wohl kaum 500 Tasks planen. Stattdessen führen Sie ein einzelnes Script aus, das die 500 Messungen für Sie vornimmt.
For Next Ein Weg, um ein Script die gleichen Aktionen mehrmals durchführen zu lassen, ist es die Aktionen (Befehl) in eine For-Next-Schleife einzuschließen.
Seite 31 von 394
Script 2.9 prüft zum Beispiel jede Stunde den freien Plattenplatz eines Computers - und zwar 12 Stunden lang. Hierzu wird das For-Statement aus Zeile 5 verwendet. Es besagt, dass der in der For Next-Schleife eingeschlossene Codeblock 12 Mal ausgeführt werden soll. Die Zeilen 6 bis 10 enthalten den Code, der zum Abfragen des Plattenplatzes notwendig ist, und in Zeile 11 wird das Script für eine Stunde angehalten (über eine Konstante, die das Script für 3.600.000 Millisekunden anhält). Zeile 12 markiert mit dem Next-Statement schließlich das Ende der Schleife. Wenn das Script ausgeführt wird, dann wird als erstes eine Verbindung zum Remotecomputer atl-dc-01 aufgebaut. Das Script fragt den freien Festplattenplatz ab und hält dann für eine Stunde an. Nach der Stunde springt das Script wieder zum ersten Statement innerhalb der For Next-Schleife und arbeitet den Codeblock in der Schleife ein weiteres Mal ab. Dies geht so weiter, bis die Schleife 12 Mal durchlaufen wurde. Nach der Schleife gibt es keinen weiteren Code mehr, so dass das Script dann beendet ist. Script 2.9: Befehle mehrfach ausführen 1 Const CONVERSION_FACTOR = 1048576 2 Const ONE_HOUR = 3600000 3 Computer = "atl-dc-01" 4 Set objWMIService = GetObject("winmgmts://" & Computer) 5 For i = 1 to 12 6 Set colLogicalDisk = objWMIService.InstancesOf("Win32_LogicalDisk") 7 For Each objLogicalDisk In colLogicalDisk 8 FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 9 Wscript.Echo objLogicalDisk.DeviceID & " " & Int(FreeMegaBytes) 10 Next 11 Wscript.Sleep ONE_HOUR 12Next
Mit dem For-Next-Statement ist es also möglich, einen bestimmen Codeblock mehrmals ausführen zu lassen. Sie sollten dies nicht mit dem For-Each-Statement verwechseln. For Each wird verwendet, um alle Elemente einer Collection zu durchlaufen. For Next wird verwendet, um einen bestimmten Codeblock mehrmals auszuführen. Um ein For-Next-Statement zu verwenden, müssen Sie sowohl den Startpunkt als auch den Endpunkt der Schleife festlegen. Da For-Next-Schleifen typischerweise X Mal ausgeführt werden, fangen sie normalerweise mit 1 an und enden mit X. Um eine Schleife 10 Mal auszuführen, fangen Sie daher mit 1 an und enden mit 10. Anmerkung: •
Sie können auch einen beliebigen anderen Startpunkt auswählen (zum Beispiel 314 oder 6912 statt 1). Ihr Script wird jedoch besser lesbar und wartbar sein, wenn Sie mit 1 beginnen.
Die For-Next-Schleife benötigt eine Schleifenvariable (auch Zähler genannt). Dieser Zähler speichert, wie oft die Schleife bereits durchlaufen wurde. Im folgenden Beispiel wird zum Beispiel die Variable i als Zähler verwendet. Der Zähler beginnt bei 1 und führt den Codeblock in der For Next-Schleife aus. Nachdem alle Befehle in der Schleife ausgeführt wurden, wird der Zähler automatisch um Eins erhöht. Das bedeutet, dass i nun den Wert 2 hat. Das Script springt nun zurück zum Anfang der Schleife und prüft, ob der Wert 2 noch unterhalb der gewünschten Schleifenzahl ist. Da er das ist, wird der Code in der Schleife ein weiteres Mal ausgeführt. Der Zähler wird wieder um Eins auf 3 erhöht, usw. For i = 1 to 5 Wscript.Echo i Next
Seite 32 von 394
Wscript.Echo "For Next-Schleife vollständig."
Was passiert, wenn i gleich 6 ist? Das Script springt zurück zum Anfang der Schleife und prüft, ob 6 noch innerhalb der gewünschten Schleifenzahl liegt. Da dies nicht der Fall ist, wird die Schleife beendet. Das Script wird mit der ersten Zeile hinter der Schleife (die Zeile unter Next) weitergeführt. In unserem Bespielscript wird nun die Nachricht "For-Next-Schleife vollständig" ausgegeben. Die Ausgabe des Scripts sollte folgendermaßen aussehen: 1 2 3 4 5 For Next-Schleife vollständig.
Anmerkung: •
Es gibt Fälle, in denen Sie den gleichen Befehlsblock immer wieder ausführen möchten ohne dass Sie vorher wissen, wie oft der Block genau ausgeführt werden soll. Stellen Sie sich zum Beispiel vor, Sie möchten den freien Festplattenplatz so lange prüfen, bis dieser unter einen bestimmen Wert fällt. In solchen Situationen sollten Sie eine Do-Loop-Schleife verwenden, die später in diesem Kapitel besprochen wird.
Entscheidungen treffen Einer der primären Gründe für den Einsatz von Scripten in der Systemadministration ist, dass sie die Notwendigkeit für einen Eingriff durch die Administratoren verringern sollen. Das bis jetzt entwickelte Script kann zwar schon eine ganze Menge, aber der Systemadministrator muss noch immer die Ausgabe des Scripts interpretieren. Er muss weiterhin selbst entscheiden, ob der Plattenplatz zu gering wird oder nicht. Das Script kann jedoch so verbessert werden, dass es nur dann eine Benachrichtigung ausgibt, wenn der Plattenplatz unter eine bestimmte Grenze fällt. Mit einem solchen Ansatz werden die Administratoren benachrichtigt, wenn der Plattenplatz zur Neige geht. Wenn sie keine Benachrichtigungen erhalten, dann ist alles in Ordnung. VBScript bietet Ihnen einige Programmierkonstrukte, die es Scripten ermöglichen "Entscheidungen zu treffen". Das Script kann bestimmte Daten analysieren und dann auf Basis dieser Daten eine bestimmte Aktion durchführen. Die einfachste Form eine Entscheidung ist das If-Then-Statement. Es vergleicht einen bestimmten Wert mit einen anderen Wert (zum Beispiel, ob der freie Speicherplatz kleiner als 100 MB ist). Wenn der Vergleich wahr ist (zum Beispiel, wenn nur noch 99 MB freier Platz zur Verfügung steht), dann führt das Script eine bestimmte Aktion durch. Wenn der Vergleich nicht wahr ist, dann passiert nichts. Script 2.10 ist ein Beispiel für einen solchen Vorgang. In Zeile 8 prüft das Script ob der freie Speicherplatz kleiner als 100 MB ist (indem es den Wert mit der Konstante WARNING_THRESHOLD vergleicht). Wenn diese Bedingung wahr ist, dann wird der Befehl hinter dem If-Then-Statement ausgeführt. Diesen finden Sie in Zeile 9. Er gibt eine Nachricht aus. Wenn die Bedingung falsch ist, dann wird Zeile 9 nicht ausgeführt. Stattdessen wird mit Zeile 10, dem Ende des If-Then-Blocks, weitergemacht. Seite 33 von 394
Script 2.10:Entscheidungen treffen 1 Const CONVERSION_FACTOR = 1048576 2 Const WARNING_THRESHOLD = 100 3 Computer = "atl-dc-01" 4 Set objWMIService = GetObject("winmgmts://" & Computer) 5 Set colLogicalDisk = objWMIService.InstancesOf("Win32_LogicalDisk") 6 For Each objLogicalDisk In colLogicalDisk 7 FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 8 If FreeMegaBytes < WARNING_THRESHOLD Then 9 Wscript.Echo objLogicalDisk.DeviceID & " hat wenig freien 10Speicherplatz." 11 End If Next
Mehre Aktionen mit If Then Else durchführen Script 2.10 zeigt eine Warnmeldung an, wenn der freie Festplattenplatz gering ist. Wenn dies nicht der Fall ist, dann wird auch keine Nachricht angezeigt. Für ein einfaches Überwachungsscript reicht dies sicher aus. Andererseits weiß der Benutzer bei der Scriptausführung nicht, ob er keine Meldung bekommen hat, weil der Plattenplatz ausreichen ist, oder weil das Script aus irgendeinem Grund fehlgeschlagen ist. Besser wäre es also, im Fall von wenig verfügbarem Speicherplatz eine Warnmeldung, und in allen anderen Fällen eine "Alles Ok"-Nachricht auszugeben. Einen solchen Ansatz können Sie über das If-Then-Else-Statement umsetzen. If-Then-Else-Statements arbeiten folgendermaßen: Wenn (If) eine Bedingung wahr ist, dann (then) führe folgende Aktion durch - Andernfalls (else) führe folgende Aktion durch. Wenn der Plattenplatz gering ist, dann gib eine Warnmeldung aus - andernfalls gib eine "Alles Ok"-Nachricht aus. Script 2.11 zeigt Ihnen ein Beispiel hierzu. In Zeile 8 wird der freie Plattenplatz mit einem Grenzwert (warning_threshold) verglichen. Wenn die Bedingung wahr ist (also wenn der freie Platz unter dem Grenzwert liegt), dann wird Zeile 9 ausgeführt. Was passiert, wenn die Bedingung falsch ist? In diesem Fall wird Zeile 11 hinter dem ElseStatement ausgeführt. Script 2.11: Verwendung des If-Then-Else-Statements 1 Const CONVERSION_FACTOR = 1048576 2 Const WARNING_THRESHOLD = 100 3 Computer = "atl-dc-01" 4 Set objWMIService = GetObject("winmgmts://" & Computer) 5 Set colLogicalDisk = objWMIService.InstancesOf("Win32_LogicalDisk") 6 For Each objLogicalDisk In colLogicalDisk 7 FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 8 If FreeMegaBytes < WARNING_THRESHOLD Then 9 Wscript.Echo objLogicalDisk.DeviceID & " is low on disk space." 10 Else 11 Wscript.Echo objLogicalDisk.DeviceID & " has adequate disk space." 12 End If 13Next
Es ist durchaus möglich, raffiniertere Szenarien zu konstruieren, zum Beispiel welche, in denen es mehr als zwei mögliche Aktionen gibt. Später in diesem Kapitel werden Sie noch zwei unterschiedliche Wege hierzu kennen lernen. Seite 34 von 394
Arrays (Felder) Collections sind eine hervorragende Möglichkeit Informationen zusammenzufassen, da sie es ermöglichen mit einer unbegrenzten Zahl von Elementen zu arbeiten - und zwar auch dann, wenn Sie keine Details dieser Elemente kennen. Script 2.8 ermöglicht es Ihnen zum Beispiel den freien Plattenplatz für alle installierten Festplatten eines Computers abzufragen. Sie müssen vorher nicht wissen, wie viele Festplatten installiert sind. Um die einzelnen Elemente einer solchen Collection durchzugehen, verwenden Sie eine einfache For-Each-Schleife. Diese Collections werden von Automatisationsobjekten für Sie erstellt. Es könnte jedoch sein, dass Sie andere Informationen verarbeiten möchten (Informationen, die nicht von einem Automatisationsobjekt zur Verfügung gestellt werden), und dass Sie für diese Informationen ebenfalls eine einfache Möglichkeit zum Durchgehen von Elementen benötigen. Stellen Sie sich zum Beispiel vor, dass Sie den verfügbaren Plattenplatz auf drei Computern statt auf nur einem prüfen möchten. Sie könnten hierzu natürlich einen Scriptcode schreiben, der dies auf dem ersten Computer durchführt, den Scriptcode dann kopieren und wieder einfügen, den kopierten Code so anpassen, dass er den Plattenplatz auf dem zweiten Computer prüft, usw. Natürlich würde ein solcher Ansatz funktionieren. Er könnte jedoch schnell sehr mühsam werden. Stellen Sie sich vor, Sie müssten statt drei Computern nun 100 Computer prüfen. Oder stellen Sie sich vor, Sie müssten Änderungen am Code vornehmen - zum Beispiel um nicht nur den freien Plattenplatz, sondern auch die Gesamtgröße des Laufwerks abzufragen. In diesem Fall müssten Sie bei 100 Computern auch 100 Änderungen am Code vornehmen. Das wäre nicht nur sehr mühsam, sondern es gäbe ich eine gute Chance, dass Sie irgendwo einen Fehler machen. Ein besserer Ansatz wäre hier eine For-Each-Schleife, die einfach eine Collection mit Computern durchgeht und für jeden den freien Plattenplatz abfragt. Dies können Sie erreichen, indem Sie die Computernamen in einem Array speichern. Ein Array ist eine Datenstruktur, die Sie ganz ähnlich wie eine Collection verwenden können. Script 2.12 schreibt die Namen von drei Computern (atl-dc-01, atl-dc-02 und atl-dc-03) in ein Array. Dann verwendet es eine For-Each-Schleife, um eine Verbindung mit jedem Computer aufzubauen und den freien Plattenplatz abzufragen. Das Array wird in Zeile 3 mit Hilfe der Funktion Array erstellt. Ihr werden als Parameter die drei Computernamen angehängt (da die Namen Zeichenketten sind, werden sie jeweils in Anführungszeichen eingeschlossen). In Zeile 4 werden die einzelnen Elemente des Arrays Computers mit Hilfe einer For-EachSchleife durchlaufen. Script 2.12: Mit einem Array arbeiten 1 Const CONVERSION_FACTOR = 1048576 2 Const WARNING_THRESHOLD = 100 3 Computers = Array("atl-dc-01", "atl-dc-02", "atl-dc-03") 4 For Each Computer In Computers 5 Set objWMIService = GetObject("winmgmts://" & Computer) 6 Set colLogicalDisk = objWMIService.InstancesOf("Win32_LogicalDisk") 7 For Each objLogicalDisk In colLogicalDisk 8 FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR If FreeMegaBytes < WARNING_THRESHOLD Then 9 10 Wscript.Echo Computer & " " & objLogicalDisk.DeviceID & _ 11 " is low on disk space." 12 End If
Seite 35 von 394
13 Next 14Next
Auch wenn Arrays einer Collection sehr ähnlich sind, so gibt es doch einen grundlegenden Unterschied. Als Scriptentwickler haben Sie über Collections sehr wenig Kontrolle. Wenn Sie eine Liste der installierten Laufwerke über WMI abrufen, dann erhalten Sie die Liste in der Reihenfolge zurück, die WMI festgelegt hat. Sie sind außerdem nicht in der Lage auf ein einzelnes Laufwerk zuzugreifen, ohne die gesamte Collection durchzugehen. Im Gegensatz dazu können Sie die Reihenfolge der Elemente eines Arrays bestimmten - denn Sie sind normalerweise derjenige, der den Array mit Elementen füllt. Außerdem haben Sie die Möglichkeit auf einzelne Elemente eines Arrays zuzugreifen, ohne das gesamte Array durchlaufen zu müssen. Das liegt daran, dass jedem Element eines Arrays eine Indexnummer zugewiesen wird. Unter VBScript hat das erste Element eines Arrays immer die Indexnummer 0 - die nachfolgenden Elemente erhalten fortlaufende Indexnummern (1, 2, 3 usw.). Der Array aus Script 2.12 enthält nach diesen Regeln also die in Tabelle 2.3 gezeigten Elemente und Indexnummern. Sie sollten sich merken, dass die höchste Indexnummer eines Arrays immer der Gesamtzahl von Elementen minus eins entspricht. Tabelle 2.3: Indexnummern in einem Array IndexnummerElement
0
atl-dc-01
1
atl-dc-02
2
atl-dc-03
Sie können die Indexnummern verwenden, um auf die einzelnen Elemente eines Arrays zuzugreifen. Die folgende Codezeile gibt zum Beispiel den Text atl-dc-02 aus - der Wert von Element 1 (genauer: das Element mit der Indexnummer 1) im Array: Wscript.Echo Computers(1)
Um den Wert eines anderen Elementes auszugeben, ersetzten Sie den Wert 1 einfach durch die entsprechende Indexnummer. Später in diesem Kapitel werden noch weitere Verfahren zum Erstellen von Arrays und dem Zugriff auf einzelne Array-Elemente besprochen.
Eingabe Script 2.12 wurde für eine Organisation entwickelt, in der sich die Computer-Infrastruktur wahrscheinlich nicht ändern wird. Es muss wohl kaum erwähnt werden, dass eine solche statische Infrastruktur eher die Ausnahmen als die Regel darstellt. In den meisten Organisationen gibt es eine viel dynamischere Umgebung. Auch wenn heute nur drei Server überwacht werden müssen (atl-dc-01, atl-dc-02, atl-dc-03), so gibt es doch keine Garantie dafür, dass sich dies nicht in Zukunft ändert. Aus diesem Grund möchten wir die Computernamen nicht fest in das Script integrieren (hardcodieren). Eine solche Vorgehensweise führt oft zu Problemen: •
Fehlende Flexibilität - Script 2.12 fragt nur die Computer atl-dc-01, atl-dc-02 und atl-dc-03 ab. Wenn Sie den freien Plattenplatz von Computer atl-dc-04 abfragen möchten, dann Seite 36 von 394
müssen Sie das Script verändern. •
Regelmäßige Änderungen - Script 2.12 wurde entwickelt, um eine bestimmte Gruppe von Computern abzufragen (zum Beispiel alle Domänencontroller an einem bestimmten Standort). Jedes Mal, wenn ein neuer Domänencontroller hinzugefügt wird, muss das Script geändert werden. Wenn in Ihrer Organisation nur dieses eine Script verwendet wird, ist das sicher kein allzu großes Problem. Wenn Sie jedoch Dutzende von Scripten verwenden, dann verbringen Sie möglicherweise mehr Zeit mit der Pflege von Scripten, als Sie durch die Scripte einsparen.
Es gibt einige Arten, auf die Sie Informationen (zum Beispiel Computernamen) an ein Script übergeben können (weitere Informationen hierzu finden Sie im Kapitel Creating Enterprise Scripts in Teil 3 dieses Buchs). Der wahrscheinlich einfachste Weg ist es, den Benutzer beim Aufruf des Scripts Parameter (auch Argumente genannt) angeben zu lassen. Ein Argument ist eine Information, die zusammen mit dem Befehl, über den das Script gestartet wird, angegeben wird. Nehmen wir einmal an, Sie starten ein Script normalerweise, indem Sie folgenden Befehl in der Eingabeaufforderung eingeben: cscript FreeDiskSpace.vbs Argumente sind alle Informationen, die Sie noch an diesen Befehl anhängen. Ein Befehl mit drei Argumenten (ein Computername ist jeweils ein Argument) sieht zum Beispiel so aus: cscript FreeDiskSpace.vbs atl-dc-01 atl-dc-02 atl-dc-03 Sie müssen Ihrem Script natürlich auch Code hinzufügen, der diese Argumente verarbeitet. Im Kapitel Der WSH wird dies sehr detailliert besprochen - in Script 2.13 sehen Sie daher nur ein einfaches Beispiel. In Zeile 9 dieses Scripts wird eine For-Each-Schleife verwendet, um die vom Benutzer beim Start des Scripts angegebenen Argumente durchzugehen. Die einzelnen Argumente werden jeweils der Variable Computer zugewiesen. Mit Hilfe dieser Variable wird dann in Zeile 10 eine Verbindung zum WMI-Dienst dieses Computers aufgebaut. Script 2.13: Eingaben des Benutzers verarbeiten 1 Const CONVERSION_FACTOR = 1048576 2 Const WARNING_THRESHOLD = 100 3 4 If WScript.Arguments.Count = 0 Then 5 Wscript.Echo "Usage: FirstScript.vbs server1 [server2] [server3] ..." 6 WScript.Quit 7 End If 8 9 For Each Computer In WScript.Arguments 10 Set objWMIService = GetObject("winmgmts://" & Computer) 11 Set colLogicalDisk = objWMIService.InstancesOf("Win32_LogicalDisk") 12 For Each objLogicalDisk In colLogicalDisk 13 FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 14 If FreeMegaBytes < WARNING_THRESHOLD Then Wscript.Echo Computer & " " & objLogicalDisk.DeviceID & _ 15 16 " is low on disk space." 17 End If Next 18 19Next
Seite 37 von 394
Ein Vorteil von Befehlszeilenargumenten ist, dass sie automatisch über eine Collection bereitgestellt werden (Wscript.Arguments). So ist es einfach, die Argumente über eine ForEach-Schleife durchzugehen - denn dieser Vorgang unterscheidet sich nicht von der Auflistung aller Laufwerke eines Computers. Dank der Collection ist es auch ganz einfach festzustellen, wie viele Argumente angegeben wurden - wenn überhaupt welche angegeben wurden. Hierzu wird in Zeile 4 des Scripts das Attribut Wscript.Arguments.Count verwendet. In ihm steht die Zahl der bei Scriptstart übergebenen Argumente. Wenn Wscript.Arguments.Count den Wert 0 hat, dann wurden keine Argumente übergeben. In diesem Fall wird in 2.13 einfach ein Text ausgegeben, der die möglichen Argumente des Scripts anzeigt. Danach wird das Script mit dem Befehl WScript.Quit beendet.).
Fehlerbehandlung Script 2.13 enthält einen potentiellen Fehler. Nehmen wird einmal an, der Benutzer gibt einen ungültigen Computernamen als Argument an. Wenn das Script dann versucht eine Verbindung mit diesem Computer aufzubauen, wird dies mit der Fehlermeldung "Der Remoteservercomputer existiert nicht oder ist nicht verfügbar: ,GetObjekt'" abgebrochen. Solche Fehler sind natürlich in allen bis jetzt in diesem Kapitel vorgestellten Scripten möglich - auch wenn die Computernamen fest eingetragen (hardcodiert) wurden. Schließlich kann ein Script nicht zwischen einem ungültigen Computernamen und einem gültigen Computernamen, der nur im Moment nicht über das Netzwerk erreichbar ist, unterscheiden. Stellen Sie sich zum Beispiel vor, Computer atl-dc-01 aus Script 2.12 ist im Moment nicht erreichbar. In diesem Fall wird das Script bei der Abfrage dieses Computers mit einer Fehlermeldung abbrechen. Leider werden auch die beiden anderen Computer nicht mehr abgefragt - selbst wenn diese einwandfrei zu erreichen sind (das Script wird ja schon beim ersten Computer wegen des Fehlers beendet). Das beschriebene Verhalten ist ein gutes Beispiel für einen Laufzeitfehler - ein Fehler, der erst nach dem Start des Scripts auftritt (im Gegensatz zu einem Syntaxfehler - zum Beispiel einem falsch geschriebenen Befehl, der bereits vor der Ausführung der ersten Scriptzeile auftritt). Um sich vor solchen Laufzeitfehlern zu schützen, können Sie den VBScript-Befehl zur Fehlerbehandlung On Error Resume Next in Ihren Scripten verwenden. Ohne eine Fehlerbehandlung wird das Script bei einem Laufzeitfehler sofort angehalten. Mit Fehlerbehandlung wird das Script nicht angehalten. Stattdessen wird versucht, einfach die nächste Zeile des Scripts auszuführen. Die Scriptzeile, die den Fehler generiert hat, wird einfach ignoriert.
Das Err-Objekt Der Befehl On Error Resume Next bewirkt, dass das Script nach einem Laufzeitfehler weiter ausgeführt wird. Hierbei gibt es jedoch mindestens zwei potentielle Probleme. Ersten wird keine Fehlermeldung angezeigt. Sie merken also nicht, dass ein Fehler aufgetreten ist und können nicht feststellen, ob das Script fehlgeschlagen ist. Und zweitens könnte es ja sein, dass Sie nicht möchten, dass das Script nach einem Fehler weiter ausgeführt wird. Stellen Sie sich folgende Script-Funktionalität vor, die den Befehl On Error Resume Next verwendet: Seite 38 von 394
1.Eine Verbindung zu einem Remotecomputer herstellen. 2.Dateien vom lokalen Computer auf den Remotecomputer kopieren. 3.Die Originaldateien vom lokalen Computer löschen. Stellen Sie sich nun vor, Sie starten das Script, und der Remotecomputer ist nicht erreichbar. Folgendes würde passieren: 1.Das Script versucht eine Verbindung zum Remotecomputer herzustellen. Dies schlägt fehl. On Error Resume Next stellt jedoch sicher, dass das Script weiter ausgeführt wird. 2.Das Script versucht, Dateien auf den Remotecomputer zu kopieren. Dies schlägt natürlich ebenfalls fehl. Auch hier stellt On Error Resume Next sicher, dass das Script weiter ausgeführt wird. 3.Das Script löschte die Dateien vom lokalen Computer. Unglücklicherweise wird das sauber ausgeführt. Der lokale Computer steht ja zur Verfügung. Als Ergebnis sind die Dateien vom lokalen Computer gelöscht und nicht auf den Remotecomputer kopiert. Glücklicherweise können Sie das VBScript-Objekt Err verwenden, um festzustellen, ob ein Fehler aufgetreten ist. Danach können Sie die erforderlichen Maßnahmen ergreifen. Das Err-Objekt wird automatisch erstellt, sobald Sie das Script starten (solche immer verfügbaren Objekte werden auch Intrinsic-Objekte genannt). Es stellt mehrere Attribute zur Verfügung. Drei von ihnen sehen Sie in Tabelle 2.4. Wenn ein Laufzeitfehler auftritt, werden diese Attribute automatisch mit Werten gefüllt. Diese Werte zeigen Ihnen, was für ein Fehler aufgetreten ist. Tabelle 2.4: Attribute des Err-Objektes Attribut
Beschreibung
DescriptionEine Beschreibung des Fehlers. Sie kann dazu verwendet werden, den Benutzer über den Fehler zu informieren. Geben Sie das Attribut einfach auf dem Bildschirm aus: Wscript.Echo Err.Description Number
Ein Integer-Wert (Ganzzahl), der den aufgetretenen Fehler eindeutig identifiziert. Es kann sich um eine VBScript-Fehlernummer oder um die Fehlernummer eines Automatisationsobjektes handeln. Die Herkunft der Fehlernummer können Sie über das Attribut Source feststellen.
Source
Klassenname oder ProgID (Programmatic Identifier) des Objektes, das den Fehler verursacht hat. Wenn VBScript den Fehler verursacht hat, dann enthält das Attribut den Wert 'Laufzeitfehler in Microsoft VBScript'. Wenn ein Automatisationsobjekt den Fehler verursacht hat, dann finden Sie hier die ProgID dieses Objektes (zum Beispiel 'Word.Application').
Wenn ein Script gestartet wird, dann weist VBScript dem Attribut Number den Standardwert 0 zu. Wenn ein Fehler auftritt, dann wir dem Attribut sofort die Fehlernummer zugewiesen. Wenn Sie also den Wert des Attributes Number regelmäßig abfragen, dann können Sie feststellen, ob ein Fehler aufgetreten ist. Sie können Ihr Script zum Beispiel so gestalten, dass es nach einem Verbindungsversuch mit einem Remotecomputer den Fehlerstatus im Attribut Number prüft. Wenn Err.Number nicht mit 0 ist, dann ist irgendein Fehler aufgetreten - und Seite 39 von 394
Sie können davon ausgehen, dass der Verbindungsversuch mit dem Remotecomputer fehlgeschlagen ist. Ihr Script kann dann hierauf reagieren. Script 2.14 implementiert eine solche Fehlerbehandlung. In Zeile 1 wird die Fehlerbehandlung erst einmal durch On Error Resume Next aktiviert. In Zeile 10 wird eine For-Each-Schleife verwendet, um die Servernamen durchzugehen. In Zeile 11 versucht das Script jeweils eine Verbindung mit dem Server aufzubauen. Was passiert nun, wenn einer der Server nicht erreichbar ist? In Zeile 11 versucht das Script auf den Server zuzugreifen. Wenn die Verbindung erfolgreich war, dann wird kein Fehler generiert. In diesem Fall steht in Err.Number noch immer der Wert 0. Wenn der Verbindungsversuch jedoch fehlschlägt, dann wird ein Fehler generiert. In Err.Number steht dann eine Fehlernummer. In einem solchen Fall steht der Befehl On Error Resume Next sicher, dass das Script in der nächsten Zeile weiter ausgeführt wird. In Zeile 12 prüft das Script den Wert in Err.Number. Wenn dieser Wert nicht 0 ist (wenn also ein Fehler aufgetreten ist), dann gibt das Script den Wert in Err.Description aus. Danach geht es mit einem neuen Schleifendurchlauf normal weiter. Wenn der Wert in Err.Number jedoch 0 ist, dann bedeutet das, dass die Verbindung erfolgreich war. In diesem Fall fragt das Script den verfügbaren Plattenplatz ab und startet dann ganz normal einen weiteren Schleifendurchlauf mit dem nächsten Servernamen. Script 2.14: Fehler abfangen 1 On Error Resume Next 2 Const CONVERSION_FACTOR = 1048576 3 Const WARNING_THRESHOLD = 100 4 5 If WScript.Arguments.Count = 0 Then 6 Wscript.Echo "Usage: FirstScript.vbs server1 [server2] [server3] ..." WScript.Quit 7 8 End If 9 10For Each Computer In WScript.Arguments 11 Set objWMIService = GetObject("winmgmts://" & Computer) If Err.Number <> 0 Then 12 13 Wscript.Echo Computer & " " & Err.Description 14 Err.Clear Else 15 Set colLogicalDisk = _ 16 17 objWMIService.InstancesOf("Win32_LogicalDisk") 18 For Each objLogicalDisk In colLogicalDisk FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR 19 If FreeMegaBytes < WARNING_THRESHOLD Then 20 Wscript.Echo Computer & " " & objLogicalDisk.DeviceID & _ 21 22 " is low on disk space." 23 End If Next 24 25 End If 26Next
Wenn Sie Script 2.14 ausführen, und einer der Server nicht erreichbar ist, dann enthalten die Attribute des Err-Objektes die in Tabelle 2.5 zu sehenden Werte. Tabelle 2.5: Werte der Attribute des Err-Objektes Attribut
Wert
Err.DescriptionDer Remoteservercomputer existiert nicht oder ist nicht verfügbar: ,GetObjekt' Seite 40 von 394
Attribut
Wert
Err.Number
462
Err.Source
Laufzeitfehler in Microsoft VBScript
Fehler löschen Script 2.14 verwendet in Zeile 14 die Methode Clear, um die Attribute des Err-Objektes explizit zurückzusetzen. Dies ist wichtig, da sich diese Attribute ansonsten nur dann ändern würden, wenn ein anderer Fehler auftritt. Wenn kein Fehler auftritt, dann würden die Werte der Attribute gleich bleiben. Das Script würde also bei jedem Durchlauf einen Fehler erkennen - auch dann, wenn gar kein Fehler aufgetreten ist (der Wert des Attributes Err.Number ist ja nicht 0). Mit der Methode Clear umgehen Sie dieses Problem, indem Sie alle Attribute des ErrObjektes auf die Standardwertezurücksetzen (Number ist 0, Source und Description sind leer).
VBScript-Referenz Die erste Hälfte dieses Kapitels hat Sie mit den grundlegenden Konzepten hinter VBScript vertraut gemacht. Sie haben einen ersten Einblick darin erhalten, welche Aufgaben Sie mit VBScript durchführen können - besonders im Hinblick auf die Verwendung von ADSI und WMI. Die zweite Hälfte dieses Kapitels ist mehr als Standardreferenz gedacht. Es konzentriert sich auf die Methoden und Funktionen von VBScript, die für Systemadministratoren am nützlichsten sind. Es handelt sich nicht um eine umfassende Übersicht aller Dinge, die Sie mit VBScript machen können. Wenn Sie so eine Auflistung benötigen, so finden Sie diese im MSDN unter http://msdn.microsoft.com/library/default.asp?url=/library/enus/script56/html/vtoriVBScript.asp?frame=true (englischsprachig). Die folgenden Abschnitte besprechen nur eine Untermenge aller VBSCript-Funktionen und -Methoden. Diese werden in einem Kontext verwendet, der Sie bei der Entwicklung von Scripten zur Systemadministration unterstützen soll. Zeilenumbrüche in VBScript Viele Script- und Programmiersprachen interessieren sich nicht dafür, ob der Code in eine physikalische Zeile oder in viele Zeilen geschrieben wurde. Das folgende Microsoft® JScript®- Codebeispiel umfasst beispielsweise zwar neun Zeilen - JScript behandelt es aber trotzdem wie eine einzige Zeile (und damit auch wie einen einzigen Befehl). Das liegt daran, dass JScript ein Zeilenende an einem bestimmten Zeichen erkennt (dem Semikolon). Die tatsächlichen Zeilen (also die Zeilenumbrüche) sind für JScript vollkommen irrelevant. var objWMI = new Enumerator (GetObject("winmgmts:") . InstancesOf("Win32_process")) ;
Ein Ähnliches Stück Code in VBScript wird dagegen zu einem Syntax-Error führen: Set
Seite 41 von 394
objWMI = (GetObject("winmgmts:") . InstancesOf("Win32_process"))
Das liegt daran, dass VBScript das Zeilenende (und damit normalerweise auch das Ende eines Befehls) am Zeilenumbruch statt an einem bestimmten Zeichen erkennt. Im Allgemeinen ist dies ein Vorteil von VBScript. Es kann zum Beispiel nicht passieren, dass Sie vergessen das Zeilenendezeichen (das Semikolon) einzugeben. Ein Problem gibt es aber trotzdem: Um die Lesbarkeit Ihrer Scripte zu verbessern, sollten Sie keine Zeilen verwenden, die länger als max. 80 Zeichen sind (in einigen Texteditoren können Sie möglicherweise gar nicht mehr als 80 Zeichen in einer Zeile eingeben). Was machen wir also, wenn eine Zeile 100 Zeichen enthalten muss? Auch wenn es nahe liegend erscheint: Sie können die Zeile nicht einfach durch einen Zielenumbruch in zwei Zeilen aufteilen. Das folgende Codestück würde unter VBScript zum Bespiel zu einem Fehler führen, da die erste Befehlszeile durch einen Zeilenumbruch in zwei einzelne Zeile aufgeteilt wurde (Zeile 1 und 2): strMessageToDisplay = strUserFirstName, strUserMiddleInitial, strUserLastName, strCurrentStatus Wscript.Echo strMessageToDisplay
In diesem Fall würde nun jede der beiden Zeilen als einzelner Befehl interpretiert. Die erste Zeile mag ja auch noch immer einen gültigen Befehl enthalten (eine Zuweisung zu einer Variable). Die zweite Zeile jedoch ist kein gültiger Befehl mehr. Um einen Befehl über mehrere Zeile fortzusetzen, müssen Sie statt des Zeilenumbruchs den Unterstrich verwenden (_). strMessageToDisplay = strUserFirstName, strUserMiddleInitial, strUserLastName, _ strCurrentStatus Wscript.Echo strMessageToDisplay
Das Leerzeichen und der Unterstrich am Ende von Zeile 1 bedeuten für VBScript, dass der Befehl in der nächsten Zeile fortgesetzt wird. Um dies auch für den Leser zu verdeutlichen, wurde Zeile zwei um vier Zeichen eingerückt. Wenn Sie versuchen eine Befehlszeile mit Zeichenketten (die in Anführungsstriche eingefasst sind) zu trennen, dann wird dies noch etwas komplexer. Stellen Sie sich zum Beispiel vor, sie teilen eine WMI-Abfrage folgendermaßen durch einen Unterstrich in zwei Zeilen auf: Set colServiceList = GetObject("winmgmts:").ExecQuery("SELECT * FROM Win32_Service WHERE State = 'Stopped' AND StartMode = 'Auto' ")
_
Wenn sie das Script so ausführen, dann erhalten Sie wieder einen Laufzeitfehler. Das liegt daran, dass Sie die Befehlszeile innerhalb einer Zeichenkette getrennt haben, die in Anführungsstrichen eingeschlossen war (und der trennende Unterstrich daher nur als Teil der Zeichenkette statt als Trennzeichen behandelt wird). Um eine solche Befehlszeile zu trennen gehen Sie folgendermaßen vor: 1.Beenden Sie die erste Zeile mit einem Anführungszeichen. Hängen Sie dann das Leerzeichen und den Unterstrich dahinter. 2.Beginnen Sie die zweite Zeile mit einem kaufmännischen "Und" (&). Dies zeigt VBScript, dass hier die unterbrochene Zeichenkette aus Zeile 1 fortgesetzt wird. Seite 42 von 394
3.Beginnen sie die fortgesetzte Befehlszeile mit einem weiteren Anführungszeichen. Das "&" und die Anführungsstriche zeigen VBScript, dass die in Zeile 1 begonnene Zeichenkette mit der folgenden Zeichenkette fortgesetzt werden soll. Die korrekte Befehlszeile muss also so aussehen: Set colServiceList = GetObject("winmgmts:").ExecQuery("SELECT * FROM " _ & "Win32_Service WHERE State = 'Stopped' AND StartMode = 'Auto' ")
Wenn Sie Befehlszeilen auf diese Art teilen, dann sollten Sie aufpassen, wo Sie die Leerzeichen setzen. Im gezeigten Beispiel wurde zum Beispiel ein Leerzeichen hinter dem Wort 'FROM' und vor dem schließenden Anführungszeichen eingefügt. Wenn dieses Leerzeichen nicht vorhanden wäre, dann würde die Zeichenkette so aussehen: " SELECT * FROMWin32_Service WHERE State = 'Stopped' AND StartMode = 'Auto' "
Natürlich würde dies zu einem Fehler führen, "FROMWIn32"kann nicht richtig sein.
Arbeiten mit Variablen Variablen sind benannte Speicherstellen im Speicher des Computers. Die meisten Scriptsprachen erlauben eine implizite Deklaration von Variablen. Sie können Variablen also verwenden, ohne deren vorgesehene Nutzung zu deklarieren. Sie können das folgende Script zum Beispiel ausführen, ohne dass es zu einem Fehler kommt, und das, obwohl in der ersten Zeile des Scripts der Wert 11 zur Variable sngDegreesCelsius zugewiesen wird und die Variable vorher nicht deklariert wurde (VBScript weiß also vorher nichts von deren Existenz). sngDegreesCelsius = 11 sngDegreesFahrenheit = ConvertToFahrenheit(sngDegreesCelsius) Wscript.Echo sngDegreesFahrenheit Function ConvertToFahrenheit(ByVal sngDegreesCelsius) ConvertToFahrenheit = (sngDegreesCelsius * (9/5)) + 32 End Function
Durch eine implizite Variablendeklaration kann das Entwickeln von Scripten schneller und einfacher werden. Sie kann jedoch auch zu sehr subtilen Fehlern führen, die schwer zu finden und zu beheben sind. Das folgende Script illustriert solche Probleme. Das vorherige Script konvertierte 11° Celsius in Fahrenheit (51.8°). Das folgende Script macht genau das gleiche. Leider gibt es 32 statt 51.8 zurück. sngDegreesCelsius = 11 sngDegreesFahrenheit = ConvertToFahrenheit(sngDegreesCelsius) Wscript.Echo sngDegreesFahrenheit Function ConvertToFahrenheit(ByVal sngDegreesCelsius) ConvertToFahrenheit = (sngDegresCelsius * (9/5)) + 32 End Function
Warum gibt das Script einen falschen Wert zurück? Das Problem liegt in einem einfachen Tippfehler. In Zeile 6 sollte die Variable sngDegreesCelsius verwendet werden. Stattdessen hat der Entwickler jedoch sngDegresCelsius (ein e fehlt im Wort Degrees). Das führt dazu, dass die Berechnung den Wert in der Variable sngDegresCelsius statt jenem in sngDegreesCelsius verwendet. Da der Variablen sngDegresCelsius nie ein Wert zugewiesen wurde, enthält sie den Standardwert 0. Als Konsequenz wird der Wert 0 mit 9/5 multipliziert das Ergebnis ist 0. Das Script addiert dann 32 zu diesem Wert und gibt das falsche Endergebnis 32 zurück. Seite 43 von 394
Solche Fehler können wirklich schwer zu finden sein. Der Syntax ist korrekt, daher wird keine Fehlermeldung generiert. Sie erwarten einen numerischen Wert, und Sie erhalten auch einen. In einem größeren Script könnte die Suche nach einem Tippfehler sehr lange dauern.
Variablen unter VBScript deklarieren Um Probleme wie das oben demonstrierte zu vermeiden, können Sie alle Variablen explizit deklarieren. In diesem Fall führen alle nicht deklarierten Variablen zu einem Laufzeitfehler. In dem folgenden Script wird zum Beispiel über den Befehl Option Explicit eine explizite Variablendeklaration erzwungen und jede Variable wird über den Befehl Dim deklariert: Option Explicit Dim sngDegreesCelsius Dim sngDegreesFahrenehit sngDegreesCelsius = 11 sngDegreesFahrenheit = ConvertToFahrenheit(sngDegreesCelsius) Wscript.Echo sngDegreesFahrenheit Function ConvertToFahrenheit(ByVal sngDegreesCelsius) ConvertToFahrenheit = (sngDegresCelsius * (9/5)) + 32 End Function
Wenn das Script ausgeführt wird, findet der Scripting-Host eine undeklarierte Variable. Daher wird die Ausführung des Scripts angehalten, und es wird ein Fehlermeldung wie die folgende angezeigt: C:\Scripts\TempConvert.vbs(10, 5) Laufzeitfehler in Microsoft VBScript: Variable ist nicht definiert: 'sngDegresCelsius'
Um in VBScript Variablen zu deklarieren, gehen Sie folgendermaßen vor: 1.Verwenden Sie den Befehl Option Explicit, um die Deklaration von Variablen zu erzwingen. Der Befehl muss in der ersten Zeile Ihres Scripts stehen. 2.Verwenden Sie den Befehl Dimzur Deklaration von Variablen. Auch wenn Sie mit einem Dim-Befehl mehrere Variablen deklarieren können, sollten Sie sich doch auf eine Variable pro Dim-Befehl beschränken. Dies ermöglicht es Ihnen, einen Kommentar hinter der Deklaration einzufügen, indem Sie die Verwendung der Variable beschreiben können. Das Ergebnis könnte so aussehen: Option Explicit Dim intFirstNumber ' First number in our simple equation Dim intSecondNumber ' Second number in our simple equation Dim intTotal ' Sum of intFirstNumber and intSecondNumber
Initialisierung von Variablen Initialisieren von Variablen bedeutet ganz einfach, dass Sie den Variablen schon zu Beginn einen Wert zuweisen. Die folgenden beiden Codezeilen initialisieren zum Beispiel zwei Variablen. X wird der Wert 100 zugewiesen und Y der Wert abcde: X = 100 Y = 'abcde'
Wenn Sie Variablen deklarieren, diese aber nicht initialisieren (ihnen also keinen Wert zuweisen), dann hat die Variable einen von zwei Standardwerten: •
Wenn die Variable als Zeichenkette (String) verwendet wird, dann hat sie den Wert Leer Seite 44 von 394
(Empty). •
Wenn die Variable als Zahl verwendet wird, dann hat Sie den Wert 0.
Das folgende Script deklariert zum Beispiel zwei Variablen (X und Y), weist diesen jedoch keinen Wert zu: Dim X Dim Y Wscript.Echo X & Y Wscript.Echo X + Y
In Zeile 3 werden die beiden Variablen als Zeichenketten verwendet (der Operator "&" verbindet zwei Zeichenketten zu einer). Das Ergebnis dieser Zeile sehen Sie in Abbildung 2.8. Da beide Variablen leer sind, ist auch die Kombination aus beiden leer. Das Fenster zeigt also eine leere Zeichenkette an.
Abbildung 2.8: Verbinden zweier nicht initialisierter Variablen In Zeile 4 werden die beiden Variablen als Zahlen verwendet. Numerische Variablen, die nicht initialisiert wurden, haben automatisch den Wert 0. Daher gibt diese Zeile des Scripts auch die Summe aus 0 + 0 zurück.
Abbildung 2.9: Addition zweier nicht initialisierter Variablen Verwendung des Gleichheitszeichens in VBScript In VBScript hat das Gleichheitszeichen (=) unterschiedliche Bedeutungen. Normalerweise wird der Ausdruck X = 2 + 2 so gelesen: 'X gleich 2 plus 2.' In VBScript wird er allerdings so gelesen: 'X wird der Wert von 2 plus 2 zugewiesen.' Anscheinend gibt es keinen großen Unterschied. In beiden Fällen wird X der Wert 4 zugewiesen. Sehen Sie sich jedoch einmal folgendes Script an. Es zählt in einer Schleife von 1 bis 10: For i = 1 to 10 X = X + 1 Next
Die zweite Zeile des Scripts scheint mathematisch unmöglich. Wie kann X gleich X plus 1 sein? Unter VBScript ist dies jedoch ein gültiger Ausdruck. Es handelt sich nicht um eine Seite 45 von 394
mathematische Berechnung. Stattdessen wird einfach nur der Variable X ein neuer Wert zugewiesen. Der Ausdruck liest sich also so: 'X wird der momentane Wert von X plus 1 zugewiesen.' In anderen Worten, wenn X im Moment 3 ist, denn wird X mit diesem Ausdruck der neue Wert 4 zugewiesen - 3 (der momentane Wert von X) plus 1. Das Gleichheitszeichen ist also tatsächlich ein Zuweisungszeichen. Sie können es auch zum Erstellen von Zeichenketten verwenden. Das folgende Script konstruiert zum Beispiel eine Meldung aus mehreren Zeichenketten: Message = "Dies " Message = Message & "ist eine " Message = Message & "Testmeldung." Wscript.Echo Message
Die Ausgabe des Scripts sehen Sie in Abbildung 2.10.
Abbildung 2.10: Zusammengefasste Meldung
Verwendung von Konstanten Konstanten sind Werte, die während der Laufzeit eines Scripts nicht geändert werden können. Nehmen Sie zum Beispiel an, dass eines Ihrer Scripte US-Dollar in Euro konvertiert. Wenn wir davon ausgehen, dass der Wechselkurs für einen Dollar 0,84 Euro beträgt, dann können Sie diesen Wert fest in Ihr Script implementieren (hardcodieren): curConvertedPrice = curPriceInEuro * 0.84
Auch wenn dieser Ansatz funktioniert gibt es doch einige potentielle Probleme: • •
•
Ein anderer Script-Entwickler weiß möglicherweise nicht, wofür der Wert 0.84 steht. Wechselkurse ändern sich regelmäßig. Wenn Sie den Wert direkt in Ihr Script eintragen, dann müssen Sie bei einer Änderung das ganze Script durchsuchen und an jeder Stelle den Wert ändern. Wenn Sie auch nur eine Stelle übersehen, an der der Wert verwendet wird, dann kann dies zu falschen Berechnungen führen. Jedes Mal, wenn Sie den Wert per Hand im Script eingeben oder ändern, dann besteht die Gefahr von Tippfehlern. Wenn Sie zum Beispiel versehentlich 0.084 eingehen, dann wird das drastische Auswirkungen auf die Berechnung haben.
Um solche Probleme zu vermeiden, sollten Sie statt hardcodierter Werte Konstanten verwenden. Diese bieten Ihnen einige Vorteile: • •
Sie können einer Konstante einen eindeutigen Namen geben. Statt der Zahl 0.84 können Sie so zum Beispiel den Namen US_DOLLAR_IN_EURO_WECHSELKURS verwenden. Sie sind einfach zu ändern. Wenn sich der Wechselkurs ändert, dann ändern Sie nur die Seite 46 von 394
Stelle, an der die Konstante definiert wird. • • •
Da Sie den Wert nur einmal eingeben müssen, sind sie weniger anfällig für Tippfehler. Sie können nicht versehentlich geändert werden. Nachdem eine Konstante definiert wurde, führt jeder Versuch im Script den Wert der Konstante zu ändern, zu einem Fehler. Sie können für Zeichenketten und für Zahlenwerte verwendet werden.
Definieren von Konstanten Unter VBScript werden Konstanten mit dem Schlüsselwort Const gefolgt von dem Namen und dem Wert der Konstante, definiert. Sie müssen der Konstante bei deren Definition einen Wert zuweisen; es ist nicht möglich, der Konstante über eine Variable, eine andere Konstante oder über eine Funktion einen Wert zuzuweisen. Das folgende Beispiel versucht, eine Konstante mit der Variable NumberOfDepartments zu definieren und verursacht den Fehler "Literalkonstante erwartet": NumberOfDepartments = 20 Const NUMBER_OF_DEPARTMENTS = NumberOfDepartments
Der Konstante muss stattdessen der Wert 20 zugewiesen werden: Const NUMBER_OF_DEPARTMENTS = 20
Vordefinierte Konstanten verwenden VBScript umfasst bereits einige vordefinierte Konstanten. Diese können Sie zum Beispiel für Nachrichtenfenster, Ausgaben oder andere Aktivitäten verwenden. Das folgende Script zeigt zum Beispiel ein Nachrichtenfenster an. Die im Nachrichtenfenster angezeigten Schalter werden hierbei durch einen numerischen Wert festgelegt. Auch wenn das Script problemlos funktioniert, ist es doch schwer festzustellen, was genau das Script macht. Sie müssen wissen, dass die Zahl 260 bedeutet, dass ein Nachrichtenfenster mit den Schaltern Ja und Nein erstellt werden soll, bei dem der Schalter Nein standardmäßig den Focus hat. Die Zahl 7 bedeutet, dass der Benutzer auf den Schalter Nein geklickt hat. ConfirmDelete = MsgBox ("Möchten Sie die Dateien löschen? ", 260, "Alle Dateien löschen") If ConfirmDelete = 7 then Wscript.Quit End If
_
Die folgende Variante des Scripts verwendet statt Zahlen die vordefinierten Konstanten von VBScript (VbYesNo, VBDefaultButton2 und VbNo). Das Script wird so besser lesbar und verständlicher. ConfirmDelete = MsgBox ("Möchten Sie die Dateien löschen? ", VbYesNo OR VBDefaultButton2, "Alle Dateien löschen") If ConfirmDelete = VbNo then Wscript.Quit End If
_
Mit Konstanten wird es außerdem einfacher das Script in einer anderen Programmiersprache umzusetzen. VBScript verwendet für die Konstante True zum Beispiel den Wert -1; in Visual Basic hat True aber den Wert 1. Wenn Sie eine Konstante statt des Wertes verwendet haben, dann müssen Sie sich über dieses Problem keine Sorgen machen, wenn Sie das Script übertragen sollten. Die zwei am häufigsten verwendeten vordefinierten Konstanten sind wahrscheinlich: Seite 47 von 394
•
VbCrLf - Diese Konstante steht für einen Zeilenumbruch. Sie wird meist zur Formatierung von Ausgaben verwendet. Das folgende Script produziert zum Beispiel einen mehrzeiligen Nachrichtentext: Wscript.Echo 'Erste Textzeile.' & VbCrLF & VbCrLF & _ 'Zweite Textzeile.' Wenn Sie das Script mit Wscript ausführen, dann sehen Sie ein Nachrichtenfenster, das wie in Abbildung 2.11 aussieht.
Abbildung 2.11: Mit VbCrLf getrennter Text •
VbTab - Diese Konstante entspricht einem Tabulatorzeichen. Das folgende Script produziert zum Beispiel drei durch Tabulatoren getrennte Textspalten: Wscript.Echo ' 1' & VbTab & ' 2' & VbTab & ' 3' Wscript.Echo 'A' & VbTab & 'B' & VbTab & 'C' Wscript.Echo 'D' & VbTab & 'E' & VbTab & 'F' Wenn Sie das Script mit Cscript ausführen, dann sieht seine Ausgabe in der Eingabeaufforderung so aus: 123 ABC DEF
Scripte, die in VBScript geschrieben werden, können auch nur auf die vordefinierten Konstanten von VBScript zugreifen. Sie haben keinen Zugriff auf die vordefinierten Konstanten von WMI, ADSI, der Script-Laufzeitbibliothek oder externen Automatisationsobjekten. Alle VBSCript-Konstanten (zum Beispiel VbCrLf oder VbYesNo) können Sie nutzen, ohne diese vorher definieren zu müssen. Wenn Sie jedoch WMI- oder ADSI-Konstanten nutzen möchten, dann müssen Sie diese vorher selbst definieren. Das Objekt Drive aus der Script-Laufzeitbibliothek verfügt zum Beispiel über die Konstante mit dem Namen Fixed. Da VBScript jedoch auf die Konstanten dieses Automatisationsobjektes keinen Zugriff hat, müssen Sie die Konstante Fixed selbst mit einem entsprechenden Wert definieren. Wenn Sie die Konstante nicht vorher definieren, dann wird das Script einen Fehler produzieren oder fehlerhaft arbeiten. Wenn Sie zum Beispiel versuchen das folgende Script auszuführen, dann wird es alles Mögliche machen. Es wird jedoch nicht die reparierten Laufwerke abfragen: Set objFSO = CreateObject("Scripting.FileSystemObject") Set colDiskDrives = objFSO.Drives For Each objDiskDrive in colDiskDrives If objDiskDrive.DriveType = Fixed then
Seite 48 von 394
Wscript.Echo objDiskDrive.DriveLetter End if Next
Das Script arbeitet fehlerhaft, da VBScript nicht weiß, dass die Konstante Fixed den Wert 2 hat. Stattdessen behandelt VBScript Fixed als einfache Variable, der kein Wert zugewiesen wurde. Damit ist die Variable leer, und es wird nach Laufwerken gesucht, bei denen das Attribut DriveType den Wert 0 statt 2 hat. Da VBScript kein Laufwerk mit diesem Wert finden kann, wird auch nichts ausgegeben. Damit das Script funktioniert, müssen Sie eine eigene Konstante mit dem Namen Fixed und dem Wert 2 definieren: Const Fixed = 2 Set objFSO = CreateObject("Scripting.FileSystemObject") Set colDiskDrives = objFSO.Drives For Each objDiskDrive in colDiskDrives If objDiskDrive.DriveType = Fixed then Wscript.Echo objDiskDrive.DriveLetter End if Next
Datentypen unter VBScript VBScript ist eine typenlose Sprache. Das bedeutet, dass Variablen nicht auf einen einzelnen Datentyp beschränkt werden können. Stattdessen verwendet VBScript nur einen einzigen Variablentyp: Variant. Dieser kann sämtliche Datentypen speichern. Im Gegensatz dazu müssen Sie in einer Programmiersprache wie C++ den Typ der in einer Variablen zu speichernden Daten genau definieren. Wenn Sie versuchen Daten in einer Variable zu speichern, die für einen anderen Datentyp vorgesehen ist, dann kommt es zu einem Fehler. Variant-Variablen können das Entwickeln von Scripten sehr vereinfachen. Sie können Variablen deklarieren und verwenden, und müssen sich keine Gedanke über die in ihnen gespeicherten Daten machen. Wenn Sie sich jedoch nicht mit der Typumwandlung auskennen, dann kann das gleiche Prinzip aber auch zu Problemen führen. Typumwandlung Scriptsprachen scheinen nur für den Entwickler typlos zu sein. Intern müssen auch Scriptsprachen mit Datentypen arbeiten. Wenn sie auf einen einfachen Ausdruck wie x = a + b trifft, dann muss die Scriptsprache aus diesem Ausdruck zum Beispiel die Datentypen von a und b herleiten. Mit anderen Worten: Die Scriptsprache muss die beiden Werte vom Typ Variant nehmen und Werte vom Typ Integer (Ganzzahl) oder String (Zeichenkette) aus ihnen machen. Nachdem die Datentypen hergeleitet wurden, kann die Rechenoperation durchgeführt werden. Da der Wert von Typ Variant temporär in einen neuen Datentyp umgewandelt wird, wird diese Herleitung der Datentypen Typumwandlung genannt. Die Typumwandlung basiert auf bestimmen Regeln. Sie funktioniert in den allermeisten Fällen automatisch und problemlos. Die Typumwandlung kann jedoch auch zu Problemen führen. Das folgende Script ist ein Beispiel hierfür: intFirstNumber = InputBox("Erste Zahl eingeben:") intSecondNumber = InputBox("Zweite Zahl eingeben:")
Seite 49 von 394
intTotal = intFirstNumber + intSecondNumber Wscript.Echo intTotal
Wenn Sie das Script ausführen und als erste Zahl 4 und als zweite Zahl 2 eingeben, dann gibt das Script als Ergebnis der Berechnung 4 + 2 die Zahl 42. Wir hatten aber mit der Zahl 6 gerechnet. Das liegt daran, dass der Operator sowohl für Zahlen als auch für Zeichenketten verwendet werden kann. VBScript hat in diesem Fall nur zwei Werte (4 und 2). Es gibt keine Angaben dazu, was das für Werte sind. Ohne weitere Informationen führt VBScript eine Typumwandlung der beiden Variablen in einen String (Zeichenkette) durch. Wenn die beiden Zeichenketten nun verkettet werden, dann ist das Ergebnis logischerweise die neue Zeichenkette "42". Im Gegensatz dazu gibt das folgende Script den korrekten Wert (2) zurück (zumindest dann, wenn Sie die Zahlen 4 und 2 eingeben). Das liegt daran, dass der Divisionsoperator (/) nur mit Zahlen arbeiten kann. In diesem Fall führt VBScript eine Typumwandlung in Ganzzahlen (Integer) durch. intFirstNumber = InputBox("Please enter the first number:") intSecondNumber = InputBox("Please enter the second number:") intTotal = intFirstNumber / intSecondNumber Wscript.Echo intTotal
Um solche Probleme zu vermeiden, können Sie die von VBScript zu verwendenden Datentypen direkt in Ihrem Script angeben. Dies wird auch Casting oder auch explizite Typumwandlung genannt. Das folgende Script verwendet die Funktion CInt, um die Eingabevariablen vor der Berechnung in Ganzahlen (Integer) zu konvertieren: intFirstNumber = CInt(InputBox("Please enter the first number:")) intSecondNumber = CInt(InputBox("Please enter the second number:")) intTotal = intFirstNumber + intSecondNumber Wscript.Echo intTotal
In Tabelle 2.6 sehen Sie alle unter VBScript verfügbaren Funktionen zur expliziten Typumwandlung. Tabelle 2.6: VBScript-Funktionen zur expliziten Typumwandlung FunktionBeschreibung
CBool Konvertiert einen Wert größer Null in Wahr (True) und den Wert 0 zu Falsch (False). CByte Konvertiert einen Wert in den Datentyp Byte. CCur
Konvertiert einen Wert in den Datentyp Currency (Währung).
CDate Konvertiert einen Wert in den Datentyp Date (Datum). CDbl
Konvertiert einen Wert in den Datentyp Double (Fliesskommazahl).
CInt
Konvertiert einen Wert in den Datentyp Integer (Ganzzahl). Wenn der Nachkommawert einer Fliesskommazahl 5 oder größer ist, dann rundet die Funktion das Ergebnis auf. Die Fliesskommazahl 3.5 wird zum Beispiel zu 4 aufgerundet.
CLng
Konvertiert einen Wert in den Datentyp Long.
CSng
Konvertiert einen Wert in den Datentyp Single.
Seite 50 von 394
FunktionBeschreibung
CStr
Konvertiert einen Wert in den Datentyp String (Zeichenkette).
Mit leeren Variablen und Null-Variablen arbeiten Für die erfolgreiche Ausführung eines Scripts kann es wichtig sein, dass Sie den Unterschied zwischen einer leeren Variable (Empty) und einer Null-Variable (Null) verstehen. Eine leere Variable ist eine Variable, die nicht initialisiert wurde. Nach dem Ausdruck Dim curBonus ist diese Variable so lange "leer", bis Sie ihr einen Wert zugewiesen haben. Eine leere Variable hat den Wert 0, wenn Sie als Zahl verwendet wird. Wenn Sie als Zeichenkette (String) verwendet wird, hat sie hingegen den Wert '' (eine Zeichenkette der Länge null). Eine Null-Variable ist hingegen eine Variable, der kein gültiger Wert zugewiesen ist. Typischerweise entstehen Null-Variablen im Zusammenhang mit Datenbankoperationen. Stellen Sie sich zum Beispiel vor, Sie führen eine Datenbankabfrage durch und erhalten so das Feld "Bonus" eines bestimmten Mitarbeiters. Diesen Wert weisen Sie dann der Variable curBonus zu. Wenn dieser Mitarbeiter keinen Bonus erhält, dann ist die Variable in diesem Fall Null. Der Unterschied zwischen einer leeren Variable und einer Null-Variable tritt bei mathematischen Operationen auf. Im folgenden Script wird der Wert von curBonus auf Empty (leer) gesetzt. Danach wird die Variable curBonus zur Variable curBaseSalary (50.000) addiert. Das Ergebnis ist: 50,000 + 0 = 50,000. curBonus = Empty curBaseSalary = 50000 curTotalCompensation = curBaseSalary + curBonus Wscript.Echo TotalCompensation
Im nächsten Script wird genau das gleiche durchgeführt. Die Variable curBonus wird jedoch auf den Wert Null gesetzt. Bei der Berechnung erhalten Sie jedoch nicht 50.000 als Ergebnis. Stattdessen lautet das Ergebnis Null. Wenn in einer mathematischen Operation eine NullVariable verwendet wird, dann ist das Endergebnis immer Null. Das liegt daran, dass der Wert einer Null-Variable nicht bekannt ist - sie hat ja keinen Wert (stellen Sie sich das Ergebnis Null vor als "ich weiß es nicht"). curBonus = Null curBaseSalary = 50000 curTotalCompensation = curBaseSalary + curBonus Wscript.Echo TotalCompensation
Null-Werte könnten bei der Arbeit mit Datenbanken und beim Abfragen von Active Directory-Informationen über ADSI zu Problemen führen. Glücklicherweise können Sie über die Methode IsNull feststellen, ob eine Variable den Wert Null hat. Das folgende Script prüft zum Beispiel den Wert der Variable curBonus. Wenn sie den Wert Null hat, wird ihr explizit der Wert 0 zugewiesen (denken Sie daran: Null heißt "kein Wert" und 0 heißt "der Wert 0"). Somit kann die Variable dann für eine Berechnung verwendet werden. Alternativ könnten Sie sich dazu entscheiden die Berechnung nicht durchzuführen und eine Meldung wie "Dem Mitarbeiter wurden keine Bonus-Informationen zugewiesen" anzuzeigen. curBonus = Null curBaseSalary = 50000 If IsNull(curBonus) Then CurBonus = 0 End If curTotalCompensation = curBaseSalary + curBonus Wscript.Echo curTotalCompensation
Seite 51 von 394
Mit Datum- und Zeitinformationen arbeiten Daten und Uhrzeiten spielen in der Systemadministration eine wichtige Rolle. Wenn Sie zum Beispiel mit Ereignisprotokollen arbeiten, dann möchten Sie möglicherweise alle Einträge für einen bestimmten Zeitraum anzeigen. Um die Verfügbarkeit von Diensten zu prüfen, müssen Sie möglicherweise feststellen, wann ein Dienst gestartet und beendet wurde - und aus diesen beiden Werten die tatsächliche Laufzeit des Dienstes berechnen. Um sicherzustellen, dass Scripte wie geplant ausgeführt werden, müssen Sie Datum und Uhrzeit der Scriptausführung festhalten. Sie sehen also, es gibt eine Menge Anwendungen für Datum- und Zeitinformationen. VBScript bietet Ihnen mehrere unterschiedliche Wege, um Datum- und Zeitwerte zu erhalten. Außerdem stehen Ihnen mehrere Methoden zur Verfügung, um mit diesen Werten zu rechnen (zum Beispiel um die Tage zwischen zwei Daten zu berechnen). Anmerkung: Die unter VBScript verwendeten Datums- und Zeitformate unterscheiden sich deutlich von denen, die in WMI verwendet werden. Mehr zu den Formaten in WMI erfahren Sie im Kapitel WM- Scripting in diesem Buch.
Abfragen der aktuellen Uhrzeit und des Datums Oft benötigen Sie zur Systemadministration die aktuelle Uhrzeit oder das aktuelle Datum. VBScript bietet Ihnen hierzu drei Funktionen an: •
Now - gib das aktuelle Datum und die Uhrzeit zurück.
•
Date - gibt nur das aktuelle Datum zurück.
•
Time - gibt nur die aktuelle Uhrzeit zurück.
Das folgende Script verwendet die drei Funktionen und zeigt die Ergebnisse in einem Nachrichtenfenster an: DateInfo = DateInfo & Now & VbCrLf DateInfo = DateInfo & Date & VbCrLf DateInfo = DateInfo & Time & VbCrLf Wscript.Echo DateInfo
Die Ausgabe des Scripts sehen Sie in Abbildung 2.12.
Abbildung 2.12: Rückgabewerte der Funktionen Now, Date und Time
Prüfen, ob ein Wert ein gültiges Datum ist Bei der Arbeit mit Daten müssen Sie manchmal feststellen, ob ein Wert ein Datum ist oder nicht. Dies kann zum Beispiel bei WMI-Abfragen oder bei der Arbeit mit Datenbanken wichtig werden. Seite 52 von 394
Die Funktion IsDate zeigt Ihnen, ob ein Wert ein gültiges Datum ist. Sie gibt den Wert 0 (False - Falsch) zurück, wenn es sich bei dem Wert nicht um ein Datum handelt - andernfalls erhalten sie den Wert -1 (True - Wahr) zurück. Sie können der Funktion den zu prüfenden Wert auf zwei Arten übergeben: •
Als VBScript-Datumswert - Das Datum wird durch zwei Doppelkreuze (#) eingeschlossen. Dies ist die empfohlene Vorgehensweise. Sie verhindert, dass VBScript den Wert falsch interpretiert. Ein Datumswert sieht folgendermaßen aus: #9/3/2002#
•
• • • • •
Datum- und Zeitformate, wie sie durch Ihre Systemeinstellungen festgelegt sind - Wenn Ihre Systemeinstellungen beispielsweise auf Deutsch festgelegt sind, dann können Sie für das Datum 6. Januar 2002 zum Beispiel die folgenden Schreibweisen verwenden: 6/1/2002 6,1,2002 6-1-2002 6.1.2002 Bei der Einstellung English (USA) ist hingegen die letzte Schreibweise ungültig. Außerdem wären die Werte von Tag und Monat vertauscht. Der 6. Januar 2002 würde zum Beispiel so dargestellt. 1/6/2002
Anmerkung: Um die auf Ihrem Computer eingestellten Formate zu prüfen, öffnen Sie die Regions- und Sprachoptionen in der Systemsteuerung. Dort klicken Sie dann auf Anpassen und wählen die Registerkarte Datum oder Uhrzeit. Das folgende Script erstellt einen Array mit Werten und geht den Array dann durch. Es verwendet die Funktion IsDate,um festzustellen, ob es sich bei den Werten des Arrays um Daten handelt. Die Rückgabe der Funktion wird dem Benutzer angezeigt. DateArray = Array("6/1/2002", "Juni 1, 2002", "6", "6/1") For Each dtmDate in DateArray If IsDate(dtmDate) = 0 Then Wscript.Echo dtmDate & " ist kein gültiges Datum." Else Wscript.Echo dtmDate & " ist ein gültiges Datum." End If Next
Wenn Sie das Script mit CScript ausführen, erhalten Sie folgende Ausgabe: 6/1/2002 ist ein gültiges Datum. Juni 1, 2002 ist ein gültiges Datum. 6 ist kein gültiges Datum. 6/1 ist ein gültiges Datum.
Anmerkung: Warum ist 6/1 ein gültiges Datum? Die Funktion IsDate versucht aus dem übergebenen Wert ein gültiges Datum zu konstruieren. Der Wert 6/1 kann als Tag/Monat interpretiert werden. Daher hängt IsDate einfach das aktuelle Jahr an und kommt zum Ergebnis Tag/Monat/Jahr.
Seite 53 von 394
Teile eines Datums oder eines Zeitwertes abfragen Oft benötigen Sie nur einen bestimmten Teil eines Datums oder eines Zeitwertes. Zum Beispiel, wenn Sie Ihr Sicherungsscript nur an Sonntagen oder nur am 15. eines Monats ausführen möchten. Unter VBScript haben Sie zwei Möglichkeiten Teile eines Datums oder eines Zeitwertes abzufragen. Mit der Funktion DatePart können Sie jeden beliebigen Teil eines Datums oder eines Zeitwertes abfragen. Außerdem stellt VBScript mehrere Funktionen zur Verfügung, um bestimmte Teile abzufragen (zum Beispiel die Funktionen Day, Month und Year). Die Funktion DatePart benötigt zwei Parameter: Das Datum und den Teil des Datums, den Sie herausfinden möchten. Der Teil muss in einem bestimmen Format angegeben werden. Dieses Format wird in der folgenden Tabelle beschrieben. Tabelle 2.7: Parameter der Funktion DatePart ParameterBeschreibung
yyyy
Jahr - gibt die Jahreszahl des Datum-/Zeitwerts zurück.
q
Quartal - gibt das Quartal - 1, 2, 3 oder 4 - des Datum-/Zeitwerts zurück.
m
Monat - gibt den Monat des Datum-/Zeitwerts zurück. Hierbei gelten folgend Werte: 1 - Januar 2 - Februar 3 - März 4 - April 5 - Mai 6 - Juni 7 - Juli 8 - August 9 - September 10 - Oktober 11 - November 12 - Dezember
y
Tag des Jahres - hierbei steht 1 für den 1. Januar und 365 für den 31. Dezember (bei Schaltjahren 366). Der 1. Februar wäre zum Beispiel der Wert 32, da er der zweiunddreißigste Tag des Jahres ist.
d
Tag - gibt den Tag des Monats zurück. Für den 17. April würden Sie zum Beispiel Seite 54 von 394
ParameterBeschreibung
den Wert 17 erhalten. w
Wochentag - gibt den Wochentag zurück. Hierbei gelten die folgenden Werte: 1 - Sonntag 2 - Montag 3 - Dienstag 4 - Mittwoch 5 - Donnerstag 6 - Freitag 7 - Samstag Wenn Sie möchten, könnten Sie angeben, dass die Woche mit einem anderen Tag als Sonntag beginnt. Weitere Informationen hierzu finden Sie weiter unten in diesem Kapitel.
ww
Woche des Jahres - hierbei steht 1 für die erste Woche im Januar und 52 für die letzte Woche im Dezember. Auch hier können Sie die erste Woche des Jahres nach Ihrem Wünschen festlegen.
h
Stunde - Gibt die Stunde im 24-Stunden-Format zurück. Für den Zeitraum zwischen 00:00 Uhr und 01:00 Uhr wird der Wert 0 zurückgegeben.
n
Minute
s
Sekunde
Den von der Funktion DatePart zurückgegebenen Wert können Sie zum Beispiel einer Variablen zuweisen. Die folgende Codezeile extrahiert zum Beispiel das Jahr aus dem aktuellen Datum und weist dieses der Variable CurrentYear zu: CurrentYear = DatePart("yyyy", Date)
In diesem Beispiel werden die folgenden Parameter verwendet: • •
'yyyy" - gibt an, dass das Jahr aus dem Datum extrahiert werden soll. Dieser Parameter muss immer in Anführungszeichen eingeschlossen werden. Date - gibt das Datum an, aus dem der angegebene Teil extrahiert werden soll. In diesem Fall wird für diesen Parameter die Funktion Date angegeben, die das aktuelle Datum zurückgibt. Sie können natürlich auch ein tatsächliches Datum angeben. Denken Sie daran, dass solche Datumswerte in Doppelkreuze eingeschlossen werden müssen (zum Beispiel '6/1/2002'). Die folgenden beiden Codezeilen geben zum Beispiel das Jahr 1977 zurück: DateToCheck = #8/15/1977# CurrentYear = DatePart('yyyy' , DateToCheck)
Seite 55 von 394
Anmerkung: Auch wenn Sie ein Datum zu einer Variablen zuweisen, müssen Sie das Datum in Doppelkreuze (#) einschließen. So stellen Sie sicher, dass VBScript den Wert auch als Datum und nicht als Zahl oder Zeichenkette interpretiert. Alternativ können Sie auch die Funktion CDate verwenden. Script 2.15 interpretiert das aktuelle Datum und die Uhrzeit und gibt die einzelnen Komponenten für den Benutzer aus. Script 2.15: Verwendung der Funktion DatePart 1 Wscript.Echo 2 Wscript.Echo 3 Wscript.Echo 4 Wscript.Echo 5 Wscript.Echo 6 Wscript.Echo 7 Wscript.Echo 8 Wscript.Echo 9 Wscript.Echo 10Wscript.Echo 11Wscript.Echo
Now "Jahr: " & DatePart("yyyy" , Now) "Quartal: " & DatePart("q", Now) "Monat: " & DatePart("m" , Now) "Tag des Jahres: " & DatePart("y" , Now) "Tag: " & DatePart("d" , Now) "Wochentag: " & DatePart("w" , Now) "Woche des Jahrs: " & DatePart("ww" , Now) "Stunde: " & DatePart("h", Now) "Minute: " & DatePart("n" , Now) "Sekunde: " & DatePart("s" , Now)
Wenn das Script am 14. März 2004 um 20:45:20 ausgeführt wird, dann produziert es folgende Ausgabe: 14.03.2004 20:45:20 Jahr: 2004 Quartal: 1 Monat: 3 Tag des Jahres: 74 Tag: 14 Wochentag: 1 Woche des Jahrs: 12 Stunde: 20 Minute: 45 Sekunde: 20
Wenn Sie für die Funktion DatePart unvollständige Parameter verwenden, wird keine Fehlermeldung ausgegeben. Es könnte jedoch sein, dass Sie nicht das erwartete Resultat erhalten. Die folgende Codezeile gibt zum Beispiel den Wert 1899 zurück: Wscript.Echo DatePart("yyyy", "8:00")
Diese Codezeile gibt den Wert 0 zurück: Wscript.Echo DatePart("h", "12/1/2002")
Optionen von DatePart Standardmäßig ist unter VBScript die erste Woche des Jahres die Woche mit dem 1. Januar. Sie können dieses Verhalten jedoch mit den in der folgenden Tabelle gezeigten Werten ändern. Tabelle 2.8: Parameter für die Einstellung "Erste Woche des Jahres" Konstante
WertBeschreibung
vbUseSystem
0
Verwendet die National Language Support API, um die erste Woche des Jahres auf Basis der Regional- und Spracheinstellungen festzulegen. Seite 56 von 394
Konstante
WertBeschreibung
vbFirstJan1
1
Setzt die erste Woche auf die Woche mit dem 1. Januar.
vbFirstFourDays 2
Setzt die erste Woche auf die erste Woche mit mindestens 4 Tagen.
VbFirstFullWeek3
Setzt die erste Woche auf die erste Woche, die mit einem Montag beginnt.
Hängen Sie den Wert als dritten Parameter hinter das Datum an. Das folgende Script zeigt die Verwendung des Parameters "Erste Woche": TestDate = "6/1/2003" Wscript.Echo TestDate Wscript.Echo "Woche: " Wscript.Echo "Woche: " Wscript.Echo "Woche: " Wscript.Echo "Woche: "
& & & &
DatePart("ww" DatePart("ww" DatePart("ww" DatePart("ww"
, , , ,
TestDate) TestDate, vbFirstJan1) TestDate, vbFirstFourDays) TestDate, vbFirstFullWeek)
Wenn Sie das Script mit CScript ausführen, dann erhalten Sie folgendes Ergebnis: 6/1/2003 Woche: 2 Woche: 2 Woche: 2 Woche: 1
Wie Sie in Abbildung 2.13 sehen, fällt der 6. Januar 2003 nicht in dieselbe Woche wie der 1. Januar. Außerdem liegt er nicht in der ersten Woche, die mindestens vier Tage hat. Er liegt jedoch in der ersten Woche, die mit einem Montag beginnt.
Abbildung 2.13: 6. Januar 2003 Weitere Funktionen, um Teile eines Datums zu extrahieren Zusätzlich zur Funktion DatePart können Sie die Funktionen aus Tabelle 2.9 verwenden. DatePart bietet allerdings einige Möglichkeiten, die Ihnen mit den zusätzlichen Funktionen nicht zur Verfügung stehen (zum Beispiel den Tag des Jahres). Möglicherweise fällt es Ihnen jedoch schwer, sich die Parameter von DatePart zu merken. In diesem Fall sind die zusätzlichen Funktionen deutlich einfacher zu verwenden - sie erfordern keine Parameter. Die folgenden beiden Codezeilen geben beispielsweise die Minute der aktuellen Uhrzeit zurück. Für die Funktion Minute benötigen Sie jedoch keine Parameter: Wscript.Echo DatePart("n", Now) Wscript.Echo Minute(Now)
Tabelle 2.9: Funktionen, um Teile eines Datums oder eines Zeitwertes zu extrahieren Funktion Beschreibung Seite 57 von 394
Funktion Beschreibung
Day
Gibt den Tag zurück
Hour
Gibt die Stunde zurück
Minute Gibt die Minute zurück Month
Gibt den Monat zurück
Second Gibt die Sekunde zurück WeekdayGibt den Wochentag zurück. Hierbei gelten folgende Wert: 1 - Sonntag 2 - Montag 3 - Dienstag 4 - Mittwoch 5 - Donnerstag 6 - Freitag 7 - Samstag Year
Gibt das Jahr zurück.
Um diese Funktionen zu verwenden, reicht es, sie einfach mit dem Datum- oder Zeitwert als Parameter aufzurufen. Das folgende Script führt dies zum Beispiel mit dem aktuellen Datum und der Uhrzeit durch: CurrentDate = Now Wscript.Echo "Jahr: " & VbTab & VbTab & Year(CurrentDate) Wscript.Echo "Monat: " & VbTab & VbTab & Month(CurrentDate) Wscript.Echo "Tag: " & VbTab & VbTab & Day(CurrentDate) Wscript.Echo "Wochentag: " & VbTab & Weekday(CurrentDate) Wscript.Echo "Stunde: " & VbTab & VbTab & Hour(CurrentDate) Wscript.Echo "Minute: " & VbTab & Minute(CurrentDate) Wscript.Echo "Sekunde: " & VbTab & Second(CurrentDate)
Wenn Sie das Script über CScript ausführen, erhalten Sie zum Beispiel folgende Ausgabe: Jahr: Monat: Tag: Wochentag: Stunde: Minute: Sekunde:
2004 3 14 1 21 17 37
In der Systemadministration ist das Berechnen von Zeiträumen genauso wichtig wie das Feststellen von Daten - zum Beispiel wenn Sie wissen möchten, welcher Tag in 180 Tagen von heute an ist, oder wenn Sie wissen möchten, wie lange ein Dienst zwischen Start und Ende ausgeführt wurde.
Seite 58 von 394
Zum Berechnen von Zeiträumen stellt VBScript zwei Funktionen zur Verfügung. DateDiff und DateAdd. Berechnen des Zeitraumes zwischen zwei Daten oder Uhrzeiten Hierzu wird die Funktion DateDiff verwendet. Sie benötigt drei Parameter: •
• •
Den Datums- oder Zeitintervall (zum Beispiel die Zahl der Tage zwischen zwei Daten oder die Zahl der Stunden zwischen zwei Uhrzeiten). Dieser Parameter verwendet dieselben Werte wie die Funktion DatePart - diese finden Sie in Tabelle 2.7). Das Startdatum. Das Enddatum.
Script 2.16 berechnet zum Beispiel die Tage zwischen dem aktuellen Datum und dem 1. Juli 2007. Hierzu wird der Parameter "d" verwendet. Wenn Sie zum Beispiel die Wochen zwischen den beiden Daten berechnen möchten, dann würden Sie den Parameter durch "w" ersetzen. Script 2.16: Berechnen von Zeiträumen 1Wscript.Echo "Date: " & Date 2Wscript.Echo "Tage bis 1. Juli 2007: " & DateDiff("d", Date, "7/1/2002")
Wenn Sie das Script über Cscript ausführen, erhalten Sie folgendes Ergebnis: Date: 14.03.2004 Tage seit 1. Juli 2002: 1029
Anmerkung: Abhängig von den verwendeten Daten könnte es auch sein, dass Sie eine negative Zahl erhalten. Dies passiert, wenn das Startdatum hinter dem Enddatum liegt. Wenn Sie keinen negativen Wert erhalten möchten, dann können Sie dies mit der Funktion Abs vermeiden. Sie gibt Ihnen den absoluten Wert einer Zahl zurück. Der Aufruf würde dann folgendermaßen aussehen: Abs(DateDiff('d', '7/1/2002', '1/18/2002'). Mit DateDiff erhalten Sie möglicherweise unterschiedliche Ergebnisse, abhängig davon, ob Sie den Paramter 'w' oder 'ww' verwenden. Der Parameter 'w' berechnet die Anzahl der Wochen zwischen zwei Daten auf Basis des Tages des Startdatums. Der 18. Januar 2002 fiel zum Beispiel auf einen Freitag. Mit dem Parameter "w" berechnet DateDiff die Zahl der Freitage zwischen diesem Datum und einem anderen Datum. Im Gegensatz dazu berechnet DateDiff mit dem Parameter "ww" die Wochen auf Basis der Sonntage zwischen den beiden Daten. Mit DateDiff können Sie auch feststellen, wie lange Ihr Script zur Ausführung benötig hat. Hierzu weisen Sie einfach in der ersten Zeile einer Variable den Rückgabewert der Funktion Now zu. Sie enthält nun die Uhrzeit des Scriptstarts. Am Ende des Scripts berechnen Sie die Differenz zwischen der Zeit in der Variable und dem Rückgabewert der Funktion Now. Das folgende Script fragt den freien Plattenplatz für alle Laufwerke des lokalen Computers ab und misst die Zeit, die es zur Ausführung benötigt: Start = Now Set objWMIService = GetObject("winmgmts://") Set colLogicalDisk = objWMIService.InstancesOf("Win32_LogicalDisk") For Each objLogicalDisk In colLogicalDisk Wscript.Echo objLogicalDisk.DeviceID & " " & objLogicalDisk.FreeSpace
Seite 59 von 394
Next Wscript.Echo DateDiff("s", Start, Now)
Daten berechnen Mit DateDiff können Sie feststellen, wie groß der Zeitraum zwischen zwei Daten ist. Es wird jedoch ganz sicher auch Fälle geben, in denen Sie den Zeitraum kennen und mit diesem ein Datum berechnen müssen. Wenn Sie zum Beispiel alle Protokolleinträge der letzten 45 Tage abrufen möchten, dann kennen Sie das Enddatum (heute) und den Zeitraum (45 Tage) - nun fehlt ihnen noch das Startdatum. Ein solches Datum können Sie mit der Funktion DateAdd berechnen. Die Funktion erwartet drei Parameter: •
• •
Den Intervall (auch hier verwenden Sie bitte wieder die Werte von DatePart aus Tabelle 2.7). Wenn Sie zum Beispiel das Datum in 180 Tagen von heute an berechnen möchten, dann verwenden Sie den Parameter "d". Den Zeitraum (zum Beispiel 180 für 180 Tage). Um von einem Datum aus rückwärts zu rechnen, verwenden Sie einfach eine negative Zahl. Das Startdatum.
Script 2.17 berechnet das Datum, das 180 Tag nach dem heutigen Datum liegt. Script 2.17: Berechnen eines Datums 1Wscript.Echo "Datum: " & Date 2Wscript.Echo "180 von heute an: " & DateAdd("d", 180, Date)
Wenn das Script am 14. März 2004 ausgeführt wird, dann erhalten Sie folgende Ausgabe: Datum: 14.03.2004 180 von heute an: 10.09.2004
Formatierung von Datum- und Zeitwerten Standardmäßig zeigt VBScript die Ausgaben der Funktionen Now, Date und Time so an, dass sie den Betriebssystemeinstellungen entsprechen. Auf Computer mit der deutschen Version von Windows sieht diese Ausgabe normalerweise so aus, wie in Tabelle 2.10 dargestellt. Tabelle 2.10: Standardausgabe von Datum und Uhrzeit FunktionBeispielausgabe
Now
14.03.2004 22:08:10
Date
14.03.2004
Time
22:08:10
Sie müssen sich jedoch nicht auf diese Standardformate beschränken. Stattdessen stellt VBScript mehrere Konstanten zur Verfügung, über die Sie mit der Funktion FormatDateTime Datum- und Zeitwerte neu formatieren können. Tabelle 2.11: Parameter der Funktion FormatDateTime Konstante
Wert
vbGeneralDate0 Seite 60 von 394
Konstante
Wert
vbLongDate
1
vbShortDate 2 vbLongTime 3 vbShortTime 4 Das folgende Script zeigt das aktuelle Datum und die Uhrzeit an, und gibt diese in unterschiedlichen Formaten aus: CurrentDate = Now Wscript.Echo "GeneralDate: " & FormatDateTime(CurrentDate, vbGeneralDate) Wscript.Echo "LongDate: " & FormatDateTime(CurrentDate, vbLongDate) Wscript.Echo "ShortDate: " & FormatDateTime(CurrentDate, vbShortDate) Wscript.Echo "LongTime: " & FormatDateTime(CurrentDate, vbLongTime) Wscript.Echo "ShortTime: " & FormatDateTime(CurrentDate, vbShortTime)
Wenn Sie das Script über CScript ausführen, erhalten Sie das folgende Ergebnis: GeneralDate: 14.03.2004 22:12:53 LongDate: Sonntag, 14. März 2004 ShortDate: 14.03.2004 LongTime: 22:12:53 ShortTime: 22:12
Dies sind die einzigen vordefinierten Formate unter VBScript. Sie können Sich jedoch auch eigene Formate zusammenbauen. Die folgende Codezeile zeigt das Datum zum Beispiel im Format "Monat/Jahr" an: Wscript.Echo Month(Now) & "/" & Year(Now)
Formatierung von Wochentag und Monat des Jahres Die Funktionen WeekdayDay und Month liefern Ihnen den Wochentag oder den Monat eines Datums. Bei dem Rückgabewert handelt es sich allerdings um einen Integer-Wert (Ganzahl). Die Bedeutung der Werte sehen Sie in Tabelle 2.12 Tabelle 2.12: Werte für Wochentag und Monat WertWochentagMonat
1
Sonntag
Januar
2
Montag
Februar
3
Dienstag
März
4
Mittwoch April
5
Donnerstag Mai
6
Freitag
Juni
7
Samstag
Juli
8
-
August
9
-
September
10 -
Oktober Seite 61 von 394
WertWochentagMonat
11 -
November
12 -
Dezember
Um die Funktionen WeekdayName und MonthName verwenden zu können, müssen Sie erst den Tag oder den Monat aus dem Datum extrahieren. Sie können nicht direkt mit einem Datum verwendet werden. Die folgende Codezeile funktioniert zum Beispiel nicht: Wscript.Echo MonthName("19/9/2002")
Um MonthName verwenden zu können, müssen Sie zuerst die Funktion Month verwenden: MonthValue = Month("19/9/2002") Wscript.Echo MonthName(MonthValue)
Alternativ können Sie die beiden Funktionen in einer Codezeile verschachteln: Wscript.Echo MonthName(Month("19/9/2002"))
Eine genauere Beschreibung der Funktionen WeekdayName und MonthName finden Sie in Tabelle 2.13. Tabelle 2.13: WeekdayName und MonthName Funktion
Beschreibung
WeekdayNameGibt statt der Nummer des Tages dessen Namen zurück. Als Parameter muss nur der Tag angegeben werden. Daher müssen Sie die Funktionen WeekdayName und Day folgendermaßen verschachteln: Wscript.Echo WeekDayName(Day('1/9/2002')) Diese Codezeile gibt den Wert Sonntag zurück. Mit einem weiteren optionalen Parameter können Sie sich die Abkürzung von den Namen des Wochentages zurückliefern lassen. Hängen Sie den Parameter True einfach hinter den Wochentag an: Wscript.Echo WeekDayName(Day('9/1/2002'), True) MonthName
Gibt statt der Nummer des Monats dessen Namen zurück. Als Parameter muss nur der Monat angegeben werden. Daher müssen Sie die Funktionen MonthName und Month folgendermaßen verschachteln: Wscript.Echo MonthName(Month('9/1/2002')) Diese Codezeile gibt den Wert September zurück. Mit einem weiteren optionalen Parameter können Sie sich die Abkürzung von den Namen des Monats zurückliefern lassen. Hängen Sie den Parameter True einfach hinter den Monat an: Wscript.Echo MonthName(Month('9/1/2002') , True)
Arbeiten mit Strings (Zeichenketten) Viele Scripte zur Systemadministration geben Informationen über verwaltete Elemente zurück - zum Beispiel Dienste, Hardware oder Ereignisse. Viele dieser Informationen werden als Seite 62 von 394
Zeichenketten zurückgegeben. Wenn Sie zum Beispiel die Beschreibung eines Ereignisses aus dem Ereignisprotokoll abrufen, dann erhalten Sie einen String wie den folgenden: Dienst "Universeller Plug & Play-Gerätehost" befindet sich jetzt im Status "Ausgeführt".
Auch wenn Zeichenketten eigentlich ganz einfach sind, kann die Arbeit mit Ihnen kniffelig sein. Ganz besonders, wenn die Daten von einem COM-Objekt zurückgegeben werden oder wenn Sie die Daten in einer Datenbank speichern oder auf dem Bildschirm anzeigen müssen. Das liegt daran, dass Sie keine Kontrolle darüber haben, wie die zurückgegebenen Daten aussehen. Es kann sein, dass sie falsch formatiert sind (zum Beispiel nur Großbuchstaben) oder das eine Zeichenkette zu lang oder zu kurz für den vorgesehenen Platz ist (wenn das Feld in der Datenbank zum Beispiel nur 20 Zeichen lang und die Zeichenkette 30 Zeichen lang ist). Daher sollten Sie in der Lage sein mit Strings umzugehen - zum Beispiel einen String zu verkürzen oder zu verlängern, einen String neu zu formatieren oder in einem String nach einem Text zu suchen. Hierzu stellt Ihnen VBScript einige Funktionen zur Verfügung.
Manipulation von Strings und String-Längen Oft benötigen Sie nur einen Teil eines Strings. Der Windows Script Host (WSH) stellt zum Beispiel ein Attribut mit dem Namen FullName zur Verfügung. Dieses zeigt Ihnen, ob ein Script unter CScript oder WScript ausgeführt wird. Diese Information kann sehr nützlich sein, wenn Sie Scripte verwenden, die entweder nur unter CScript oder nur unter WScript ausgeführt werden können. Das Attribut gibt aber leider nicht nur den Namen des Scripting Hosts zurück, sondern den gesamten Pfad der ausführbaren Datei des Scripting Hosts. Dieser sieht normalerweise so aus: C:\Windows\Cscript.exe C:\Windows\Wscript.exe.
Meistens benötigen Sie jedoch nicht den gesamten Pfad, sondern nur den Namen des Scripting Hosts - außerdem hängt der Pfad auch vom Speicherort der ausführbaren Datei des Scripting Hosts ab. Daher benötigen Sie eine Methode, um nur die letzten acht Zeichen (cscript.exe oder wscript.exe) aus dem Pfad zu extrahieren. In anderen Fällen ist die Länge eines Strings sehr wichtig. Ganz besonders dann, wenn es um formatierte Bildschirmausgaben oder Ausgaben in eine Textdatei geht. Das folgende Script ruft zum Beispiel eine Collection ab, die die im Ordner C:\Windows gespeicherten Dateien enthält und gibt Dateiname, Dateigröße und das Datum des letzten Zugriffs für jede Datei aus: Set Set Set For
FSO = CreateObject("Scripting.FileSystemObject") Folder = FSO.GetFolder("C:\Windows") FileList = Folder.Files Each File in FileList Wscript.Echo File.Name & VbTab & File.Size & File.DateLastModified
Next
Wenn Sie das Script ausführen, dann wird die Ausgabe so ähnlich wie unten aussehen. Wie Sie feststellen werden, ist es nicht so einfach, diese Ausgabe zu lesen. regedit.exe 14131229.08.2002 04:43:40 regopt.log 134816.01.2004 22:15:13 Rhododendron.bmp 1736218.08.2001 14:00:00 Santa Fe-Stuck.bmp 6583218.08.2001 14:00:00 SchedLgU.Txt 542414.03.2004 18:36:49 Seifenblase.bmp 6597818.08.2001 14:00:00 sessmgr.setup.log 106016.01.2004 22:27:34 SET3.tmp 108618229.08.2002 09:54:40 SETA.tmp 1389818.08.2001 14:00:00
Seite 63 von 394
Mit ein paar Tabulatoren und in Spalten angeordnet ist die Ausgabe viel einfacher zu lesen und zu überblicken: regedit.exe regopt.log Rhododendron.bmp Santa Fe-Stuck.bmp SchedLgU.Txt Seifenblase.bmp sessmgr.setup.log SET3.tmp SETA.tmp
141312 1348 17362 65832 5424 65978 1060 1086182 13898
29.08.2002 16.01.2004 18.08.2001 18.08.2001 14.03.2004 18.08.2001 16.01.2004 29.08.2002 18.08.2001
04:43:40 22:15:13 14:00:00 14:00:00 18:36:49 14:00:00 22:27:34 09:54:40 14:00:00
Um eine solche Ausgabe zu erreichen, müssen Sie Strings verändern. In der letzten Ausgabe beginnt der Dateiname zum Beispiel an Zeichenposition 1, und die Dateigröße beginnt immer an Zeichenposition 30: 123456789012345678901234567890123456789012345678901234567890123456789 SETA.tmp 13898 18.08.2001 14:00:00
Um eine solche Ausgabe zu erstellen, müssen Sie Folgendes durchführen: 1.Sie müssen die Länge der Dateinamen berechnen (der Dateiname SETA.tmp ist zum Beispiel 8 Zeichen lang). 2.Sie müssen die Länge des Dateinamens (8 Zeichen) vom Gesamtplatz für den Dateinamen (30 Zeichen) abziehen. 3.Sie müssen entsprechend viele Leerzeichen einfügen (30 minus 8 = 22 Leerzeichen). Glücklicherweise stellt VBScript eine Menge Funktionen zur Manipulation von Strings zur Verfügung. Einige dieser Funktionen sehen Sie in Tabelle 2.14. Tabelle 2.14: Funktionen zur String-Bearbeitung FunktionBeschreibung
Len
Gibt die Zahl der Zeichen in einem String zurück. Diese Funktion ist besonders bei der Bildschirmausgabe von Strings nützlich. Stellen Sie sich vor, Sie möchten in einer bestimmten Spalte nicht mehr als 20 Zeichen ausgeben. Um dies zu garantieren, müssen Sie feststellen können, wie viele Zeichen ein String enthält. Das folgende Codestück gibt den Wert 24 zurück, da der String Dies ist ein TestString 24 Zeichen lang ist. TestString = "Dies ist ein Test-String" Wscript.Echo Len(TestString)
Left
Gibt eine bestimmte Anzahl von Zeichen zurück. Die zurückgegebenen Zeichen beginnen mit dem ersten Zeichen des Strings und setzen sich nach rechts bis zum Ende des Strings fort. Sie müssen zwei Parameter angeben: den String und die Anzahl der zurückzugebenden Zeichen. Das folgende Codestück gibt den String Die zurück, da dies die ersten drei Zeichen des Strings Dies ist ein Test-Stringsind. TestString = "Dies ist ein Test-String" Wscript.Echo Left(TestString, 3)
Right
Gibt eine bestimmte Zahl von Zeichen zurück. Die zurückgegebenen Zeichen beginnen mit dem letzten Zeichen des Strings und setzen sich nach links bis zum Anfang des Strings fort. Sie müssen zwei Parameter angeben: den String und die Anzahl der zurückzugebenden Zeichen. Seite 64 von 394
FunktionBeschreibung
Das folgende Codestück gibt den String ing zurück, da dies die letzten drei Zeichen des Strings Dies ist ein Test-Stringsind. TestString = "Dies ist ein Test-String" Wscript.Echo Right(TestString, 3) Mid
Gibt eine bestimmte Zahl von Zeichen zurück. Die zurückgegebenen Zeichen beginnen an der als Parameter angegebenen Position und setzten sich zum Ende des Strings fort (von links nach rechts). Sie müssen drei Parameter angeben: den String, die Startposition im String (zum Beispiel 5, um beim fünften Zeichen des Strings zu beginnen) und die Anzahl der zurückzugebenden Zeichen. Das folgende Codestück gibt den String ist_ zurück (der Unterstrich steht für ein Leerzeichen), da dies die Zeichen an den Positionen 6, 7, 8 und 9 sind. TestString = "Dies ist ein Test-String" Wscript.Echo Mid(TestString, 6, 4)
Space
Fügt die angegebene Anzahl von Leerzeichen in einen String ein. Das folgende Codestück fügt zum Beispiel 10 Leerzeichen zwischen Dies ist ein und Test-String ein. Wscript.Echo "Dies ist ein" & Space(10) _ & "Test-String"
LTrim Entfernt alle Leerzeichen am Anfang eines Strings. TestString = " This is a test string " Wscript.Echo LTrim(TestString) RTrim Entfernt alle Leerzeichen am Ende eines Strings. TestString = " This is a test string " Wscript.Echo RTrim(TestString) Trim
Entfernt alle Leerzeichen am Anfang und am Ende eines Strings. Das folgende Codestück wandelt den String " Dies ist ein Teststring " zum Beispiel in den String "Dies ist ein Test-String" um. TestString = " This is a test string " Wscript.Echo Trim(TestString)
Tabellarische Ausgaben Die Funktionen zur Stringbearbeitungen sind besonders bei der Erstellung von tabellarischen Ausgaben sehr nützlich. Ein Beispiel hierfür sehen Sie in Script 2.18. Es verwendet die Funktionen Len und Space, um eine Tabelle zu erstellen, in der zwei Dienste und deren Status angezeigt werden. Das Script geht folgendermaßen vor: 1.Es setzt den Wert von vier Variablen - zwei für die Namen der Dienste und zwei für deren Status. 2.Es stellt mit der Funktion Len die Länge des Strings in der ersten Variable fest und speichert diesen Wert in der Variable NameLength. "Warndienst" hat zum Beispiel 10 Zeichen daher wird in NameLength der Wert 10 gespeichert. 3.Das Script subtrahiert den Wert in NameLength von 20 (der Spaltenbreite der Spalte für die Dienstnamen) und speichert das Ergebnis in der Variable SpacesToAdd. Für den "Warndienst" wäre das 10 (20-10). 4.Es schreibt den Namen des Dienstes in die Variable DisplayName und hängt so viele Leerzeichen an, dass der String in dieser Variable 20 Zeichen lang ist. Für den "Warndienst" wären das zum Beispiel 10 zusätzliche Leerzeichen (der Wert in der Variable SpacesToAdd). Diese Leerzeichen werden über die Funktion Space hinzugefügt. Seite 65 von 394
Die Variable DisplayName enthält dann folgenden String (die Trennstriche stehen für Leerzeichen): Warndienst---------5.Das Script gib die Variable DisplayName und den Status des Dienstes aus. 6.Es wiederholt den Prozess für den nächsten Dienstnamen. Script 2.18: Tabellarische Ausgaben 1 Service1 = "Warndienst" 2 State1 = "Wird ausgeführt" 3 Service2 = "DHCP-Client" 4 State2 = "Angehalten" 5 6 NameLength = Len(Service1) 7 SpacesToAdd = 20 - NameLength 8 DisplayName = Service1 & Space(SpacesToAdd) 9 Wscript.Echo DisplayName & State1 10 11Display = "" 12 13NameLength = Len(Service2) 14SpacesToAdd = 20 - NameLength 15DisplayName = Service2 & Space(SpacesToAdd) 16Wscript.Echo DisplayName & State2
Wenn Sie das Script unter CScript ausführen, dann erhalten Sie die folgende Ausgabe: Warndienst DHCP-Client
Wird ausgeführt Angehalten
Text in Nachrichtenfenster formatieren Wenn Sie es vorziehen, Text in Nachrichtenfenstern auszugeben, dann werden Sie feststellen, dass es hier sehr viel schwerer ist den Text in Spalten auszugeben - wenn nicht gar unmöglich. Das liegt an den unterschiedlichen Scripten in der Eingabeaufforderung und im Nachrichtenfenster. In der Eingabeaufforderung wird eine nichtproportionale Schrift verwendet. Das bedeutet, dass alle Zeichen (auch die Leerzeichen) gleich breit sind. In Abbildung 2.14 sehen Sie, dass die Buchstaben i, j und l genauso breit sind wie m, v und w.
Abbildung 2.14: Buchstaben in einer nichtproportionalen Schrift Im Gegensatz dazu wird in einem Nachrichtenfenster normalerweise eine proportionale Schrift verwendet. Das bedeutet, dass die Breite der Buchstaben unterschiedlich ist. Wie Sie in Abbildung 2,15 sehen, sind die Zeichen i, j und l hier viel schmaler als die Zeichen m, v und w. Seite 66 von 394
Abbildung 2.15: Zeichenbreite bei einer proportionalen Schrift Aufgrund dieser unterschiedlichen Zeichenbreiten können Sie nicht einfach davon ausgehen, dass eine Spalte zum Beispiel an Position 20 beginnt und dass die Spalte damit bündig ausgerichtet ist.
In einem String nach Text suchen Sie haben also nicht immer eine vollständige Kontrolle über den ausgegebenen Text. Stellen wir uns einmal vor, Sie möchten eine Protokolldatei öffnen und feststellen, welche Aktionen fehlgeschlagen und welche erfolgreich waren. In den meisten Fällen enthalten Protokolleinträge sehr viel mehr Text als ein einfaches Erfolgreich oder Fehlgeschlagen. Stattdessen können Einträge zum Beispiel so aussehen: MSI: False - MSP: True - CMW: False MSISW: REINSTALL=all REINSTALLMODE=vomus /QB /L*V+ C:\WINNT\DEBUG\officexp_sp1.log MSPSW: REBOOT=ReallySuppress /QB- /L*V+ C:\WINDOWS\DEBUG\officexp_sp1.log InstallFile Result: [0] and Success: True Client Maint Wiz: PROPLUS detected
Um hier Erfolg oder Fehlschlag festzustellen, muss Ihr Script den gesamten Text lesen und dann nach den gewünschten Informationen suchen (in diesem Beispiel nach dem Text 'Success: True'). Die Funktion InStr gibt Ihnen die Möglichkeit, nach einem String in einem anderen String zu suchen. Stellen Sie sich zum Beispiel vor, Sie haben eine Protokolldatei mit Hunderten solcher Einträge: 6/1/2002 6/1/2002 6/1/2002 6/1/2002
File a.doc successfully File b.doc successfully Error: File c.doc could File d.doc successfully
copied copied not be copied copied
Statt diese nun per Hand zu prüfen, können Sie ein Script schreiben, dass die Protokolldatei Zeilenweise einliest und nur die Zeilen ausgibt, die das Wort "Error" enthalten. Anmerkung: Die Funktion InStr wurde für einfache Stringbearbeitung entwickelt. Wenn Sie komplexere Aktionen mit einem String durchführen müssen, dann verwenden Sie reguläre Ausdrücke. Die Verwendung von regulären Ausdrücken liegt jedoch nicht im Themenbereich dieses Buches. Weitere Informationen hierzu finden Sie unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig).
Seite 67 von 394
InStr gibt die Zeichenposition zurück, an der der Suchstring gefunden wurde. Nehmen wir an, Sie suchen nach dem Text ment im Wort developmental. In diesem Fall gibt InStr den Wert 8 zurück, da der Text ment an Zeichenposition Acht anfängt.
Abbildung 2.16: Zeichenpositionen im Wort Developmental Wenn Sie jedoch nach dem Text mant suchen, dann gibt InStr den Wert 0 zurück. Dieser Rückgabewert bedeutet, dass der Suchstring nicht gefunden wurde. InStr benötigt zwei Parameter: den String, in dem gesucht werden soll und den String, nach dem gesucht werden soll (der Suchstring). Die folgende Codezeile sucht im Wort developmental zum Beispiel nach dem Text ment: Wscript.Echo InStr("ment", "developmental")
Sie können die verwendeten Strings auch zu Variablen zuweisen und diese dann in der Funktion verwenden. Das folgende Script sucht im Satz In diesem Text-String wird nach zwei unterschiedlichen Wörtern gesucht nach den Wörtern Strang und Test. TestString = "In diesem Text-String wird nach zwei unterschiedlichen Wörtern gesucht" PresentString = "Test" AbsentString = "Strang" Wscript.Echo InStr(PresentString, TestString) Wscript.Echo InStr(AbsentString, TestString)
Wenn Sie das Script ausführen, werden die Werte 11 und 0 zurückgegeben. Der Wert 11 zeigt, dass das Wort Test an Zeichenposition Elf gefunden wurde, und der Wert 0 zeigt, dass das Wort Strang gar nicht gefunden wird.
Groß- und Kleinbuchstaben Die Kontrolle über die Schrifteigenschaften ist nicht gerade die starke Seite von VBScript. Egal, ob Sie etwas in einer Eingabeaufforderung oder in einem Nachrichtenfenster ausgeben über Eigenschaften wie Schriftart, -größe und -farbe haben Sie keine Kontrolle. Das Einzige, was Sie verändern können, ist die Groß- und Kleinschreibung. Mit Großbuchstaben können Sie wichtige Informationen betonen. Die folgende hypothetische Ausgabe ist zum Beispiel besser zu lesen, wenn der fehlerhafte Server schnell zu erkennen ist: Server atl-dc-01 atl-dc-02 ATL-DC-03 atl-dc-04
Status Healthy Healthy ERROR Healthy
Auch für Eingaben kann die Kontrolle über die Groß- und Kleinschreibung ganz nützlich sein. Wenn Sie zum Beispiel sicherstellen möchten, dass der Benutzer ein Landeskürzel (DE, GB, USA) in Großbuchstaben eingibt, dann können Sie die den eingegebenen String einfach in Großbuchstaben umwandeln - so spielt es keine Rolle, was der Benutzer eingegeben hat. Tabelle 2.15: Funktionen zur Umwandlung in Groß- oder Kleinbuchstaben FunktionBeschreibung Seite 68 von 394
FunktionBeschreibung
LCase Konvertiert alle alphabetischen Zeichen in Kleinbuchstaben. Das folgende Codestück konvertiert den String Ulf Dornheck Busscher in den String ulf dornheck busscher. UserName = "Ulf Dornheck Busscher" Wscript.Echo LCase(UserName) UCase Konvertiert alle alphabetischen Zeichen in Großbuchstaben. Das folgende Codestück konvertiert den String Ulf Dornheck Busscher in den String ULF DORNHECK BUSSCHER. UserName = "Ulf Dornheck Busscher" Wscript.Echo UCase(UserName)
Arbeiten mit Zahlen Vieles im Bereich der Systemadministration hat mit Zahlen zu tun: Über wie viel Speicher verfügt ein Computer? Wie viele fehlgeschlagene Anmeldeversuche gab es? Wie viel Prozessorzeit wird auf einem Domänencontroller verbraucht? Solche Informationen können über Scripte sehr einfach ermittelt werden. Die Fragen oben können Sie zum Beispiel alle über WMI beantworten. Leider werden Informationen nicht immer im gewünschten Format zurückgegeben. Der freue Plattenplatz wird zum Beispiel in Byte zurückgeliefert - bei einer Festplatte mit ca. 10 GB freiem Speicherplatz erhalten Sie so den Wert 10,842,824,704 Byte zurück. Die Prozessorauslastung wird in 100 NanosekundenSchritten angegeben. Die Maximalhöhe und -breite des von einem Drucker unterstützten Papiers wird in Zehntelmillimeter zurückgegeben. Glücklicherweise bietet VBScript Ihnen einige Funktionen zur Verarbeitung von Zahlen.
Rechenreihenfolge Die meisten Berechnungen, die Sie als Systemadministrator vornehmen werden, sind sehr einfach - zum Beispiel eine Division durch 1,024, um Byte in KB umzurechnen. Trotzdem müssen Sie vielleicht gelegentlich Berechnungen durchführen, die mehr als eine Rechenart verwenden (zum Beispiel zwei Werte addieren und das Ergebnis durch einen weiteren Wert teilen). Daher müssen Sie wissen, in welcher Reihenfolge VBScript Berechnungen durchführt. Rechenoperationen (Addition, Subtraktion, Multiplikation und Division) werden von VBScript nicht gleichwertig behandelt. Stattdessen führt VBScript diese in der folgenden Reihenfolge durch: 1.Division 2.Multiplikation 3.Addition 4.Subtraktion Warum ist das für Sie wichtig? Schauen Sie sich folgendes Script an. Es addiert zwei Werte, multipliziert diese mit einem dritten, subtrahiert einen vierten und dividiert durch einen fünften: Seite 69 von 394
Wscript.Echo 1 + 2 * 3 - 4 / 5
Wenn Sie das Script ausführen, dann wird der Wert 6,2 ausgegeben. Und so kommt es zu diesem Wert: 1.Da die Division Vorrang vor allen anderen Rechenoperationen hat, dividiert VBScript als erstes 4 durch 5. Die verbleibende Berechnung sieht nach diesem Schritt so aus: 1 + 2 * 3 - 0.8 2.Als nächstes wird multipliziert: 2 und 3 ergibt 6. Die verbleibende Berechnung sieht so aus: 1 + 6 - 0.8 3.Die Addition ist die nächste Berechnung; 1 und 6 ergibt 7 7 - 0.8 4.Als letztes wird die Subtraktion durchgeführt: 7 minus 0,8 ist 6,2. Nun könnte es natürlich sein, dass dies nicht die von Ihnen gewünschte Rechenreihenfolge ist. Stattdessen wollten Sie folgende Berechnung durchführen: 1.Addition 1 + 2 2.Ergebnis mit 3 multiplizieren 3.4 subtrahieren 4.Ergebnis durch 5 dividieren Damit dies so passiert, müssen Sie Klammern verwenden. Sie zeigen VBScript die richtige Rechenreihenfolge. Klammern haben immer Vorrang - egal welche Rechenoperation verwendet wird. Wenn mehrere verschachtelte Klammern verwendet werden, dann werden die Berechnungen immer von der innersten Klammer zur äußersten Klammer durchgeführt (die innerste Klammer wird also als erstes berechnet). Wscript.Echo (((1 + 2) * 3) - 4) / 5
Formatierung von Zahlen Sich selbst überlassen formatiert VBScript Zahlen immer gleich. Erinnern wir uns an eines der ersten Scripte dieses Kapitels: es gab den freien Plattenplatz für ein Laufwerk zurück. Der zurückgegebene Wert war 10842824704. Er war ziemlich schwer zu lesen - besser wäre eine Ausgabe wie 10.842.824.704 gewesen. Eine Berechnung weiter oben in diesem Kapitel gab zum Beispiel den Wert 10340,4458007813 zurück. Wenn überhaupt, dann werden Sie eine so genaue Kommazahl nur ganz selten benötigen. Es wäre besser, wenn der Wert als 10.340 oder 10.340,45 zurückgegeben würde. Diese Ausgaben sind nicht nur besser zu lesen, sie benötigen auch weniger Anzeigeplatz. Das könnte zum Beispiel wichtig werden, wenn Sie Ausgaben in Tabellenform durchführen möchten. Mit der Funktion FormatNumber können Sie definieren, wie Zahlen ausgegeben werden. Sie erwartet als Parameter die zu formatierende Zahl und eine oder alle Parameter aus Tabelle 2.16. Die folgende Codezeile gibt die Zahl 37,874 zum Beispiel ohne Nachkommastellen aus - es wird als nur 37 angezeigt: Wscript.Echo FormatNumber(37.874, 0)
Tabelle 2.16: Paramter von FormatNumber Seite 70 von 394
Parameter
Beschreibung
Zahl der Nachkommastellen.
Die Funktion FormatNumber löscht nicht angezeigte Nachkommastellen nicht einfach. Stattdessen werden sie auf- oder abgerundet. Wenn Sie die Zahl 11,6 zum Beispiel ohne Nachkommastellen anzeigen lassen, dann wird die Zahl 12 angezeigt. Im Gegensatz dazu ergäbe 19,2 die Zahl 19. Wenn Sie statt der Zahl der Nachkommastellen den Wert -1 verwenden, dann werden die Einstellungen aus den Regional- und Spracheinstellung des Computers verwendet.
Führende Null.
Mögliche Werte: -1 - True. Mit führender 0. 0 - False. Ohne führende 0 -2 - Einstellungen aus Regional- und Spracheinstellungen verwenden.
Negative Zahlen Mögliche Werte: in Klammern. -1 - True. Negative Zahlen in Klammern. 0 - False. Keine Klammern. -2 - Einstellungen aus Regional- und Spracheinstellungen verwenden. TausenderTrennzeichen.
Mögliche Werte: -1 - True. Mit Tausender-Trennzeichen. 0 - False. Ohne Tausender-Trennzeichen -2 - Einstellungen aus Regional- und Spracheinstellungen verwenden.
Damit Ihr Script einfacher zu lesen und zu pflegen sein wird, können Sie statt der in der Tabelle gezeigten Werte auch Konstanten verwenden. In der folgenden Scriptzeile ist zum Beispiel nicht unbedingt sofort klar, wofür die 0 steht: Wscript.Echo FormatNumber(37.874, 0)
Mit einer Konstante ist das Script viel einfacher zu lesen: Const NoDecimalPlaces = 0 Wscript.Echo FormatNumber(37.874, NoDecimalPlaces)
Alle Formatierungsparameter sind optional. Sie können durchaus alle Parameter ignorieren und nur das Tausender-Trennzeichen verwenden. Dieser Parameter muss allerdings immer der fünfte Parameter sein (nach den Nachkommastellen und den drei anderen optionalen Parametern). Wenn Sie nur einzelne Parameter verwenden möchten, dann haben Sie die folgenden Möglichkeiten: • •
Verwenden Sie "leere" Parameter (schreiben Sie für die Parameter, die Sie nicht verwenden möchten, also nur ein Komma ohne Wert). Verwenden Sie für die nicht verwendeten Parameter den Standardwert. Setzen Sie diese Seite 71 von 394
hierzu einfach auf False (Falsch). Im folgenden Script werden zum Beispiel Konstanten mit den entsprechenden Werten verwendet: Const Decimals = 3 Const LeadingZero = True Const UseParentheses = True Const Default = False NumberToFormat = 1 / -7 Wscript.Echo NumberToFormat Wscript.Echo FormatNumber(NumberToFormat, Decimals) Wscript.Echo FormatNumber(NumberToFormat, Default, LeadingZero) Wscript.Echo FormatNumber(NumberToFormat, Default, Default, UseParentheses)
Wenn Sie das Script unter CScript ausführen, erhalten Sie folgende Ausgabe: -0,142857142857143 -0,143 -0,14 (0,14)
Einen alternativen Weg sehen Sie im folgenden Script. Es formatiert das Ergebnis der Multiplikation von 33 mit 454: Const GroupDigits = True Const Default = False Const NoDecimals = 0 NumberToFormat = 33 * 454 Wscript.Echo NumberToFormat Wscript.Echo FormatNumber(NumberToFormat, Default, Default, Default, GroupDigits) Wscript.Echo FormatNumber _ (NumberToFormat, NoDecimals , Default, Default, GroupDigits)
Wenn Sie dieses Script unter CScript ausführen, dann erhalten Sie folgende Ausgabe: 14982 14,982.00 14,982
Prozentwerte formatieren Manchmal sind Prozentwerte nützlicher als reine Zahlen. Die Maximalgröße des Ereignisprotokolls kann zum Beispiel von Computer zu Computer unterschiedlich sein. Daher wissen Sie nach der Abfrage der aktuellen Größe des Ereignisprotokolls (zum Beispiel über WMI) nicht, wie voll das Protokoll tatsächlich ist. Besser wäre es, die prozentuale Auslastung des Ereignisprotokolls abzufragen. Wenn Sie Prozentwerte abfragen, dann sollten Sie diese auch als Prozentwerte anzeigen. Wenn das Ereignisprotokoll zum Beispiel maximal 5.000.000 Byte groß werden kann und die aktuelle Größe 3.127.354 Byte ist, dann können Sie 3.127.354 durch 5.000.000 teilen. Sie erhalten als Ergebnis den Wert 0,6254708 zurück. Dieses Ergebnis wäre als Prozentwert (63%) viel aussagekräftiger. Die Funktion FormatPercent konvertiert eine Zahl in einen Prozentwert; das bedeutet, dass sie die Zahl mit 100 multipliziert und ein Prozentzeichen anhängt. FormatPercent akzeptiert die gleichen Formatierungsparameter wie FormatNumber: • •
Nachkommastellen Führende Null bei Kommawerten, die kleiner als 1 und größer als -1 sind Seite 72 von 394
• •
Negative Werte in Klammern Tausender-Trennzeichen (6.500.000% statt 6500000%)
Die Syntax der Funktion FormatPercent ist identisch mit der Funktion FormatNumber: Das folgende Script dividiert 1 durch 7 und gibt das Ergebnis aus. Es verwendet FormatPercent, um die Ausgabe als Prozentwert ohne Nachkommastellen zu formatieren. Const NoDecimals = 0 NumberToFormat = 1 / 7 Wscript.Echo NumberToFormat Wscript.Echo FormatPercent(NumberToFormat, NoDecimals) Wenn Sie das Script unter CScript ausführen, dann erhalten Sie folgende Ausgabe: 0,142857142857143 14%
Befehle mehrfach ausführen Ein Grund dafür, dass die Systemadministration so schwierig und zeitaufwändig ist, liegt darin, dass IT-Umgebungen dynamisch sind. Das bedeutet, dass Systemadministratoren dieselben Aufgaben wieder und wieder ausführen müssen. Wenn ein Computer heute über ausreichend freien Festplattenplatz verfügt, dann muss das morgen oder nächsten Monat nicht ebenfalls so sein. Zum Glück wird Scripten nie langweilig. Sie werden auch nie müde oder machen Fehler, wenn sie eine Aufgabe ständig wiederholen müssen. Ein Script wird so zum perfekten Instrument, um sich ständig wiederholende Aufgaben auszuführen. Hierzu steht Ihnen unter VBScript die Do-Loop-Schleife zur Verfügung.
Do Loop Eine Do-Loop-Schleife ermöglicht es Ihnen dieselben Befehle so lange auszuführen, bis eine bestimmte Bedingung eingetreten ist - zum Beispiel, wenn Sie den freien Festplattenplatz so lange überwachen möchten, bis dieser unter einen bestimmten Wert fällt. Im Gegensatz zur For-Next-Schleife können Sie mit der Do-Loop-Schleife Befehle für einen bestimmten Zeitraum ausführen. VBScript unterstützt zwei Arten von Do-Loop-Schleifen: Do Until und Do While. Die DoUntil-Schleife wird so lange ausgeführt, bis eine bestimmte Bedingung zutrifft (wahr ist). Das folgende Script verwendet das Objekt FileSystemObject, um eine Textdatei zu öffnen und diese dann Zeile für Zeile auszulesen. Hierzu ist der Code zum Auslesen der Textzeilen in einer Schleife platziert. Diese wird so lange ausgeführt, bis (until) das Ende der Datei (Ende des Textstreams - End Of Stream) erreicht ist. Die Bedingung für diese Schleife sieht so aus: Do Until objTextFile.AtEndOfStream
Diesen Befehl können Sie auch so lesen: "Führe die Schleife so lange aus, bis das Ende der Datei erreicht ist". Const ForReading = 1 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objTextFile = objFSO.OpenTextFile _ ("c:\test.txt", ForReading) Do Until objTextFile.AtEndOfStream strNextLine = objTextFile.Readline
Seite 73 von 394
Wscript.Echo strNextLine Loop
Wenn das Script ausgeführt wird, wird die Schleifenbedingung bei jedem Durchlauf geprüft. Wenn die Bedingung wahr (True) ist - wenn also das Dateiende erreicht ist - dann wird die Schleife nicht mehr weiter ausgeführt. Im Gegensatz dazu wird eine Do-While-Schleife so lange ausgeführt, wie die Bedingung nicht zutrifft (also falsch ist - False). Das folgende Script verwendet hierzu die Syntax: Do While Not objTextFile.AtEndOfStream
Diese Scriptzeile könnte übersetzt lauten "Führe die Schleife so lange aus, wie das Ende der Textdatei nicht erreicht ist". Const ForReading = 1 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objTextFile = objFSO.OpenTextFile _ ("c:\test.txt", ForReading) Do While Not objTextFile.AtEndOfStream strNextLine = objTextFile.Readline Wscript.Echo strNextLine Loop
Endlosschleifen Vielleicht haben Sie es sich schon gedacht: Es ist möglich Endlosschleifen zu erstellen - diese werden nie beendet (wenn die Bedingung niemals zutrifft, dann wird die Schleife unendlich ausgeführt). Meist handelt es sich bei solchen Schleifen um einen Fehler des Scripterstellers. Es könnte jedoch auch sein, dass Sie zum Beispiel ein Script benötigen, das Sie bei jedem Fehler im Ereignisprotokoll benachrichtigt. Sie möchten nicht, dass das Script nach dem ersten Fehler beendet wird; stattdessen möchten Sie, dass es in einer Endlosschleife auf weitere Fehler wartet. Um ein Script mit einer solchen Schleife zu erstellen, verwenden Sie einfach eine Bedingung, die niemals zutreffen kann. Das folgende Script macht dies, indem es die Schleife so lange ausführt, bis die Variable LoopVariable den Wert 0 hat. Da das Script jedoch nie etwas an dem Wert von LoopVariable ändert, wird diese Bedingung niemals zutreffen. LoopVariable = 1 Do Until LoopVariable = 0 FreeMegaBytes = objLogicalDisk.FreeSpace / CONVERSION_FACTOR If FreeMegaBytes < WARNING_THRESHOLD Then Wscript.Echo Computer & " " & objLogicalDisk.DeviceID & _ " is low on disk space." End If Loop
Dieses Script müssen Sie bei Bedarf dann natürlich auch manuell beenden.
Prüfen der Schleifenbedingung Die Schleifenbedingung können Sie unter VBScript an zwei Stellen der Schleife prüfen: Entweder bevor die Schleife ausgeführt wird, oder nachdem die Schleife ausgeführt wurde. Dies kann durchaus ein Unterschied sein. Wenn Sie möchten, dass die Schleife nur dann ausgeführt wird, wenn die Bedingung zutrifft, dann prüfen Sie die Bedingung vor der Schleifenausführung. Wenn Sie sicherstellen möchten, dass die Schleife mindestens einmal ausgeführt wird (egal, ob die Bedingung zutrifft oder nicht), dann prüfen Sie die Bedingung am Ende der Schleife.
Seite 74 von 394
Das folgende Script verdeutlicht den Unterschied zwischen den beiden Varianten. Nachdem Die Variable LoopVariable auf den Wert 1 gesetzt wurde, verwendet das Script zwei Schleifen. In der ersten Schleife wird die Bedingung vor dem Schleifendurchlauf geprüft. Die Syntax hierzu lautet: Do Until LoopVariable = 1
Die zweite Schleife prüft die Bedingung erst nach dem Schleifendurchlauf. Die entsprechende Syntax lautet: Loop Until LoopVariable = 1
Das Script selbst sieht so aus: LoopVariable = 1 Do Until LoopVariable = Wscript.Echo "Script Loop Do Wscript.Echo "Script Loop Until LoopVariable
1 ist in Schleife 1." ist in Schleife 2." = 1
Wenn Sie das Script ausführen, dann erhalten Sie folgende Ausgabe: Script ist in Schleife 2
Wie Sie sehen können, wird Schleife 2 einmal ausgeführt. Schleife 1 wird gar nicht ausgeführt. Woher kommt dieser Unterschied? In der ersten Schleife ist die Bedingung (wenn die Variable LoopVariable gleich 1 ist) vor der Ausführung der Schleife getestet. Da LoopVariable den Wert 1 hat, wird die Do-Loop-Schleife sofort beendet. Sie wird also nicht einmal ausgeführt. In der zweiten Schleife wird die Bedingung erst dann getestet, wenn die Schleife ausgeführt wurde. Der genaue Ablauf sieht also so aus: 1.Die Schleife beginnt mit dem Befehl Do Loop 2.In der Schleife wird der Text 'Script ist in Schleife2' ausgegeben 3.Am Ende der Schleife wird die Bedingung geprüft (ist die Variable LoopVariable gleich 1?). Das ist wahr. Daher wird die Schleife beendet, nachdem sie einmal ausgeführt wurde. Mit dem Befehl Loop Until wird jede Schleife als mindestens einmal ausgeführt. Wenn Sie dies nicht möchten, dann verwenden Sie den Befehl Do Until.
Eine Schleife verlassen VBScript arbeitet standardmäßig jede Zeile Code innerhalb einer Do-Loop-Schleife nacheinander ab. In den meisten Fällen ist das auch genau das, was Sie möchten. Es mag jedoch auch Ausnahmen geben, in denen Sie eine Schleife sofort verlassen möchten - und zwar ohne den restlichen Code in der Schleife abzuarbeiten. Dies können Sie mit dem Befehl Exit Do erreichen. Er sorgt dafür, dass diese Schleife sofort beendet wird. Das folgende Script erzeugt einen Fehler (5 geteilt durch 0). Die Schleifenbedingung sagt: "Führe die Schleife aus, bis ein Fehler auftritt" (Err 0). Sie könnten also annehmen, dass die Schleife bei dem Versuch 5 durch 0 zu teilen beendet wird (das wäre ja ein Fehler). On Error Resume Next Do Until Err <> 0
Seite 75 von 394
X = 5/0 Wscript.Echo " Das Ergebnis ist " & X & "." Loop
Wenn Sie das Script ausführen, dann erhalten Sie jedoch die folgende Ausgabe:
Abbildung 2.17: Fehlerhaftes Nachrichtenfenster Warum ist das so? Wenn das Script den Fehler durch die Division durch 0 feststellt, dann wird die Schleife nicht verlassen - obwohl dies in der Bedingung so angegeben ist. Stattdessen werden die verbleibenden Befehle der Schleife weiter abgearbeitet. Das liegt daran, dass die Schleifenbedingung nur einmal pro Durchlauf geprüft wird (am Anfang). So wird nach dem Fehler noch die Ausgabe durchgeführt (X enthält den Wert Null). Erst dann wird vor dem nächsten Vorlauf die Schleifenbedingung erneut geprüft und die Schleife beendet. Wenn Sie eine Schleife vor der Beendigung eines Durchlaufes verlassen müssen, dann verwenden Sie den Befehl Exit Do. Im folgenden Script wird die Fehlerbedingung innerhalb der Schleife getestet. Wenn ein Fehler aufgetreten ist, dann wird eine Fehlermeldung ausgegeben, und die Schleife wird über den Befehl Exit Do sofort beendet. On Error Resume Next Do Until Err <> 0 X = 5/0 If Err <> 0 Then Wscript.Echo "Es ist ein Fehler aufgetreten." Exit Do End If Wscript.Echo " Das Ergebnis ist " & X & "." Loop
Wenn Sie dieses Script ausführen, dann erhalten Sie die folgende Anzeige:
Abbildung 2.18: Das Nachrichtenfenster zeigt an, dass ein Fehler aufgetreten ist Wie Sie sehen können, wurde die Schleife beendet, bevor die Ergebnisausgabe durchgeführt wird.
Entscheidungen treffen Ein Script wird normalerweise sequenziell ausgeführt. Das heißt, Zeile 1 wird abgearbeitet, dann Zeile 2, Zeile 3 usw. bis die letzte Zeile abgearbeitet wurde. Im Allgemeinen wird das Schreiben von Scripten durch diese Tatsache einfacher. Nachdem ein Script gestartet wurde, können Sie davon ausgehen, dass es alle Zeilen korrekt ausführt Seite 76 von 394
und dann beendet wird. Das Ausführen jeder Zeile des Scripts kann jedoch aus zu Komplikationen führen. Was passiert, wenn es Zeilen gibt, die Sie nicht ausführen wollen? Nehmen wir einmal ein, Sie haben ein Script geschrieben, dass eine bestimmte Gruppe von Dateien auf einen Remotecomputer sichert und die Originaldateien dann löscht. Solange keine Probleme auftauchen, möchten Sie auch, dass jede Zeile des Scripts ganz normal ausgeführt wird. Was ist aber, wenn der Remotecomputer nicht erreichbar ist? In diesem Fall versucht das Script die Dateien auf den Remotecomputer zu sichern. Dies schlägt dann natürlich fehl. Dann löscht das Script die Originaldateien. Problem erkannt? In einem Fall wie diesem wünschen Sie sich sicherlich ein Script, das eine bestimmte Bedingung überprüfen kann ("Ist der Remotecomputer erreichbar?") und nur weiterarbeitet, wenn diese Bedingung wahr ist. Eine solche Entscheidung können Sie über die Befehle If Then oder Select Case implementieren.
Mehrere Bedingungen prüfen Die Scripte, die in der ersten Hälfte dieses Kapitels verwendet wurde, trafen ihre Entscheidungen auf Basis einer einzelnen Bedingung. Wenn zum Beispiel die Menge an freiem Speicherplatz unter einen bestimmten Wert gefallen ist, dann wird ein Alarm ausgelöst: If FreeMegaBytes < WARNING_THRESHOLD Then Wscript.Echo objLogicalDisk.DeviceID & " is low on disk space." End If
Es könnte jedoch vorkommen, dass Sie eine bestimmte Aktion nur dann durchführen möchten, wenn zwei oder mehr Bedingungen zutreffen. Eine sofortige Alarmmeldung benötigen Sie zum Beispiel nur für die Mailserver. Bei allen anderen Servern möchten Sie nicht sofort benachrichtigt werden. Stattdessen soll zum Beispiel nur ein Protokolleintrag erzeugt werden. Der Alarm soll also nur dann angezeigt werden, wenn zwei Bedingungen wahr sind: der freie Plattenplatz liegt unter einer bestimmten Grenze und der Server ist ein Mailserver. Eine solche Entscheidung können Sie über den logischen Operator And (Und) implementieren. Im folgenden Script wird die Alarmmeldung nur in diesem Fall angezeigt. Wenn eine der beiden Bedingungen nicht wahr ist, dann wird kein Alarm angezeigt. If FreeMegaBytes < WARNING_THRESHOLD And ServerType = "Email" Then Wscript.Echo objLogicalDisk.DeviceID & " is low on disk space." End If
In anderen Fällen kann es zum Beispiel wichtig sein, eine Aktion durchzuführen, wenn eine oder einige der Bedingungen wahr sind. Zum Beispiel, wenn Sie eine Meldung anzeigen möchten, wenn der freie Speicherplatz unter einer bestimmten Grenze liegt, oder wenn der verfügbare Arbeitsspeicher unter eine bestimmten Grenze liegt. In Tabelle 2.17 sehen Sie alle möglichen Zustände und Entscheidungen für ein solches Script Tabelle 2.17: Entscheidungsmatrix Ausreichend SpeicherplatzAusreichen ArbeitsspeicherAlarmmeldung
Ja
Ja
Nein
Ja
Nein
Ja
Nein
Ja
Ja Seite 77 von 394
Ausreichend SpeicherplatzAusreichen ArbeitsspeicherAlarmmeldung
Nein
Nein
Ja
Solche Entscheidungen können Sie mit dem logischen Operator Or (Oder) implementieren: If FreeMegaBytes < WARNING_THRESHOLD Or AvailableMemory < MEMORY_THRESHOLD Then Wscript.Echo "Computer is low on disk space or memory." End If
Der logische Operator Or (Oder) gibt Wahr zurück, wenn irgendeine der Bedingungen wahr ist (oder wenn alle wahr sind).
If Then ElseIf Systemadministratoren müssen oft Entweder/Oder-Entscheidungen treffen. Entweder der Computer hat genügend Festplattenplatz oder nicht. In solchen Situationen bietet Ihnen das IfThen-Else-Konstrukt eine optimale Möglichkeit eine Bedingung mit minimalem Programmieraufwand zu implementieren. Natürlich kann es sein, dass es mehr als zwei mögliche Bedingungen gibt. Stellen Sie sich zum Beispiel vor, Sie möchten die Wartung von Festplatten nach den folgenden Kriterien entscheiden: • • •
Ein Laufwerk mit weniger als 100 MB verfügbarem Speicherplatz benötigt Ihre sofortige Aufmerksamkeit. Um ein Laufwerk mit weniger als 250 MB und mehr als 100 MB freiem Speicherplatz können Sie sich bei Gelegenheit kümmern. Ein Laufwerk mit mehr als 250 MB freiem Speicherplatz benötigt keine Aufmerksamkeit.
Um mehrere Alternativen zu implementieren, können Sie den If-Then-Else-Befehl verwenden. Mit diesem prüft das Script ganz normal die Bedingung (zum Beispiel x = 1). Wenn diese wahr ist, dann führt das Script die entsprechende Aktion aus. Wenn die Bedingung jedoch nicht wahr ist, dann können Sie eine zweite Bedingung prüfen lassen. Die Syntax hierzu sieht so aus: ElseIf x = 2 Then
Sie können so viele ElseIf-Befehle verwenden, wie Sie möchten. Statt des letzten ElseIfBefehls, verwenden Sie einen einfachen Else-Befehl. Das folgende Script zeigt - abhängig von den folgenden Bedingungen - jeweils unterschiedliche Meldungen an • • •
Weniger als 100 MB verfügbarer Speicherplatz Weniger als 250 MB und mehr als 100 MB freier Speicherplatz Mehr als 250 MB freier Speicherplatz
If FreeMegabytes <= 100 Then Wscript.Echo Computer.Name & " benötigt sofortige Aufmerksamkeit." ElseIf FreeMegabytes < 250 Then Wscript.Echo Computer.Name & " sollten Sie so schnell wie möglich überprüfen." Else Wscript.Echo Computer.Name & " verfügt über genügend Speicherplatz."
Seite 78 von 394
End If
Wenn Sie mehrere Bedingungen implementieren, dann prüft If-Then-Else alle Bedingungen, bis eine Bedingung gefunden wird, die wahr ist. In diesem Fall werden die Befehle hinter dieser Bedingung ausgeführt und keine weiteren Bedingungen mehr geprüft. Das bedeutet natürlich, dass Sie die Bedingungen sehr gewissenhaft anordnen müssen. Sehen wir uns zum Beispiel einmal das folgende Script an: x = 5 If x < 20 Then Wscript.Echo "X ist zwischen 11 und 20." ElseIf x < 11 Then Wscript.Echo " X ist zwischen 0 und 10." Else Wscript.Echo "X ist gleich 0." End If
Die Ausgabe des Scripts sehen Sie in Abbildung 2.19
Abbildung 2.19: Falsche Anordnung von Bedingungen Woher kommt dieses falsche Ergebnis? Die erste Bedingung prüft, ob X kleiner als 20 ist. Da dies immer wahr ist, wird natürlich auch immer die Nachricht 'X ist zwischen 11 und 20" ausgegeben. Nachdem die erste Bedingung wahr ist, werden keine weiteren Bedingungen geprüft. Damit das Script korrekt arbeitet, müssen Sie die Bedingungen neu anordnen: x = 5 If x = 0 Then Wscript.Echo "X ist gleich 0." ElseIf x < 11 Then Wscript.Echo " X ist zwischen 0 und 10." Else Wscript.Echo " X ist zwischen 11 und 20." End If
Einen Else-Befehl im Ende zu verwenden ist immer eine gute Idee. So können Sie auch die Fälle abfangen, in denen keine der ElseIf-Bedingungen zutrifft. Sehen Sie sich zum Beispiel das folgende Script an: If JobID = 1 Then Wscript.Echo "Dieser Benutzer ist ein Manager." ElseIf JobID = 2 Then Wscript.Echo " Dieser Benutzer ist kein Manager." End If
Es funktioniert sehr gut, solange jedem Benutzer eine JobID von 1 oder 2 zugewiesen wurde. Was passiert aber, wenn die JobID eines Benutzers 3 ist (oder wenn der Benutzer keine JobID hat)? Diesen Fall können Sie über einen abschließenden Else-Befehl abfangen: If JobID = 1 Then Wscript.Echo "Dieser Benutzer ist ein Manager." ElseIf JobID = 2 Then Wscript.Echo " Dieser Benutzer ist kein Manager."
Seite 79 von 394
End If Wscript.Echo "Diesem Benutzer wurde keine gültige Job-ID zugewiesen." End If
Select Case Der Befehl Select-Case ist eine strukturiertere Alternative zum Befehl If-Then-ElseIf. Wenn Sie mehr als drei Bedingungen überprüfen müssen, dann ist er meistens schneller und einfacher zu implementieren. Das folgende Script verwendet den Select-Case-Befehl zum Beispiel, um die WMI-Rückgabewerte zum Druckerstatus auszuwerten: Select Case PrinterStatus Case 1 strCurrentState Case 2 strCurrentState Case 3 strCurrentState Case 4 strCurrentState Case 5 strCurrentState End Select
= = = = =
"Other" "Unknown" "Idle" "Printing" "Warming Up"
Die gleiche Aufgabe mit If-Then-ElseIf-Befehlen zu lösen führt zu einem mehr als doppelt so langen Script, dass schwer zu lesen ist: If PrinterStatus = 1 Then strCurrentState = "Other" ElseIf PrinterStatus = 2 Then strCurrentState = "Unknown" ElseIf PrinterStatus = 3 Then strCurrentState = "Idle" ElseIf PrinterStatus = 4 Then strCurrentState = "Printing" ElseIf PrinterStatus = 5 Then strCurrentState = "Warming Up" End Select
Der Select-Case-Befehl wird folgendermaßen verwendet: 1.Beginnen Sie den Befehlsblock mit dem Befehl Select Case und dem Namen der Variable, die Sie auswerten möchten. 2.Fügen Sie für jeden potentiellen Wert der Variable einen Case-Befehl ein. Diesem CaseBefehl muss dann jeweils der Scriptcode folgen, der ausgeführt werden soll, wenn die Variable den mit Case angegebenen Wert enthält. 3.Beenden Sie den Select-Case-Block mit einem abschließenden End-Select-Befehl. Wenn keine der Case-Bedingungen zutrifft, dann können Sie diesen Fall mit einem abschließenden Case-Else-Befehl abfangen: Select Case PrinterStatus Case 1 strCurrentState = "Other" Case 2 strCurrentState = "Unknown" Case 3 strCurrentState = "Idle" Case 4 strCurrentState = "Printing" Case 5 strCurrentState = "Warming Up" Case Else strCurrentState = "Status cannot be determined." End Select
Außerdem haben Sie die Möglichkeit, die Case-Bedingung in eine eigene Zeile zu schreiben. Dieser Zeile können dann beliebig viele Zeilen mit Anweisungen für diesen Fall folgen: Case 1 strCurrentState = "Other" Wscript.Echo strCurrentState
Seite 80 von 394
Case 2
Arrays Scripte sind wohl am nützlichsten, wenn Sie dieselbe Aufgabe mehrfach ausführen können. Wenn Sie zum Beispiel nur über einen Computer verfügen, dann ist es nicht sehr mühsam, das Snap-In Dienste aufzumachen, und einfach zu schauen, welche Dienste installiert sind. Ein Script hierfür zu schreiben dürfte wohl deutlich länger dauern. Wenn Sie jedoch 100 Computer betreuen, dann ziehen Sie es sicher vor, wenn ein Script diese Aufgabe für Sie erledigt. Wenn ein Script Informationen von 100 Computern abfragen soll, dann muss es natürlich deren Namen irgendwo speichern. Außerdem muss es sich merken können, von welchen Computern es bereits Informationen abgefragt hat und von welchen nicht. Ein Standardweg zur Speicherung solcher Informationen ist die Verwendung von Arrays (Felder). Ein Array ist ein bestimmter Variablentyp, der mehrere Werte speichern kann. Anmerkung: Eine vollständige Besprechung zu Arrays - insbesondere die Verwendung von mehrdimensionalen Arrays - liegt außerhalb des Themenbereichs dieses Kapitels. Das Kapitel konzentriert sich auf die Verwendung von Arrays in administrativen Scripten.
Erstellen von Arrays Der einfachste Weg ein Array zu erstellen, ist die Funktion Array. Auch wenn das wirklich ganz einfach ist, sollten Sie zwei Dinge bei der Verwendung der Array-Funktion bedenken. Erstens können mit der Array-Funktion nur eindimensionale Arrays erstellt werden. Und zweitens müssen Sie bei dieser Funktion alle Elemente des Arrays bereits im Voraus kennen. Bei administrativen Aufgaben ist das normalerweise nicht der Fall - meist schreiben Sie ein Script, um genau diese Elemente (zum Beispiel die Dienste oder die Computer in einer OU) abzurufen. Wenn Sie die Elemente des Arrays bereits kennen, dann ist die Funktion Array natürlich trotzdem sehr nützlich. Ein Script in der ersten Hälfte dieses Kapitels sollte zum Beispiel auf drei Computern ausgeführt werden. Mit der Funktion Array können Sie die Namen dieser drei Computer in ein Array schreiben: Computers = Array("atl-dc-01", "atl-dc-02", "atl-dc-03")
Ein Array deklarieren und füllen Normalerweise kennen Sie die zu speichernden Elemente vorher nicht. Das Script könnte die Computernamen zu Beispiel aus einer Textdatei einlesen oder die in einem Ordner enthaltenen Dateinamen in einem Array speichern. In einem solchen Fall kennen Sie die Elemente und deren Anzahl erst dann, wenn das Script ausgeführt wird. Unter VBScript haben Sie die Möglichkeit, eine Variable als Array zu definieren. Diese Variable müssen Sie nicht sofort mit Elementen füllen. Hierzu legen Sie mit dem Befehl Dim die Variable und die maximale Anzahl der in ihr gespeicherten Elemente fest. Das erste Element eines Arrays wird immer durch die Indexnummer 0 bezeichnet - nicht durch 1. Die Indexnummer des letzten Elementes eines Arrays entspricht der Maximalzahl an Elementen minus 1. Wenn Ihr Array also 9 Elemente aufnehmen soll, dann müssen Sie es über den Dim-Befehl mit der Größe 8 definieren (9 - 1). Seite 81 von 394
Das folgende Beispiel erstellt einen Array mit dem Namen arrTestArray und der Größe 4 (4 1 = 3): Dim arrTestArray(3)
Nachdem Sie einen Array deklariert haben, können Sie ihn mit den gewünschten Werten auffüllen. Die nächste Codezeile weist Element 0 des Arrays (das erste Element) zum Beispiel den Wert "A" zu: arrTestArray(0) = "A"
Dieses einfache Script erstellt einen Array mit dem Namen arrTestArray und weist den vier Elementen des Arrays Werte zu: Dim arrTestArray(3) arrTestArray(0) = "A" arrTestArray(1) = "B" arrTestArray(2) = "C" arrTestArray(3) = "D"
So weit so gut. Was passiert aber, wenn Sie den Array ursprünglich zu klein deklariert haben? Was, wenn arrTestArray fünf Werte aufnehmen soll? Wenn Sie einfach versuchen dem fünften Elemente einen Wert zuzuweisen, dann werden Sie einen Fehler produzieren. Das folgende Beispielscript versucht einem Array mit vier Elementen einen fünften Wert zuzuweisen: Dim arrTestArray(3) arrTestArray(0) = "A" arrTestArray(1) = "B" arrTestArray(2) = "C" arrTestArray(3) = "D" arrTestArray(4) = "E"
Wie Sie sehen, erzeugt das Script den Fehler "Index außerhalb des gültigen Bereichs: ,(number: 4)' wenn Sie versuchen es auszuführen. In einer solchen Situation haben Sie zwei Möglichkeiten: •
•
Sie können den Array schon im Voraus größer machen. Sie deklarieren arrTestArray einfach mit 1.000 Elementen statt mit 4 Elementen. Das wäre allerdings in vielen Fällen eine sinnlose Speicherverschwendung, da Sie viele der Elemente ja gar nicht verwenden. Sie erstellen einen dynamischen Array.
Dynamische Arrays erstellen Ein dynamisches Array bietet Ihnen zwei Vorteile: • •
Sie müssen die Maximalgröße des Arrays nicht im Voraus festlegen. Die Größe des Arrays kann während der Ausführung des Scripts verändert werden.
Wenn Sie zum Beispiel die Computernamen aus einer Textdatei einlesen, und das Array ist zu klein, dann können Sie das Array für jeden weiteren Computernamen einfach vergrößern und zwar so lange, bis alle Computernamen gelesen und gespeichert wurden. Um ein dynamisches Array zu erstellen, müssen Sie zwei Dinge tun: Erstens dürfen Sie bei der Deklaration des Arrays keine Größe angeben. Stattdessen schreiben Sie einfach zwei Klammern: Dim arrTestArray()
Seite 82 von 394
Und zweitens müssen Sie den Befehl ReDim Preserve verwenden, um das Array zu vergrößern. Dieser Befehl erfüllt zwei Aufgaben: • •
Der Teil ReDim sorgt für die Vergrößerung des Arrays. Der Teil Preserve stellt sicher, dass die vorhandenen Inhalte des Arrays bei diesem Vorgang nicht gelöscht werden. Wenn Sie nur den Befehl ReDim verwenden, dann werden alle ArrayInhalte bei der Vergrößerung gelöscht.
Das folgende Script erstellt ein dynamisches Array mit dem Namen arrTestArray. Danach wird eine Variable mit dem Namen intSize erstellt. Dieser wird der Wert 0 zugewiesen. Mit der Variable wird dann das Array so neudimensioniert, dass es ein Element aufnehmen kann (1 - 1 = 0). Nachdem dem ersten Element (Index 0) ein Wert zugewiesen wurde, wird der Befehl ReDim Preserve verwendet, um das Array so zu vergrößern, dass es zwei Elemente aufnehmen kann (intSize + 1). Dem zweiten Element wird dann ebenfalls ein Wert zugewiesen: Dim arrTestArray() intSize = 0 ReDim Preserve arrTestArray(intSize) arrTestArray(intSize) = "A" ReDim Preserve arrTestArray(intSize + 1) arrTestArray(intSize + 1) = "B"
Um zu zeigen, wie der Befehl ReDim Preserve in einem echten Script zur Systemadministration verwendet werden kann, fragt das folgende Scripte eine Liste aller auf dem Computer installierten Dienste ab. Es speichert die Dienstnamen in einem dynamischen Array. Hierzu geht es folgendermaßen vor: 1.Es erstellt ein dynamisches Array mit dem Namen arrTestArray. 2.Es erstellt eine Variable mit dem Namen intSize und weist dieser den Wert 0 zu. 3.Es verbindet sich mit dem WMI-Dienst und fragt eine Liste der installierten Dienste ab. 4.Für jeden Dienst in der erhaltenen Collection führt es folgendes aus: 1.Es verwendet den Befehl ReDim Preserve, um die Größe von arrTestArray auf den Wert in der Variable intSize anzupassen. Beim ersten Dienst der Collection hat intSize den Wert 0. Das Array hat daher auch die Größe 0 - was bedeutet, dass ein Element in ihm gespeichert werden kann. 2.Es weist dem gerade erstellen Array-Element den Dienstnamen zu (objService.DisplayName). 3.Es erhöht den Wert von intSize um eins. Nach dem ersten Element hat intSize zum Beispiel den Wert 1 (den bisherigen Wert plus 1). 4.Es wiederholt diese Schritte für die verbleibenden Dienste aus der Collection. Dim arrTestArray() intSize = 0 strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colRunningServices = objWMIService.ExecQuery _ ("SELECT * FROM Win32_Service")
Seite 83 von 394
For Each objService in colRunningServices ReDim Preserve arrTestArray(intSize) arrTestArray(intSize) = objService.DisplayName intSize = intSize + 1 Next
Einen String mit Trennzeichen in ein Array konvertieren Um administrative Daten für eine Vielzahl von Anwendungen (inklusive Scripte) lesbar zu machen, werden diese Daten oft durch bestimmte Zeichen getrennt in Textdateien gespeichert. Das Trennzeichen ist in diesem Fall einfach das Zeichen, dass die einzelnen in der Textdatei gespeicherten Informationen voneinander trennt - typischer Weise ist dieses Zeichen ein Komma. Solche Dateien werden auch oft als CSV-Dateien (Comma Separated Values) bezeichnet. Eine Protokolldatei, in der die Namen von Servern, die Namen der Ereignisprotokolle auf diesen Servern und in diesen Protokollen aufgetretenen Fehlernummern gespeichert werden, könnte zum Beispiel so aussehen: atl-dc-01,DNS Server,13 atl-dc-01,System,14 atl-dc-02,Security,3 alt-dc-02,Application,22
Ein Vorteil von solchen Dateien ist, dass sie in viele andere Anwendungen importiert werden können. Microsoft Excel und Microsoft Access würden das Beispiel oben so interpretieren: Tabelle 2.18: Importierte Beispieldaten Feld 1
Feld 2
Feld 3
atl-dc-01DNS Server13 atl-dc-01System
4
atl-dc-02Security
3
atl-dc-02Application 22 Einen durch Trennzeichen aufgeteilten String können Sie mit der Funktion Split in ein Array umwandeln. Hierzu müssen Sie der Funktion zwei Parameter übergeben: • •
Den String mit den Werten und den Trennzeichen Das Trennzeichen
Im folgenden Script ist der zu trennende Text in der Variable TestString gespeichert. Als Trennzeichen wird das Komma verwendet: TestString = " atl-dc-01,DNS Server,13" TestArray = Split(TestString , ",") For i = LBound(TestArray) to UBound(TestArray) Wscript.Echo TestArray(i) Next
Wenn Sie das Script unter CScript ausführen, dann erhalten Sie die folgende Ausgabe: atl-dc-01 DNS Server 13
Seite 84 von 394
Alternativen zur Verwendung von Arrays Arrays sind ein schneller und einfacher Weg, um eindimensionale Daten zu speichern. Wenn Sie es jedoch mit multidimensionalen Daten zu tun haben, dann kann es schnell kompliziert werden. Bevor Sie also multidimensionale Arrays verwenden, sollten Sie über die folgenden Alternativen nachdenken: •
Dictionary-Objekt: Das Dictionary-Objekt ist Teil der Script-Laufzeitbibliothek und bietet Ihnen eine einfache Möglichkeit arrayähnliche Daten zu speichern, abzufragen und zu verändern. Weitere Informationen zum Dictionary-Objekt finden Sie im Kapitel ScriptLaufzeitbibliothek in diesem Buch
•
Disconnected Recordset: Ein "Disconnected Recordset" ist eine temporäre Datenbank, die nicht mit einer tatsächlichen physikalischen Datenbank verbunden ist. Stattdessen wird sie im Speicher erstellt und nach Beendigung des Scripts gelöscht. Wie beim Dictionary-Objekt auch, können Sie beim Recordset mit einem Namen oder einer Indexnummer auf die Elemente zugreifen. Außerdem haben Sie die gleichen Möglichkeiten, wie mit einem normalen Recordset (inklusive Sortieren). Das folgende Codebeispiel sortiert zum Beispiel ein eindimensionales Array: For i = LBound(SampleArray) to UBound(SampleArray) For j = LBound(SampleArray) to UBound(SampleArray) If j = UBound(SampleArray) Then Else If SampleArray(j) > SampleArray(j + 1) Then TempValue = SampleArray(j + 1) SampleArray(j + 1) = SampleArray(j) SampleArray(j) = TempValue End If End If Next Next Eein Recordset kann im Gegensatz dazu mit der folgenden einzelnen Codezeile sortiert werden: DisconnectedRecordset.Sort = 'FileSize' Weitere Informationen zu solchen Recordsets finden Sie im Kapitel Creating Enterprise Scripts in diesem Buch.
Fehlerbehandlung VBScript stellt Ihnen ein relativ einfaches Verfahren zur Behandlung von Laufzeitfehlern zur Verfügung. Laufzeitfehler sind alle Fehler, die nicht vom Compiler abgefangen werden können und daher erst nach dem Scriptstart auftreten. Das nächste Script produziert zum Beispiel einen Syntaxfehler, da der Befehl Wscript.Echo falsch geschrieben ist. Der Compiler kann die Codezeile nicht interpretieren. Bevor ein Script ausgeführt wird, wird jede Zeile des Scripts von der Script-Engine eingelesen und auf korrekte Syntax geprüft. Bevor das gesamte Script nicht so geprüft wurde, wird keine Zeile des Scripts ausgeführt. Daher wird die Fehlermeldung sofort ausgegeben - auch wenn der Fehler erst in Zeile 3 auftritt: Seite 85 von 394
Wscript.Echo "Line 1." Wscript.Echo "Line 2." W script.Echo "Line 3."
Das Nachrichtenfenster aus Abbildung 2.20 wird angezeigt.
Abbildung 2.20: Kompilierungsfehler Im Gegensatz dazu zeigt das folgende Script erst zwei Nachrichtenfenster an, bevor es zu dem Fehler kommt (der falsch geschriebene Befehl Wscript.Echo). Das liegt daran, dass Zeile 3 für den Compiler korrekt aussieht. Er kann nicht feststellen, dass Eho keine gültige Methode des Objektes Wscript ist. Daher zeigt er diese Zeile auch nicht als fehlerhaft an, und die Scriptausführung wird gestartet: Wscript.Echo "Line 1." Wscript.Echo "Line 2." Wscript.Eho "Line 3."
Wenn dann Zeile 3 des Scripts ausgeführt wird, wird die Fehlermeldung aus Abbildung 2.21 angezeigt. Achten Sie darauf, dass es sich hier um einen Laufzeitfehler statt um einen Kompilierungsfehler handelt.
Abbildung 2.21: Laufzeitfehler Laufzeitfehler kommen nicht nur durch falsch geschriebene Befehle zustande. Die nächste Codezeile ist zum Beispiel syntaktisch und typografisch korrekt. Sie erzeugt einen Laufzeitfehler, weil nicht auf den Remotecomputer zugegriffen werden kann: Set Computer = GetObject("winmgmts:\\InaccessibleComputer")
Das bedeutet, dass wir über das Auftreten von Laufzeitfehlern nur wenig Kontrolle haben (das Netzwerk kann fehlerhaft sein, ein Computer kann heruntergefahren worden sein usw.). Sie haben unter VBScript drei Möglichkeiten, solche Fehler zu handhaben. Die Vor- und Nachteile dieser drei Optionen sehen Sie in Tabelle 2.19. Tabelle 2.19: Vorteile und Nachteile der Optionen zur Fehlerbehandlung Option
Vorteile
Nachteile Seite 86 von 394
Option
Vorteile
Nachteile
Fehlschlag Kein Aufwand für den Scriptersteller Wenn das Script an irgendeinem des Scripts in erforderlich. Scripte schlagen fehl, wenn Punkt fehlschlägt, könnte dies dazu Kauf nehmen ein Laufzeitfehler auftritt. führen, dass der Computer in einem instabilen Zustand verbleibt. In vielen Fällen ist es besser, dass ein Script fehlschlägt, wenn es nicht korrekt Fehler müssen nicht fatal sein. Stellen ausgeführt werden kann. Wenn Sie zum Sie sich zum Beispiel ein Beispiel einem Script eine Sicherung von Überwachungsscript vor, das bestimmten Dateien vornehmen lassen Informationen über 100 Computer und das Script die Originaldateien danach abruft. Wenn Computer 1 nicht löschen soll, dann ist es besser, dass das erreicht werden kann, ist es trotzdem Script fehlschlägt, wenn die Dateien nicht sinnvoll, die Informationen der gesichert wenn können. So werden restlichen 99 Computer abzufragen. wenigstens nicht die Originaldateien gelöscht. Alle Fehler Scripte werden ohne Unterbrechung Probleme sind schwer zu finden, da im Script weiter ausgeführt. Sie keine Fehlermeldung erhalten. ignorieren Scripte führen den Großteil ihrer Der Computer kann in einen Aufgaben durch - auch wenn ein Problem unbekannten Zustand versetzt auftritt. Ein Script könnte zum Beispiel 99 werden, da möglicherweise einige von 100 Dateien erfolgreich kopieren. Aktionen ausgeführt werden und andere nicht. Endbenutzer erhalten keine Fehlermeldungen, auf die sie reagieren müssen. Auf Fehler Sie haben die Möglichkeit, im Script mit aussagekräftige Fehlermeldungen eigenem anzeigen zu lassen. Code reagieren Sie können versuchen, das Problem über den Scriptcode zu beheben. Wenn ein Script zum Beispiel nicht in der Lage ist eine Verbindung zu einem Remotecomputer aufzubauen, dann kann es beispielsweise nach einiger Zeit einen neuen Verbindungsversuch starten.
Erfordert zusätzliche Arbeit bei der Scripterstellung. Der Scriptautor muss wissen, welche Fehler auftreten können. Dann muss er diese über entsprechenden Scriptcode abfangen und handhaben.
Handhabung von Laufzeitfehlern In den meisten Fällen bedeutet die Handhabung von Laufzeitfehlern einfach folgendes: "Wenn ein Fehler auftritt, brich das Script nicht ab, sondern mache irgendetwas Anderes". Typischerweise ist "irgendetwas Anderes" in eines der folgenden Dinge: • •
Sie können den Fehler ignorieren und einfach mit der nächsten Zeile des Scripts weiter machen. Sie können den Fehler abfangen und mit Scriptcode auf den Fehler reagieren (zum Beispiel könnten Sie eine Fehlermeldung anzeigen). Seite 87 von 394
Beide Methoden werden über den Befehl On Error Resume Next implementiert. Mit diesem Befehl wird ein Script beim Auftreten eines Fehlers nicht einfach beendet. Stattdessen ignoriert das Script die fehlerhafte Codezeile einfach und versucht die nächste Codezeile auszuführen. Alle Fehler ignorieren Die bei weitem einfachste Form der Fehlerbehandlung ist das Ignorieren alle Fehler. Hierzu schreiben Sie in die erste Zeile des Scripts einfach den Befehl On Error Resume Next. Das folgende Beispielscript versucht die nicht existente WSH-Methode Wscript.X zu verwenden und verursacht so einen Laufzeitfehler. Das Script wird trotzdem ohne Fehlermeldung ausgeführt: On Error Resume Next Wscript.X "Testing 1-2-3." Wscript.X "Testing 4-5-6" Wscript.X "Testing 7-8-9"
Bedenken Sie, dass die Fehlerbehandlung erst mit dem Befehl On Error Resume Next beginnt. Das folgende Script führt zu einem Laufzeitfehler, da der Befehl erst nach der ersten fehlerhaften Scriptzeile verwendet wird: Wscript.Echo "Line 1." Wscript.Echo "Line 2." Wscript.Eho "Line 3." On Error Resume Next Wscript.Echo "Line 4."
Um sicherzustellen, dass die Fehlerbehandlung korrekt funktioniert, sollten Sie den Befehl On Error Resume Next direkt am Anfang des Scripts verwenden: On Error Resume Next Wscript.Echo "Line 1." Wscript.Echo "Line 2." Wscript.Eho "Line 3." Wscript.Echo "Line 4."
Wenn Sie das vorhergehende Script unter CScript ausführen, dann erhalten Sie die folgende Ausgabe: Line 1. Line 2. Line 4.
Wenn der Laufzeitfehler in der Zeile mit dem Befehl "Wscript.Eho ." auftritt, dann wird einfach die nächste Zeile des Scripts ausgeführt. Auf Fehler reagieren Statt Fehler in Ihrem Script zu ignorieren, könnten Sie Scriptcode schreiben, der auf diese Fehler reagiert. Ein solcher Code kann zum Beispiel 1.prüfen, ob der Wert des Err-Objektes einen anderen Wert als 0 hat (solange kein Laufzeitfehler auftritt, bleibt der Wert bei 0). 2.auf Fehler reagieren (zum Beispiel Err.Number und Err.Description anzeigen). 3.den Wert des Err-Objektes auf 0 zurücksetzen. Dies ist sehr wichtig, da es sonst nicht möglich ist, einen weiteren Fehler zu erkennen. Das folgende Script versucht als Erstes eine Verbindung mit dem WMI-Dienst herzustellen. Wenn dieser Verbindungsversuch fehlschlägt (wenn also Err einen anderen Wert als 0 hat), Seite 88 von 394
dann wird ein Nachrichtenfenster mit der Fehlernummer und der Fehlerbeschreibung angezeigt. Danach wird das Err-Objektauf 0 zurückgesetzt. Im nächsten Schritt versucht das Script eine Liste aller auf dem Computer installierten Dienste abzurufen. Dies schlägt fehl, da die Methode ExecQuery falsch geschrieben ist (ExcQuery). Das Script fragt erneut das Err-Objekt ab - wiederum wird ein Nachrichtenfenster angezeigt, wenn sein Wert ungleich 0 ist. Danach wird das Err-Objekt auf den Wert 0 zurückgesetzt. On Error Resume Next Set Computer = GetObject("winmgmts:") If Err <> 0 Then Wscript.Echo Err.Number & " -- " & Err.Description Err.Clear End If Set ServiceList = Computer.ExcQuery("SELECT * FROM Win32_Service") If Err <> 0 Then Wscript.Echo Err.Number & " -- " & Err.Description Err.Clear End If
Wenn Sie das Script unter WScript ausführen, erhalten Sie die in Abbildung 2.22 zu sehende Ausgabe.
Abbildung 2.22: WMI-Fehlermeldung
Aktivieren der Fehlerbehandlung Manchmal möchten Sie sicher in einem Teil eines Scripts eine Fehlerbehandlung implementieren und in einem anderen Teil nicht. Hierzu können Sie die Fehlerbehandlung anund abschalten: • •
Mit dem Befehl On Error Resume Next schalten Sie die Fehlerbehandlung an. Mit dem Befehl On Error GoTo 0 schalten Sie die Fehlerbehandlung aus.
Das folgende Script implementiert die Fehlerbehandlung zum Beispiel gleich in der ersten Zeile. In der zweiten Zeile tritt ein Fehler auf, da Wscript.Echo falsch geschrieben wurde aufgrund der Fehlerbehandlung wird das Script jedoch weiter ausgeführt. In Zeile 3 wird die Fehlerbehandlung aus- und in Zeile 5 wieder angeschaltet. Da es jedoch dazwischen nicht zu Fehlern kommt, wird das Script ohne Fehler ausgeführt: On Error Resume Next Wscript.Eco "A" On Error GoTo 0 Wscript.Echo "B" On Error Resume Next Wscript.Eco "C" Wscript.Echo "D"
Wenn Sie das Script unter Cscript ausführen, erhalten Sie die folgende Ausgabe: B
Seite 89 von 394
D
Beim folgenden Script handelt es sich um eine leicht abgewandelte Version des vorherigen Scripts. Es schlägt jedoch fehl, da während der abgeschalteten Fehlerbehandlung ein Fehler auftritt: On Error Resume Next Wscript.Eco "A" On Error GoTo 0 Wscript.Eco "B" On Error Resume Next Wscript.Eco "C" Wscript.Echo "D"
Wenn Sie das Script unter Cscript ausführen, wird die folgende Fehlermeldung ausgegeben: Microsoft (R) Windows Script Host, Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. Alle Rechte vorbehalten. C:\test.vbs(4, 1) Laufzeitfehler in Microsoft VBScript: Das Objekt unterstützt diese Eigenschaft oder Methode nicht.: 'Wscript.Eco'
Fehlerbehandlung in COM-Objekten Ein Problem, weswegen die Fehlerbehandlung schwieriger wird, ist die Tatsache, dass Scripte normalerweise COM-Objekte verwenden. Wenn im COM-Objekt selbst ein Fehler auftritt, bemerkt VBScript dies zwar - es ist möglicherweise jedoch nicht festzustellen, was den Fehler ausgelöst hat. Im folgenden Script versucht WMI zum Beispiel eine Bindung zum nicht existierenden Drucker TestPrinter aufzubauen - dies führt natürlich zu einem Fehler. Das Script versucht dann die Fehlernummer und die Fehlerbeschreibung abzufangen und anzuzeigen: On Error Resume Next Set Test = GetObject _ ("Winmgmts:root\cimv2:Win32_Printer.Name='TestPrinter'") Wscript.Echo Err.Number & VbTab & Err.Description
Wenn Sie das Script ausführen, dann erhalten nicht etwa eine normale VBScriptFehlernummer mit vier Ziffern oder eine Fehlerbeschreibung - stattdessen wird das Nachrichtenfenster aus Abbildung 2.23 angezeigt.
Abbildung 2.23: Fehlernummer und Fehlerbeschreibung eines COM-Objektes Das Problem liegt darin, dass dieser Fehler innerhalb von WMI aufgetreten ist. VBScript erfährt nur, dass ein Fehler aufgetreten ist. In einigen Scripten mag dies auch ausreichen - in anderen Scripten oder bei der Entwicklung von Scripten wäre es jedoch deutlich praktischer mehr Informationen über den Fehler zu erhalten. Viele COM-Objekte stellen eigene Mechanismen zu Fehlerbehandlung bereit. WMI erlaubt es Ihnen zum Beispiel das Fehlerobjekt SWbemLastError zu erstellen - über dieses erhalten Sie die folgenden Informationen:
Seite 90 von 394
• • •
Die aufgerufene Methode. Der Parameter, der für den Fehler verantwortlich ist. Der Name des WMI-Providers, in dem der Fehler aufgetreten ist.
Das folgende Script verwendet das Objekt SWbemLastError, und zeigt die über das Objekt abgefragten Informationen in einem Nachrichtenfenster an: On Error Resume Next Set Test = GetObject _ ("Winmgmts:root\cimv2:Win32_Printer.Name='TestPrinter'") Set WMI_Error = CreateObject("WbemScripting.SwbemLastError") Wscript.Echo WMI_Error.Operation & VbTab & _ WMI_Error.ParameterInfo & VbTab & WMI_Error.ProviderName
Die Ausgabe des Scripts sehen Sie in Abbildung 2.24.
Abbildung 2.24: Informationen aus dem Objekt SWbemLastError Wenn Sie SWbemLastError verwenden, dann dürfen Sie das Objekt erst nach der Codezeile erstellen, von der Sie glauben, dass sie fehlerhaft ist. Das folgende Script gibt zum Beispiel keine Fehlerinformationen zurück, da SWbemLastError vor dem Auftreten des Fehlers erstellt wird: On Error Resume Next Set WMI_Error = CreateObject("WbemScripting.SwbemLastError") Set Test = GetObject _ ("Winmgmts:root\cimv2:Win32_Printer.Name='TestPrinter'") Wscript.Echo WMI_Error.Operation & VbTab & _ WMI_Error.ParameterInfo & VbTab & WMI_Error.ProviderName
Prozeduren Die ersten Administrationsscripte, die Sie schreiben, werden wahrscheinlich sehr einfach sein - sie führen eine einzelne Aufgabe aus und sind dann beendet. Ein Beispiel hierfür wäre ein Script, das sich mit einem Computer verbindet, eine Liste mit allen auf diesem Computer ausgeführten Diensten abfragt und dann endet. Vielleicht verwenden Sie ein zweites Script, das bestimmte Dienste auf einem Computer startet, und ein drittes Script, das bestimmte Dienste beendet. Wenn Sie mehr Erfahrung gewinnen, dann fangen Sie wahrscheinlich an, komplexere Scripte zu schreiben - zum Beispiel indem Sie mehrere Scripte kombinieren. Statt drei separate Scripte zum Verwalten von Diensten zu verwenden, schreiben Sie ein Script, das drei unterschiedliche Aufgaben ausführen kann. Für ein solches Script könnten Sie beispielsweise Kommandozeilenparameter verwenden: •
Wenn Sie den Parameter /info angeben, dann gibt das Script Informationen über die ausgeführten Dienste zurück.
Seite 91 von 394
•
Wenn Sie den Parameter /start angeben, dann startet das Script bestimmte Dienste.
•
Wenn Sie den Parameter /stop angeben, dann beendet das Script bestimmte Dienste.
Wenn Ihr Script umfangreicher wird, werden Sie es wahrscheinlich praktisch finden, einzelne Scriptteile in Prozeduren zu verlagern. Prozeduren sind Scriptteile, die eine bestimmte Aufgabe ausführen. Das Script zur Verwaltung von Diensten könnte zum Beispiel drei Prozeduren verwenden: eine zum Abfragen der ausgeführten Dienste, eine, um die Dienste zu beenden und eine, um die Dienst zu starten. Wenn Sie den jeweiligen Scriptcode für die drei Aufgaben in drei Prozeduren platzieren, wird die Bearbeitung des Scripts viel einfacher außerdem verbessern Sei die Lesbarkeit Ihres Scripts, und es wird einfach, bestimmte Scriptteile in andere Scripte zu übernehmen. Auch wenn Scripte die gleichen Aufgaben immer wieder ausführen, sind Prozeduren sinnvoll. Zum Beispiel, wenn Sie bei jedem Fehler im Script eine bestimmte Nachricht anzeigen lassen möchten. Hierzu können Sie natürlich den entsprechen Scriptcode an all den Stellen Ihres Scripts einfügen, an denen ein Fehler auftreten kann. Dieses Verfahren erfordert jedoch einen recht hohen Aufwand - Sie müssen nicht nur den entsprechenden Code an möglicherweise sehr vielen Stellen einfügen, sondern bei einer Änderung der Fehlerausgabe müssen alle Stellen, an denen der Code verwendet wird, von Hand bearbeitet werden. Ein besserer Ansatz wäre eine Prozedur zu erstellen, die eine Fehlernachricht anzeigt und diese Prozedur dann bei einem Fehler aufzurufen. Bei diesem Verfahren müssen Sie bei einer Änderung der Fehlernachricht den entsprechenden Code nur noch an einer Stelle ändern. Unter VBScript können Sie zwei Arten von Prozeduren erstellen: •
Subroutinen - Diese Prozeduren geben normalerweise keinen Wert zurück (zum Beispiel eine Prozedur zur Anzeige einer Fehlernachricht).
•
Funktionen - Diese Prozeduren geben einen Wert zurück. Funktionenwerden zum Beispiel für mathematische Berechnungen verwendet - beispielsweise, wenn Sie eine Funktion erstellen, die den freien Festplattenplatz von Byte zu GB konvertiert und den GB-Wert dann zurückgibt.
Aufrufen einer Prozedur Normalerweise führt VBScript die einzelnen Zeilen eines Scripte der Reihe nach aus. Prozeduren bilden jedoch eine Ausnahme von dieser Regel. Subroutinen und Funktionen werden erst dann ausgeführt, wenn diese irgendwo im Script explizit aufgerufen werden. Wenn eine Subroutine nicht aufgerufen wird, dann wird sie auch nicht ausgeführt - egal, an welcher Stelle im Script sie sich befindet. Das folgende Script enthält zum Beispiel eine Subroutine, die sich in der Mitte des Scripts befindet - diese wird jedoch niemals ausgeführt: Wscript.Echo "A" Sub EchoLine2 Wscript.Echo "B" End Sub Wscript.Echo "C"
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: A C
Seite 92 von 394
Da die Subroutine nicht aufgerufen wird, wird auch der in ihr enthaltene Code nicht ausgeführt. Das Script läuft also folgendermaßen ab: 1.Zeile 1 wird ausgeführt - der Text 'A' wird angezeigt. 2.Zeile 2 wird als Anfang einer Subroutine erkannt - da die Subroutine hier jedoch nur definiert und nicht aufgerufen wird, werden die Zeilen 2, 3 und 4 nicht ausgeführt. 3.Das Script fährt mit Zeile 5 fort - der ersten Zeile nach der Subroutine. Danach ist das Script beendet. Um die Subroutine auch auszuführen, müssen Sie sie explizit aufrufen. Als Befehl verwenden Sie hierzu den Namen der Subroutine. Das folgende Script gibt zum Beispiel erst den Text 'A' aus und ruft dann die Subroutine mit dem Namen EchoLineB aus - danach wird der Text 'C' ausgegeben: Wscript.Echo "A" EchoLine2 Wscript.Echo "C" Wscript.Quit Sub EchoLineB Wscript.Echo "B" End Sub
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: A B C
Das Script wird in diesem Fall folgendermaßen ausgeführt: 1.Zeile 1 wird ausgeführt, und der Text 'A' wird ausgegeben. 2.In Zeile 2 wird die Subroutine mit dem Namen EchoLineB aufgerufen. 3.Das Script springt zu Zeile 5 - in der die Subroutine beginnt. 4.Zeile 6 (die erste Zeile in der Subroutine) wird ausgeführt - der Text 'B' wird angezeigt. 5.In Zeile 7 ist die Subroutine beendet. Daher springt das Script zu der Zeile nach dem Scriptaufruf (Zeile 3 - die Subroutine wurde ja in Zeile 2 aufgerufen). 6.Zeile 3 wird ausgeführt - der Text 'C' wird ausgegeben 7.Zeile 4 wird ausgeführt, und das Script ist damit beendet. Prozeduren können an jeder beliebigen Position im Script platziert werden - auf die Funktionsweise oder die Leistung des Scripts hat dies keinen Einfluss. Die Platzierung der Prozeduren kann jedoch Auswirkungen auf die Lesbarkeit Ihrer Scripte haben. Weitere Informationen zur Platzierung von Prozeduren finden Sie im Kapitel Scripting Guidelines in Teil 3 dieses Buches. Das folgende Script verwendet zur Anzeige von möglichen WMI-Fehlern eine Prozedur: On Error Resume Next Set objWMIService = GetObject("Winmgmts:root\cimv2") If Err <> 0 Then ErrorHandler End If Set colPrinters = objWMIService.ExecQuery _
Seite 93 von 394
("SELECT * FROM Win32_Printer WHERE Name='TestPrinter'") If Err <> 0 Then ErrorHandler End If For Each objPrinter in colPrinters Wscript.Echo objPrinter.Name Next Sub ErrorHandler Select Case Hex(Err.Number) Case "80041001" Wscript.Echo "The call failed." Case "80041002" Wscript.Echo "The object could not be found." Case "80041010" Wscript.Echo "The specified class is not valid." Case "8004103A" Wscript.Echo "The specified object path was invalid." Case "80041048" Wscript.Echo "The specified class is not supported." Case Else Wscript.Echo "An unknown error occurred." End Select Err.Clear End Sub
Funktionen Wie Subroutinen auch, fassen Funktionen Codestücke zusammen - sie sollten jedoch einen Wert zurückgeben. Leider kümmert sich VBScript nicht darum, ob die Funktion auch tatsächlich einen Wert zurückgibt. Wenn Sie eine Funktion deklarieren, erstellt VBScript automatisch eine Variable, die denselben Namen wie die Funktion hat - in dieser Variable wird der Rückgabewert der Funktion gespeichert. Das folgende Script verwendet zum Beispiel den Befehl Wscript.Echo ThisDate. ThisDate ist der Name einer Funktion, die das aktuelle Datum zurückgibt. Für VBScript bedeutet das: • • • •
Der Befehl Wscript.Echo ThisDate führt zwei Aufgaben aus: Erst wird die Funktion ThisDate aufgerufen - diese schreibt das aktuelle Datum in die zur Funktion gehörende Variable ThisDate. Nachdem die Funktion vollständig ausgeführt wurde, verwendet der Befehl Wscript.Echo den Wert der Variable, um das Datum auszugeben. Obwohl der Befehl Option Explicit verwendet wird und die Variable nie deklariert wurde, kommt es zu keinem Fehler. Dies liegt daran, dass die Variable zusammen mit der Funktion von VBScript deklariert und initialisiert wird.
Option Explicit Wscript.Echo ThisDate Function ThisDate ThisDate = Date End Function
Bedenken Sie, dass dieser Ansatz nur mit Funktionen und nicht mit Subroutinen funktioniert. Das folgende Script führt zu einem Laufzeitfehler, da VBScript das Datum nicht zum Namen der Subroutine zuweisen kann: Wscript.Echo ThisDate Sub ThisDate
Seite 94 von 394
ThisDate = Date End Sub
Parameter an Funktionen übergeben Funktionen werden oft für mathematische Berechnungen verwendet. Das Ergebnis der Berechnung ist dann der Rückgabewert der Funktion. Ein Beispiel hierfür wäre eine Funktion, die Byte in MB oder Pfund in Kilogramm konvertiert. Hierzu müssen Sie der Funktion die entsprechenden Werte übergeben. Wenn Sie zum Beispiel die Zahlen 1 und 2 addieren möchten, dann benötigt die Funktion diese beiden Werte. Sie werden ihr als Parameter (auch Argumente genannt) übergeben. Um Parameter an eine Funktion zu übergeben, nehmen Sie diese einfach mit in den Funktionsaufruf auf. Die folgende Codezeile ruft zum Beispiel die Funktion AddTwoNumbers auf und übergibt Ihr die Wert 1 und 2 als Parameter: AddTwoNumbers(1 , 2)
Damit Sie einer Funktion Parameter übergeben können, müssen diese in der Funktion definiert werden. Hierzu fügen Sie der Funktion einfach die entsprechende Anzahl an Variablen hinzu. Die folgende Codezeile definiert eine Funktion, die drei Parameter akzeptiert: Function AddThreeNumbers(x, y, z)
Wenn die Zahl der von der Funktion erwarteten Parameter nicht mit der Zahl der Parameter im Funktionsaufruf übereinstimmt, kommt es zu einem Fehler. Das folgende Script erzeugt einen solchen Fehler. Es werden zwei Parameter an die Funktion übergeben - diese erwartet jedoch überhaupt keine Parameter: x = 5 y = 10 Wscript.Echo AddTwoNumbers(x, y) Function AddTwoNumbers AddTwoNumbers = a + b End Function
Um dieses Problem zu beheben, müssen Sie die Funktionsdefinition ändern und ihr die zwei Parameter hinzufügen: x = 5 y = 10 Wscript.Echo AddTwoNumbers(x, y) Function AddTwoNumbers(a, b) AddTwoNumbers = a + b End Function
Sicher ist Ihnen aufgefallen, dass die Parameter im Funktionsaufruf andere Namen haben (x und y) als die Parameter in der Funktionsdefinition (a und b). Es ist nicht notwendig, dass die Parameter identische Namen verwenden. Wichtig ist nur, dass die Reihenfolge der Parameter übereinstimmt. Da x der erste Parameter im Funktionsaufruf ist, wird der Wert von x der Variable a zugewiesen. y ist der zweite Parameter - daher wird der entsprechende Wert natürlich b zugewiesen. Das folgende Beispiel soll zeigen, wie eine Funktion in einem echten Administrationsscript verwendet wird. Es fragt den freien Speicherplatz von Laufwerk C ab und ruft dann eine Funktion mit dem Namen FreeMegabytes auf. Diese Funktion konvertiert den freien Speicherplatz von Byte in MB und gibt diesen Wert zurück. Der neue Wert wird dann vom Script ausgegeben: Seite 95 von 394
Set objWMIService = GetObject("winmgmts:") Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") Wscript.Echo FreeMegaBytes(objLogicalDisk.FreeSpace) Function FreeMegabytes(FreeBytes) FreeMegabytes = FreeBytes / 1048576 FreeMegabytes = Int(FreeMegabytes) End Function
Bedenken Sie, dass VBScript jedes Mal versucht die Funktion aufzurufen, wenn deren Name im Script auftaucht. Auch wenn es immer eine Variable mit dem Namen der Funktion gibt, können Sie deren Wert nicht einfach abfragen. Stattdessen wird immer die Funktion aufgerufen, und Sie erhalten den Rückgabewert der Funktion. Das folgende Script ruft zum Beispiel die Funktion FreeMegaBytes auf und versucht im nächsten Schritt auf die Variable FreeMegabytes zuzugreifen. Da auch hier die Funktion aufgerufen wird, kommt es zu einem Fehler - denn die Funktion erwartet ja einen Parameter: Set objWMIService = GetObject("winmgmts:") Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") Wscript.Echo FreeMegaBytes(objLogicalDisk.FreeSpace) Wscript.Echo "Free space: " & FreeMegaBytes Function FreeMegabytes(FreeBytes) FreeMegabytes = FreeBytes / 1048576 FreeMegabytes = Int(FreeMegabytes) End Function
Wenn Sie das Script ausführen, erhalten Sie die folgende Fehlermeldung:
Abbildung 2.25: Fehlermeldung durch einen fehlerhaften Funktionsaufruf Wenn Sie später auf den Rückgabewert der Funktion zugreifen möchten ohne die Funktion erneut aufzurufen, dann müssen Sie diesen in einer separaten Variablen speichern. Das folgende Script speichert den Rückgabewert der Funktion FreeMegabytes in der Variable AvailableSpace. Mit dieser Variable können Sie dann jederzeit auf den Wert zugreifen, ohne die Funktion erneut aufrufen zu müssen: Set objWMIService = GetObject("winmgmts:") Set objLogicalDisk = objWMIService.Get("Win32_LogicalDisk.DeviceID='c:'") AvailableSpace = FreeMegaBytes(objLogicalDisk.FreeSpace) Wscript.Echo "Free space: " & AvailableSpace Function FreeMegabytes(FreeBytes) FreeMegabytes = FreeBytes / 1048576 FreeMegabytes = Int(FreeMegabytes) End Function
Parameterübergabe by Value oder by Reference In den seltensten Fällen werden die Parameter in einem Script fest eingetragen. Normalerweise werden sie über Variablen an eine Funktion übergeben. In den folgenden beiden Codezeilen wird der Wert der Variable x auf 100 gesetzt und dieser Variable wird als Parameter an die Funktion ModifyValue übergeben: Seite 96 von 394
x = 100 Wscript.Echo ModifyValue(x)
Im Moment des Funktionsaufrufes hat x den Wert 100. Der Wert von x nach der Ausführung der Funktion hängt von zwei Dingen ab: Erstens, ob die Funktion den Wert ändert, und zweitens, ob der Wert by value(als Wert) oder by reference (als Referenz) übergeben wurde. Um den Unterschied beider Übergabearten zu erkennen sehen Sie sich das folgende Script an. Es setzt den Wert von x auf 100. In der Funktion wird der Wert von x dann auf 99 geändert: x = 100 Wscript.Echo ModifyValue(x) & VbTab & x Function ModifyValue(x) ModifyValue = x / 25 x = 99 End Function
Die Ausgabe des Scripts sehen Sie in Abbildung 2.26. Wie Sie sehen können, wurde x einmal durch 25 geteilt, und dann wurde x der neue Wert 99 zugewiesen.
Abbildung 2.26: In einer Funktion einem Parameter einen neuen Wert zuweisen In vielen Scripten ist es völlig egal, ob die Funktion den Wert von x ändert. Wenn Sie jedoch den Originalwert von x später noch benötigen, dann geraten Sie möglicherweise in Schwierigkeiten. Standardmäßig werden Variablen unter VBScript bei Funktionsaufruf by reference (als Referenz) übergeben. Das bedeutet, dass die Funktion nicht den Wert erhält, der in der Variable gespeichert ist, sondern einen Zeiger auf die Speicherstelle, in der der Wert der Variable gespeichert ist. Daher greift die Funktion natürlich auch auf den Originalwert selbst zu. Um sicherzustellen, dass der Wert einer Variable nicht in einer Funktion geändert wird, übergeben Sie den entsprechenden Parameter by value (als Wert). In diesem Fall erhält die Funktion nicht einen Zeiger auf die Speicherstelle der Originalvariable, sondern nur den Wert, der in dieser gespeichert ist. Wenn die Funktion an diesem Wert Änderungen vornimmt, dann sind die Originalspeicherstelle (die Originalvariable) und deren Wert hiervon nicht betroffen. Um eine Variable by value zu übergeben, verwenden Sie das Schlüsselwort ByVal in der Funktionsdefinition. Das folgende Script übergibt den Parameter x auf diese Art: x = 100 Wscript.Echo ModifyValue(x) & VbTab & x Function ModifyValue(ByVal x) ModifyValue = x / 25 x = 99 End Function
Die Ausgabe des Scripts sehen Sie in Abbildung 2.27. Wie Sie sehen, wird die Variable x nicht verändert. Auch wenn es so scheint, dass ihr Wert in der Funktion auf 99 gesetzt wird. In Wirklichkeit arbeitet die Funktion nur mit einer Kopie der Originalvariablen - deren Wert wird auf 99 gesetzt. Nachdem die Funktion beendet ist wird diese Kopie verworfen. Seite 97 von 394
Abbildung 2.27: Parameter by value übergeben Sie können für jeden Parameter einzeln festlegen ob er ByVal oder ByRef übergeben wird. Beide Varianten können in einer Funktionsdefinition beliebig gemischt werden: Function TestFunction(ByVal x, ByRef y)
Rekursion Rekursion ist eine Programmiertechnik, bei der eine Subroutine oder Funktion sich selbst aufruft. Möglicherweise haben Sie schon einmal ein Bild gesehen, bei der sich der Fotograf in einem Spiegel selbst fotografiert hat - der Fotograf fotografiert sein Spiegelbild, dass ein Spiegelbild fotografiert, dass wiederum sein Spiegelbild fotografiert usw. Bei der Rekursion passiert genau das gleiche: Subroutine A ruft Subroutine A auf, die wiederum Subroutine A aufruft, die wiederum Subroutine A aufruft usw. Auch wenn das Konzept der Rekursion sehr bizarr erscheint und schwer zu verstehen ist, handelt es sich doch um ein wichtiges Konzept. Nehmen wird zum Beispiel an, Sie möchten alle Dateien in einem Ordner auflisten. Auf den ersten Blick ganz einfach - aber was machen Sie, wenn der Ordner weitere Unterordner enthält? Und was, wenn diese wiederum Unterordner enthalten? In diesem Fall haben Sie mit der Rekursion die Möglichkeit alle Ordner zu durchlaufen - auch wenn Sie die Ordnerstruktur nicht kennen. Um die in Abbildung 2.28 zu sehende Ordnerstruktur zu durchlaufen, beginnen Sie beim Ordner Scripte und listen alle Unterordner auf. Für jeden der Unterordner listen Sie wiederum alle Unterordner auf, usw.
Abbildung 2.28: Beispiel-Ordnerstruktur Das folgende Script führt eine solche Rekursion durch. Hierzu verwendet es die Funktion ShowSubFolders. Diese wird so lange aufgerufen, bis alle Ordner durchlaufen wurden. Set FSO = CreateObject("Scripting.FileSystemObject") ShowSubfolders FSO.GetFolder("C:\Scripts") Sub ShowSubFolders(Folder) For Each Subfolder in Folder.SubFolders Wscript.Echo Subfolder.Path ShowSubFolders Subfolder Next End Sub
Die Funktion ShowSubFolders funktioniert folgendermaßen: Seite 98 von 394
1.Sie ruft eine Collection mit den Unterordnern des Startordners ab (c:\scripte). In dieser Collection gibt es zwei Elemente (Unterordner 1 und Unterordner 2). 2.Dann gibt sie den Ordnerpfad für das erste Element (Unterordner 1) aus und ruft sich selbst mit dem Ordner als Parameter auf. 3.Sie ruft eine Collection mit allen Unterordnern von Unterordner 1 ab. Diese Collection hat zwei Elemente: Unterordner 1A und Unterordner 1B. 4.Sie nimmt das erste Element der Collection (Unterordner 1A) und gibt den Ordnerpfad aus. Dann ruft sie sich selbst mit dem Namen dieses Ordners als Parameter auf. 5.Da Unterordner 1A keine weiteren Unterordner mehr besitzt, geht es mit dem nächsten Element der Collection weiter (Unterordner 1B). Die Funktion ruft sich nun selbst mit dem Namen dieses Ordners als Parameter auf. 6.Da Unterordner 1B keine weiteren Unterordner mehr hat, ist die Rekursion von Unterordner 1 beendet. Die Funktion macht mit dem zweiten Element (Unterordern 2) der ersten Collection (Unterordner von c:\scripte) weiter und wiederholt den gesamten Vorgang. Wenn Sie das Script ausführen, erhalten Sie die folgende Ausgabe: C:\scripte\Unterordner C:\scripte\Unterordner C:\scripte\Unterordner C:\scripte\Unterordner C:\scripte\Unterordner C:\scripte\Unterordner C:\scripte\Unterordner
1 1\Unterordner 1\Unterordner 2 2\Unterordner 2\Unterordner 2\Unterordner
1A 1B 2A 2B 2C
Bei der Rekursion handelt es sich um eine extrem mächtige Technik, um Daten in Baumstrukturen (zum Beispiel in Active Directory oder im Dateisystem) zu durchsuchen und anzuzeigen.
COM-Objekte Bei COM (Component Object Model) handelt es sich um einen Standardweg für Anwendungen (.exe-Dateien) und Bibliotheken (.dll-Dateien), um ihre Funktionalität anderen Anwendungen zur Verfügung zu stellen. Ohne COM müsste jeder, der die Systemadministration automatisieren will, ein erfahrener C++- oder Visual BasicProgrammierer sein und sich mit den Windows APIs (Application Programming Interfaces) auskennen. COM-Komponenten sind Dateien (normalerweise .exe- oder .dll-Dateien), die Beschreibungen der Objekte enthalten, die eine Komponente zur Verfügung stellt (diese Beschreibungen werden auch Klassen genannt). Wenn Sie ein COM-Objekt in einem Script erstellen (dieser Prozess wird auch als Instanzierung bezeichnet), dann erstellen Sie eine neue Kopie dieses Objektes (eine Instanz) auf Basis der Objektbeschreibung (Klasse). Nachdem die Instanz erstellt wurde, können Sie auf die Eigenschaften, Methoden und Ereignisse zugreifen, die vom neuen Objekt zur Verfügung gestellt werden. Objekte, die ihre Funktionalität so über COM zur Verfügung stellen, werden auch COMServer genannt. Anwendungen oder Scripte, die diese Funktionalitäten nutzen, werden als COM-Clients bezeichnet. Wenn Sie zum Beispiel ein Script schreiben, das WMI verwendet, Seite 99 von 394
dann ist WMI der COM-Server und Ihr Script ist der COM-Client. COM-Server können auf zwei Arten implementiert werden: •
Out-Of-Process-Server - Out-Of-Process-Server werden normalerweise über ausführbare Dateien implementiert und werden in einem anderen Prozess als das Script ausgeführt. Wenn Sie zum Beispiel ein Script starten, dann wird eine Instanz von Wscript.exe gestartet. Wenn Sie in dem Script dann eine Instanz des Microsoft Word-Objektes erstellen, dann wird diese in einem weiteren Prozess gestartet.
•
In-Process-Server - Bibliotheken (.dll-Dateien) sind normalerweise In-Process-Server - sie werden unter demselben Prozess ausgeführt wie die Anwendung oder das Script, dass sie aufgerufen hat. Wenn Sie zum Beispiel in einem Script eine neue Instanz des Objektes FileSystemObject erstellen, dann wird kein neuer Prozess für dieses Objekt gestartet. Dies liegt daran, dass es sich beim Objekt FileSystemObject (das in der DLL Scrrun.dll definiert ist) um einen In-Process-Server handelt - dieser wird im selben Prozess ausgeführt, wie das Script, das Ihn aufgerufen hat. In-Process-Server werden normalerweise schneller ausgeführt als Out-Of-Process-Server, da das Betriebssystem nicht über Prozessgrenzen hinweg arbeiten muss, um auf Methoden und Eigenschaften zuzugreifen.
Wie wir schon weiter oben in diesem Kapitel besprochen haben, arbeitet VBScript mit Objekten, die sich Automatisationsobjekte nennen. Alle COM-Objekte müssen eine oder mehrere Schnittstellen zur Verfügung stellen. Über diese Schnittstellen können die COMClients auf die COM-Server zugreifen. Alle Objekte, die die Schnittstelle IDispatch zur Verfügung stellen, werden Automatisationsobjekte genannt. Da nicht alle COM-Objekte diese Schnittstelle anbieten, kann VBScript auch auf alle COM-Objekte des Computers zugreifen.
Der COM-Prozess Als Scriptautor müssen Sie nur wissen wie Sie eine Referenz auf ein Automatisationsobjekt erstellen. Sie müssen sich keine Sorgen über das Auffinden oder das Laden des Objekts machen, da dies das Windows-Betriebssystem für Sie erledigt. Dennoch ist es nützlich, wenn Sie wissen was passiert, wenn Sie in Ihrem Script die Methode CreateObject verwenden. Die Referenzierung eines COM-Objektes ist sogar relativ simpel. Wenn Sie eine Anwendung oder eine Bibliothek installieren, die eine Objektklasse zur Verfügung stellt, dann registriert sich diese selbst im Betriebssystem. Bei dieser Registrierung teilt die Komponente dem Betriebssystem folgendes mit: • •
Dass es einen neuen COM-Server gibt. Die Objektklassen, die der neue COM-Server zur Verfügung stellt.
Hierzu werden Registrierungsschlüssel unter dem Pfad HKEY_CLASSES_ROOT der Registrierung erstellt. Unter den neuen Registrierungsschlüsseln gibt es einen, der den Programmatic Identifier (ProgID) für jede neue Objektklasse definiert. Die ProgID ist ein kurzer String, der den Namen der Objektklasse angibt. Sie wird von den Methoden CreateObject und GetObject verwendet, um das Objekt anzugeben, das Sie erstellen möchten. In der folgenden Codezeile ist die ProgID zum Beispiel Excel.Application: Set TestObject = CreateObject("Excel.Application")
Bei der ProgID handelt es sich um die Information, die das Betriebssystem benötigt, um das COM-Objekt zu finden und zu instanzieren. Ein neues Objekt erstellen Seite 100 von 394
Wenn die Methode CreateObject ausgeführt wird, dann liest die Script Engine die ProgID aus und übergibt sie der COM-API. Diese API erstellt die Objektreferenz. Mit der folgenden Codezeile wird ein neues Objekt mit der ProgIDScripting.FileSystemObject erstellt: Set TestObject = CreateObject("Scripting.FileSystemObject")
Die COM-API sucht im Pfad HKEY_CLASSES_ROOT der Registrierung nach einem Unterschlüssel mit dem Namen der ProgID. Wenn ein solcher Schlüssel gefunden wird, dann liest die API einen Unterschlüssel mit dem Namen CLSID unter dem ProgID-Schlüssel aus. Der Unterschlüssel CLSID enthält die GUID (Globally Unique Identifier) des zu erstellenden Automatisationsobjekts. Eine GUID sieht zum Beispiel so aus: {172BDDF8-CEEA-11D1-8B05-00600806D9B6}
Mit der GUID findet und verwendet das Betriebssystem COM-Objekte tatsächlich - die ProgID ist nur ein Alias, damit der Scriptautor leichter auf die Komponente zugreifen kann. Nachdem die GUID gefunden wird, wird im Schlüssel HKEY_LOCAL_MACHINE\Software\Classes\CLSID nach einem Unterschlüssel mit demselben Namen wie die GUID gesucht. Wenn das Betriebssystem diesen Schlüssel findet, sucht es hier nach dem Speicherort der ausführbaren Datei oder der Bibliotheksdatei der Komponente (im Fall des Objekts FileSystemObject ist das C:\Windows\System32\Scrrun.dll). Dann lädt die COM-API die Anwendung oder die Bibliothek, erstellt das Objekt und gibt eine Objektreferenz an das Script zurück. Servermodus Wenn ein Objekt über eine ausführbare Datei erstellt wird, dann wird die Anwendung in einem speziellen Modus gestartet - dem Servermodus oder auch Embedded-Mode. In diesem Modus wir die Anwendung ausgeführt und ist voll funktionsfähig, es werden nur keine GUIElemente angezeigt (Sie können trotzdem über den Taskmanager prüfen, ob die Anwendung ausgeführt wird). Der Servermodus ermöglicht es die Anwendung zu verwenden ohne dass der Benutzer hierdurch gestört wird. Auch wenn der Servermodus oft sehr nützlich ist, möchten Sie vielleicht doch manchmal die Benutzerschnittstelle einer Anwendung anzeigen (zum Beispiel, wenn Sie Daten im Internet Explorer anzeigen). Wenn dies der Fall ist, dann müssen Sie dies dem COM-Objekt über den entsprechenden Befehl mitteilen. Das folgende Script erstellt zum Beispiel eine Instanz des Internet Explorer und verwendet die Eigenschaft Visible des Objektes, um die Anwendung für den Benutzer sichtbar zu machen: Set IE = CreateObject("InternetExplorer.Application") IE.Visible = True
Bindung Mit Bindung wird die Art bezeichnet, auf die ein Script oder eine Anwendung auf ein COMObjekt zugreift. Wenn Sie eine Objektreferenz auf ein Automatisationsobjekt erstellen, dann muss VBScript prüfen, ob das Objekt existiert und dass die Methoden und Eigenschaften, auf die Sie zureifen, vom Objekt zur Verfügung gestellt und korrekt verwendet werden. Dieser Prozess wird Bindung genannt. COM unterstützt zwei Arten der Bindung: die frühe Bindung (early) und die späte Bindung (late). Bei der frühen Bildung werden Objekt, Eigenschaften und Methoden bereits bei der Compilierung der Anwendung überprüft. Wenn es zu diesem Zeitpunkt bereits Probleme gibt, schlägt die Compilierung fehl. Die frühe Bindung ist schneller als die späte Bindung, da das Seite 101 von 394
Objekt vor dem Ausführen der Anwendung überprüft wird. Außerdem ermöglicht die frühe Bindung einen Zugriff auf die Typenbibliothek des Objekts - diese enthält Informationen über die Methoden und Eigenschaften des Objektes. Da es sich bei VBScript nicht um eine Compilersprache handelt, wird die frühe Bindung nicht unterstützt. Stattdessen müssen Sie die späte Bindung verwenden - sie wird erst dann durchgeführt, wenn das Script ausgeführt wird. Bei der späten Bindung muss das Script Informationen über Objekt, Methoden und Eigenschaften aus der Registrierung entnehmen. Da VBScript keinen Zugriff auf die Objektbibliothek des Objektes hat, muss der Registrierungszugriff bei jeder Verwendung des Objekts durchgeführt werden. Außerdem können fehlerhafte Zugriffe erst dann festgestellt werden, wenn das Script ausgeführt wird.
Eine Methode für die Bindung eines Automatisationsobjekts auswählen Die Bindung an ein Automatisationsobjekt ist sehr einfach - der schwierigste Teil ist die Auswahl der geeigneten Methode (GetObject oder CreateObject?). In dem meisten Fällen hängt dies vom Objekt ab - einige generelle Richtlinien finden Sie jedoch in Tabelle 2.20. Tabelle 2.20: Methoden zur Bindung von Automatisationsobjekten Auszuführende Aufgabe
Methode
Binden an WMI oder ADSI.
GetObject mit dem entsprechenden Moniker. Ein Moniker ist ein Zwischenobjekt, das es ermöglicht, eine Referenz auf ein Automatisationsobjekt zu finden, zu aktivieren und zu erstellen. Sowohl auf WMI als auch auf ADSI wird über Moniker zugegriffen. Ihre Scripte können so auf WMI- und ADSI-Objekte zugreifen, ohne dass Ihnen die physikalische Position dieser Objekte bekannt ist. Typischerweise werden Moniker für den Zugriff auf Objekte verwendet, die nicht im Dateisystem gespeichert sind. Set TestObject = GetObject("winmgmts:")
Binden an eine neue Instanz eines Automatisationsobjekts.
CreateObject mit der entsprechenden ProgID. Set TestObject = CreateObject("Word.Application")
Binden an eine bestehende Instanz eines Automatisationsobjekts.
GetObject mit der entsprechenden ProgID. Set TestObject = GetObject("Word.Application")
Binden an ein GetObject mit der entsprechenden ProgID. Automatisationsobjekt über eine Set TestObject = GetObject("c:\scripts\test.xls") bestehende Datei. Binden an eine neue Instanz CreateObject mit der entsprechenden ProgID und einer eines Automatisationsobjekts mit Variable für die Ereigniszuordnung. der Möglichkeit auf Ereignisse Set TestObject = Wscript.CreateObject _ des Objektes zu reagieren. ("Word.Application", "Word") Binden an eine bestehende Instanz eines Automatisationsobjekts mit der Möglichkeit auf Ereignisse des
GetObject mit der entsprechenden ProgID und einer Variable für die Ereigniszuordnung. Set TestObject = Wscript.GetObject _ ("Word.Application", "Word") Seite 102 von 394
Auszuführende Aufgabe
Methode
Objektes zu reagieren. Binden an eine neue Instanz CreateObject mit der entsprechenden ProgID und dem eines Automatisationsobjekts auf Namen des Remotecomputers. Set TestObject = CreateObject _ ("Word.Application", "atleinem Remotecomputer. dc-01")
Überprüfen von Objektreferenzen Die Funktion IsObject ermöglicht es Ihnen zu überprüfen, ob eine Referenzierung funktioniert hat. Wenn der Aufruf von GetObject oder CreateObject funktioniert, dann gibt IsObject den Wert True (-1) zurück - andernfalls erhalten Sie den Rückgabewert False (0). Das folgende Codestück verwendet die Methode CreateObject, um eine Objektreferenz auf ein nichtexistentes Objekt zu erstellen (diese wird der Variable TestObject zugewiesen). Da dies fehlschlägt, gibt eine Prüfung der Referenz über die Methode IsObject den Wert 0 zurück. On Error Resume Next Set TestObject = CreateObject("Fake.Object") Wscript.Echo IsObject(TestObject)
Unglücklicherweise geht VBScript davon aus, dass eine einmal erstelle Objektreferenz für die gesamte Lebensdauer des Scripts gültig ist. Dies ist normalerweise kein Problem - speziell bei Objekten wie ADSI und WMI, die wohl kaum während der Scriptausführung verschwinden werden. Leider lässt sich dasselbe nicht über Automatisationsobjekte sagen. Sehen Sie sich zum Beispiel das folgende Script an. Es erstellt eine Instanz von Microsoft Word, beendet die Instanz und prüft dann über IsObject, ob die Objektreferenz noch gültig ist: Set TestObject = CreateObject("Word.Application") TestObject.Quit Wscript.Echo IsObject(TestObject)
Wenn Sie das Script ausführen, behauptet IsObject, dass TestObject noch immer ein aktives Objekt ist - und zwar darum, weil TestObject ja tatsächlich noch eine Objektreferenz ist. Sie verweist nur nicht mehr auf eine Instanz von Microsoft Word, die ausgeführt wird. Es gibt zwei Wege, um solche Probleme zu vermeiden. Ein Ansatz ist es, über WMI zu prüfen, ob der Prozess (in diesem Fall word.exe) noch ausgeführt wird. Diese Methode funktioniert zwar, Sie müssen jedoch permanent eine Abfrage der laufenden Prozesse durchführen - Ihr Script wird so langsamer. Außerdem treten weitere Schwierigkeiten auf, wenn mehrere Instanzen von word.exe ausgeführt werden. Ein besserer Ansatz ist es auf Ereignisse des Objektes zu reagieren. Wenn Word geschlossen wird, dann wird ein Ereignis an Ihr Script gesendet - dieses kann dann entsprechend reagieren. Weitere Informationen über die Verarbeitung von Ereignissen finden Sie im Abschnitt Creating Enterprise Scripts in Teil 3 in diesem Buch.
Objekte aus dem Speicher entfernen In-Process-Server (Automatisationsobjekte, die über .dll-Dateien zur Verfügung stehen) werden beim Beenden des Scripts automatisch aus dem Speicher entfernt. Das liegt daran, dass diese Objekte im selben Prozess wie das Script ausgeführt werden. Das folgende Script erstellt eine Instanz des Objekts FileSystemObject und zeigt ein Nachrichtenfenster an. Sobald
Seite 103 von 394
Sie das Nachrichtenfenster schließen, werden sowohl das Script als auch das Objekt aus dem Speicher entfernt. Set TestObject = CreateObject("Scripting.FileSystemObject") Wscript.Echo "Script beenden."
Für Out-Of-Process-Server gilt dies nicht - diese werden in einem anderen Prozess als das Script ausgeführt. Das folgende Script erstellt eine Instanz von Microsoft Word und zeigt dann ebenfalls ein Nachrichtenfenster an. Wenn Sie das Nachrichtenfenster schließen, dann wird zwar das Script beendet, der Microsoft Word-Prozess (word.exe) jedoch nicht: Set TestObject = CreateObject("Word.Application") Wscript.Echo "Script beenden."
Das liegt daran, dass es keine direkte Verbindung zwischen dem Script-Prozess und dem Word-Prozess gibt. Die Aktionen, die Sie mit dem Script-Prozess durchführen, betreffen den Word-Prozess nicht - andersrum gilt das gleiche. Über den Taskmanager können Sie überprüfen, ob der Prozess noch ausgeführt wird.
Abbildung 2.29: Automatisationsobjekt, dass auch nach der Beendigung des Scripts noch weiter ausgeführt wird Bei Out-Of-Process-Servern müssen Sie normalerweise zum Entfernen aus dem Speicher eine Methode des Objekts verwenden (um diese Methode zu finden, werden Sie meistens in der Dokumentation des Objekts nachschlagen müssen) - bei Microsoft Word handelt es sich bei dieser Methode um Quit. Das folgende Script erstellt eine Instanz von Microsoft Word und entfernt diese sofort wieder aus dem Speicher: Set TestObject = CreateObject("Word.Application") TestObject.Quit Das Schlüsselwort Nothing
Seite 104 von 394
Mit dem Schlüsselwort Nothing können Sie eine Objektreferenz und ein Objekt entfernen. Nachdem eine Objektvariable auf Nothing (Nichts) gesetzt wurde, enthält sie keine Objektreferenz mehr und kann nicht mehr zum Zugriff auf das Objekt verwendet werden. Das folgende Script erstellt eine Instanz von Microsoft Word und weist diese der Objektvariablen TestObject zu. Dann setzt es die Variable auf Nothing und versucht dann über Quit Word aus dem Speicher zu entfernen: Set TestObject = CreateObject("Word.Application") Set TestObject = Nothing TestObject.Quit
Da Sie versuchen auf ein Objekt zuzugreifen, dessen Referenz vorher bereits mit Nothing entfernt wurde, erhalten Sie die Fehlermeldung aus Abbildung 2.30, wenn Sie das Script ausführen.
Abbildung 2.30: Arbeiten mit ungültigen Objektreferenzen Wenn Sie eine Objektvariable auf Nothing setzen, geben Sie zwar etwas Speicher frei, das Objekt selbst wird jedoch nicht aus dem Speicher entfernt - daher gibt es normalerweise keinen Grund Nothing zu verwenden. Der gleiche Vorgang wird auf jeden Fall beim Beenden des Scripts durchgeführt. Zeile 2 im folgenden Script ist also unnötig: Set TestObject = CreateObject("Scripting.FileSystemObject") Set TestObject = Nothing
Scripting-Konzepte und -Technologien zur Systemadministration: Kapitel 3 - Der WSH Veröffentlicht: 26. Apr 2004
(Engl. Originaltitel: WSH Primer) Der Windows Script Host (WSH), ein Feature der Microsoft Windows 2000-Familie, ist eine mächtige Scripting-Umgebung, die ideal zur Automatisierung von Administrationsaufgaben eingesetzt werden kann. Scripte, die in der WSH-Umgebung ausgeführt werden, können die umfangreichen Möglichkeiten der WSH-Objekte und anderer COM-basierter Automatisationsobjekte nutzen - zum Beispiel WMI (Windows Management Instrumentation) und ADSI (Active Directory Service Interfaces) zur Verwaltung der Windows-Subsysteme und zur Zentralisierung von Administrationsaufgaben. Windows® 2000-Familie, ist eine mächtige Scripting-Umgebung, die ideal zur Automatisierung von Administrationsaufgaben eingesetzt werden kann. Scripte, die in der WSH-Umgebung ausgeführt werden, können die umfangreichen Möglichkeiten der WSHObjekte und anderer COM-basierter Automatisationsobjekte nutzen - zum Beispiel WMI Seite 105 von 394
(Windows Management Instrumentation) und ADSI (Active Directory Service Interfaces) zur Verwaltung der Windows-Subsysteme und zur Zentralisierung von Administrationsaufgaben.
WSH-Übersicht Die meisten Leute, die sich zum ersten Mal mit dem Windows Script Host (WSH) beschäftigen, sind leicht irritiert. Was genau ist WSH? Handelt es sich um eine Sprache wie VBScript oder JScript? Nein - auch wenn der WSH das Ausführen von Programmen ermöglicht, die in einer dieser Sprachen geschrieben wurden. Handelt es sich um ein Objektmodell wie WMI oder ADSI? Nein - der WSH stellt zwar ein einfaches Objektmodell zur Verfügung, aber ist nicht seine primäre Aufgabe. Was ist also der WSH? Wie der Name schon sagt, ist der WSH ein Script Host. Ein Script Host ist ein Programm, dass eine Umgebung zur Verfügung stellt, in der ein Benutzer Scripte ausführen kann. Diese Scripte können durchaus in unterschiedlichen Sprachen geschrieben sein. Wahrscheinlich haben Sie bereits andere Script Hosts verwendet. Zum Beispiel Microsoft® Internet Explorer - dieser Script Host führt Scripte aus, die das Dynamic-HTMLObjektmodell verwenden. Oder Shell-Programme (zum Beispiel C Shell, Bourne Shell und Korn Shell) - auch sie ermöglichen es Ihnen Scripte zu schreiben. Auch jede Eingabeaufforderung können Sie sich als Script Host vorstellen. Sie ermöglicht es Ihnen schließlich Script auszuführen, die in der "Batchsprache" geschrieben sind. Der WSH ist ein eher ungewöhnlicher Script Host. Im Gegensatz zu den oben erwähnten Script Hosts ist der WSH nicht auf eine bestimmte Scriptsprache oder auf bestimmte Objektmodelle eingeschränkt. Die Funktionalität des WSH kann man in vier Hauptbereiche unterteilen. Diese Bereiche werden im Rest des Kapitels ausführlich besprochen. • • • •
Die standardmäßige Fähigkeit, Administrationsaufgaben durchzuführen. Die Fähigkeit, COM-Objekte zu verwenden. Die Fähigkeit, Standard-Programmierungsfeatures einer WSH-kompatiblen Scriptsprache zu nutzen. Die Fähigkeit, Kommandozeilentools zu verwenden.
Standardmäßige Durchführung von Administrationsaufgaben Der WSH ist primär eine Laufzeitumgebung für Scripte. Er stellt eine Umgebung zur Verfügung, in der Scripte ausgeführt werden können - ganz ähnlich wie ein Befehlsinterpreter (zum Beispiel cmd.exe oder command.com), über den Batchdateien ausgeführt werden können. Auch wenn der WSH primär als 'Container' zur Verwendung anderer Scriptsprachen oder technologien dient, ist es doch möglich, "reine" WSH-Scripte zu nutzen. Mit dem WSH können Sie zum Beispiel Netzlaufwerke und Drucker verbinden. Das folgende zweizeilige Script ordnet zum Beispiel den Laufwerksbuchstaben X zur Freigabe \\atl-fs-01\public zu: Set objNetwork = Wscript.CreateObject("WScript.Network") objNetwork.MapNetworkDrive "X:", "\\atl-fs-01\public"
Seite 106 von 394
Verwendung von COM-Objekten Wie Sie nun wissen, können Sie den WSH zum Zuordnen von Netzlaufwerken verwenden. Leider gibt es außer dieser Möglichkeit wenig, was Sie noch über den WSH alleine durchführen können. Sie können über den WSH nicht die Hardware eines Computers abfragen oder feststellen, welche Software installiert ist. Auch wenn der WSH selbst keine dieser Möglichkeiten zur Verfügung stellt, können Sie sie trotzdem in einem WSH-Script nutzen. Mit dem WSH können Sie nämlich COM-Objekte (Component Object Model) in Ihren Scripten verwenden. Ein COM-Objekt ist eine Möglichkeit für Anwendungen (.exe) oder Programmbibliotheken (.dll) Ihre Funktionen zur Verfügung zu stellen. Der WSH kann diese Funktionen dann verwenden, indem er diese Objekte in das Script einbindet. Der WSH stellt zum Beispiel keine Methoden zur Verwaltung von Diensten zur Verfügung. WMI bietet jedoch solche Möglichkeiten (WMI ist eigentlich eine Sammlung von COMObjekten). Mit WMI können Sie die Eigenschaften von Diensten abfragen, Dienste anhalten und starten und die Diensteinstellungen konfigurieren. Statt also eigene Methoden zur Verwaltung von Diensten zur Verfügung zu stellen, verwendet der WSH einfach die von WMI. Der WSH kann jedes COM-Objekt nutzen, das eine Automatisation unterstützt (Automatisation ist ein Standardverfahren, um auf COM-Objekte zuzugreifen). Auf den meisten Computern steht eine riesige Menge an Automatisationsobjekten zur Verfügung. Der WSH kann so nahezu jede beliebige Aufgabe ausführen - vom Festlegen von Datenträgerkontingenten bis zur Active Directory-Verwaltung. Das folgende WSH-Script verwendet zum Beispiel ADSI, um ein Benutzerkonto in Active Directory zu erstellen: Set objOU = Wscript.GetObject("LDAP://OU=management,dc=fabrikam,dc=com") Set objUser = objOU.Create("User", "cn=MyerKen") objUser.Put "sAMAccountName", "myerken" objUser.SetInfo
Nutzung von Standard-Features einer WSH-kompatiblen Scriptsprache Der Verwendungszweck von Anwendungen und wie diese Anwendungen verwendet werden ist oft sehr unterschiedlich. Der Windows-Taschenrechner hat zum Beispiel nur wenig Ähnlichkeit mit Ipconfig.exe - beide haben wiederum wenig mit Microsoft® Word gemeinsam. Abgesehen von diesen Unterschieden gibt es jedoch einige grundlegende Eigenschaften, die sich diese Anwendungen teilen. Viele Anwendungen geben etwas aus und nehmen Benutzereingaben entgegen. Viele Anwendungen können Lese- und Schreiboperationen in der Registrierung durchführen. Viele Anwendungen können mit Kommandozeilenparametern gestartet werden - sogar viele grafische Anwendungen. Wenn Microsoft Word zum Beispiel mit dem Schalter /n gestartet wird, dann wird gleich ein leeres Dokument geöffnet: winword.exe /n
Diese Standardfunktionalitäten werden auch für administrative Scripte benötigt. Was würde Ihnen zum Beispiel ein Script nützen, das keine Informationen anzeigen kann? Viele dieser Funktionalitäten stellt Ihnen der WSH zur Verfügung. Er kann Informationen ausgeben und Eingaben entgegennehmen, Lese- und Schreiboperationen in der Registrierung durchführen und Kommandozeilenparameter entgegennehmen. Seite 107 von 394
Stellen wir uns vor, Sie benötigen ein Script, das einen Ordner löscht. Wenn Sie das Script starten, können Sie den Ordnernamen als Parameter angeben: deletefolder.vbs c:\scripts
Das Löschen des Ordners können Sie über das Objekt FileSystemObject durchführen. Das Script selbst schreiben Sie mit VBScript. Aber wie verarbeiten Sie die Kommandozeilenparameter? Werder das FileSystemObject noch VBScript können diese Parameter verarbeiten. Glücklicherweise ist der WSH dazu in der Lage. Das folgende Script verwende drei unterschiedliche Technologien: • • •
In Zeile 1 wird VBScript verwendet, um eine neue Instanz des Objektes FileSystemObject zu erstellen. In Zeile 2 wird der WSH verwendet, um die Kommandozeilenparameter einzulesen und diese einer Variable zuzuweisen. In Zeile 3 wird das Objekt FileSystemObject zum Löschen des angegebenen Ordners verwendet.
Set objFSO = Wscript.CreateObject("Scripting.FileSystemObject") strFolder = Wscript.Arguments.Item(0) objFSO.DeleteFolder(strFolder)
Das gleiche Script können Sie über Jscript, PythonScript oder eine andere WSH-kompatible Scriptsprache implementieren. Es ist völlig egal, ob die gewählte Sprache die Verarbeitung von Kommandozeilenparametern unterstützt, da der WSH ja diese Funktionalität zur Verfügung stellt.
Verwendung von Kommandozeilentools Sie brauchen den WSH nicht wirklich, um Kommandozeilentools auszuführen. Sie können ganz einfach in einer Eingabeaufforderung oder über eine Batchdatei gestartet werden. Der WSH ist jedoch dann ziemlich nützlich, wenn Sie die Tools oder Batchdateien mit etwas "Intelligenz" ausstatten möchten. Stellen Sie sich zum Beispiel vor, Sie möchten eine Netzwerkfreigabe nur dann zuordnen, wenn der Benutzer sich in einer bestimmten Domäne (zum Beispiel "fabricam") anmeldet - andernfalls soll nichts passieren. Ist das über eine Batchdatei möglich? Ja, ist es - es ist jedoch ziemlich kompliziert. In einem Script können Sie diese Aufgabe im Gegensatz dazu über die folgenden sechs Zeilen erledigen: Set objNetwork = Wscript.CreateObject("Wscript.Network") Set objShell = WScript.CreateObject("WScript.Shell") strDomain = objNetwork.DomainName If strDomain = "fabrikam" Then objShell.Run "net use x: \\atl-fs-01" End If
WSH vs. Cmd.exe An dieser Stelle könnte es ganz nützlich sein, den WSH und Cmd.exe (der Kommandozeileninterpreter von Windows) zu vergleichen. Beide sind ScriptingUmgebungen: Der WSH ermöglicht das Ausführen von WSH-Scripten und Cmd.exe ermöglicht das Ausführen von Batchdateien (manchmal auch Shellscripte genannt). Sowohl WSH als auch Cmd.exe benötigen eine Scriptsprache und entsprechende Werkzeuge. Es ist Seite 108 von 394
schwierig, Scripte nur mit dem WSH zu schreiben und es ist genauso schwierig, Batchdateien nur mit der Shellsprache zu schreiben. Hier treten nun die Unterschiede beider Umgebung zu tage. Während der WSH Ihnen den Zugriff auf eine große Zahl an ausgereiften Scriptsprachen ermöglicht, sind Sie mit Cmd.exe auf die einfache Shellsprache eingeschränkt. Die einzigen Tools, die Sie mit Cmd.exe verwenden können, sind Kommandozeilentools. Der WSH bietet Ihnen nicht nur einen Zugriff auf dieselben Tools, sondern Sie sind zusätzlich in der Lage, Automatisationsobjekte zu verwenden. Soll das heißen, Sie sollen alle Kommandozeilentools und Batchdateien wegwerfen und alles nur noch über WSH-Script durchführen? Natürlich nicht - wenn Sie bereits über eine funktionierende Lösung verfügen, dann gibt es keinen Grund, diese nicht weiter zu verwenden. Der WSH ist jedoch eine hervorragende Lösung für die Probleme, die Sie mit Batchdateien und Kommandozeilentools nicht lösen können.
Ein Hinweise zu WSH-Versionen Die Beispielscripte in diesem Kapitel wurden mit der WSH-Version 5.6 entwickelt und getestet. Einige der beschriebenen Funktionalitäten stehen auch nur unter Version 5.6 oder höher zu Verfügung. Daher sollten Sie prüfen, welche WSH-Version bei Ihnen installiert ist und diese im Zweifelsfall auf Version 5.6 aktualisieren. Anmerkung: Aufgrund der nützlichen zusätzlichen Funktionalitäten sollten Sie den WSH auf jeden Fall auf Version 5.6 aktualisieren - egal, ob Sie die Scripte in diesem Kapitel ausführen möchten oder nicht. Um die installierte WSH-Version abzufragen, geben Sie in einer Eingabeaufforderung einfach cscript ein und drücken die Eingabetaste. Wenn Sie Version 5.6 installiert haben, dann sollten Sie die folgende Ausgabe sehen: C:\WINDOWS>cscript Microsoft (R) Windows Script Host, Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. Alle Rechte vorbehalten. Die gleichen Informationen können Sie auch mit Hilfe eines Scripts abrufen: Wscript.Echo Wscript.Version
Die gleichen Informationen können Sie auch mit Hilfe eines Scripts abrufen: Wscript.Echo Wscript.Version
Weitere Informationen zur WSH-Version 5.6 finden sie unter http://msdn.microsoft.com/library/default.asp?url=/downloads/list/webdev.asp (englischsprachig). Dort können Sie auch die aktuelle Version des WSH herunterladen.
Die WSH-Architektur Wenn Sie Auto fahren lernen, müssen Sie nicht erst eine Ausbildung als Kfz-Mechaniker machen. Wenn Sie zwischen Gas- und Bremspedal unterscheiden können und verstehen, wie das Lenkrad funktioniert, dann sind Sie wahrscheinlich auch in der Lage von A nach B zu fahren. Wenn wir davon ausgehen, dass Sie nie wieder mit einem Auto fahren, nachdem Sie an Punkt B angekommen sind, reicht dieses Wissen sicher auch aus. Was ist jedoch, wenn Sie regelmäßig fahren möchten? In diesem Fall könnte es recht nützlich sein, wenn Sie etwas Seite 109 von 394
darüber wissen, wie ein Auto funktioniert - und warum es möglicherweise nicht funktioniert. Sie sollten wissen, dass ein Auto Benzin benötigt, dass in die Reifen Luft gehört und dass Batterien leer werden können. Wenn Sie diese grundlegenden Informationen nicht kennen, dann werden Sie möglicherweise einige unangenehme Überraschungen erleben. Das gleiche gilt für Scripting. Wenn Sie nur ein Script benötigen, um den Warndienst auf dem lokalen Computer anzuhalten, dann benötigen Sie dieses Buch nicht. Stattdessen können Sie sich folgendes Script kopieren: strComputer = "." Set objWMIService = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colServices = objWMIService.ExecQuery _ ("SELECT * FROM Win32_Service WHERE Name = 'Alerter'") For Each objService in colServices errReturnCode = objService.StopService() Next
Was passiert aber, wenn Sie einen anderen Dienst anhalten möchten? Oder wenn Sie einen Dienst auf einem Remotecomputer anhalten möchten? Was, wenn Sie den Warndienst starten möchten? Wenn Sie bestehende Scripte verändern oder eigene Scripte entwickeln möchten, dann müssen Sie auch wissen, wie diese Scripte arbeiten - und für dieses Verständnis sind Grundkenntnisse der WSH-Architektur unerlässlich.
Komponenten der WSH-Umgebung Die Architektur des WSH ist modular aufgebaut. Sie setzt sich aus Einzelteilen zusammen, die jeweils für bestimmte Funktionalitäten verantwortlich sind. Diese Modularität führt zu zwei Vorteilen: Der WSH unterstützt verschiedene Scriptsprachen und kann COM-Objekte verwenden. In Abbildung 3.1 sehen Sie die Hauptkomponenten der WSH-Umgebung und wie diese miteinander in Verbindung stehen (Scriptdateien, Script Host, Scripting Language Engines und COM-Objekte). Die Komponenten im dunklen Kasten werden mit der Installation von WSH 5.6 eingerichtet. Im Rest dieses Kapitels werden die einzelnen Komponenten genauer besprochen.
Abbildung 3.1: Hauptkomponenten der WSH-Umgebung
Seite 110 von 394
Scriptdateien WSH-Scriptdateien (meist einfach als Script bezeichnet) sind einfache Textdateien mit Scriptbefehlen ("Textdatei" bedeutet in diesem Fall, dass keine Zeichen mit speziellen Formatierungen verwendet werden). Das folgende Script wird zum Beispiel fehlschlagen, da es typografische Anführungszeichen enthält (das Anführungszeichen ist unten statt oben): Wscript.Echo "This line is correct." Wscript.Echo „This line is incorrect."
Da viele Textverarbeitungsprogramme standardmäßig solche Zeichen verwenden, stellen sie nicht unbedingt die beste Script-Entwicklungsumgebung dar. Texteditoren werden hingegen für die Arbeit mit normalen Textdateien entwickelt (zum Beispiel Notepad.exe) - daher sollten Sie Ihre Scripte mit einem Texteditor schreiben. Anmerkung: Sie sollten es auch vermeiden, Scripte mit einer Textverarbeitung zu erstellen und diese dann in einen Texteditor zu kopieren. Es gibt keine Garantie dafür, dass bei diesem Kopiervorgang tatsächlich nur unformatierter Text eingefügt wird. Probleme durch formatierten Text sind oft schwer zu finden, da ein Zeichen möglicherweise nur unformatiert aussieht. Das Script kann in jeder Scriptsprache geschrieben werden, für die eine entsprechende Scripting Language Engine installiert ist. Sie sollten die Datei mit der Dateierweiterung speichern, die der Scriptsprache zugeordnet ist. In Tabelle 3.1 sehen Sie drei Beispiele zu Scriptsprachen und deren Dateierweiterungen. Tabelle 3.1: Dateierweiterungen von Scripten DateierweiterungBeispiel-DateinameScriptsprache
.VBS
EnumPrinters.vbs VBScript
.JS
EnumProcesses.js JScript
.PLS
EnumDisks.pls
PerlScript
Wenn Sie ein Script mit VBScript schreiben, dann sollten Sie das Script also mit der Dateierweiterung .vbs speichern. Anmerkung: Es auch dann möglich ein Script auszuführen, wenn Sie nicht die StandardDateierweiterung verwendet haben. Weitere Informationen hierzu erhalten Sie später in diesem Kapitel. Nachdem Sie das Script in Notepad eingegeben und gespeichert haben, können Sie es ausführen. Ein Vorteil von Scripting ist hierbei: Sie müssen das Script nicht erst kompilieren oder weitere Dateien erstellen. Stattdessen starten Sie es im einfachsten Fall mit einem Doppelklick. Geben Sie zum Beispiel die beiden folgenden Zeilen in Notepad ein: Set objNetwork = Wscript.CreateObject("Wscript.Network") Wscript.Echo objNetwork.ComputerName
Speichern Sie die Datei mit der Dateiendung .vbs, und Sie haben ein Script erstellt, das Ihnen den Namen des lokalen Computers zurückgibt.
Seite 111 von 394
Script Hosts Der Script Host initiiert und koordiniert die Ausführung Ihres Scripts. Er liest die Scriptdatei ein und interagiert mit den Komponenten der WSH-Umgebung und den COM-Objekten. Außerdem stellt er fest, welche Scriptsprache er bei der Ausführung des Scripts verwenden muss. Wenn das Script zum Beispiel die Dateierweiterung .vbs verwendet, dann lädt der Script Host die VBScript Language Engine, und arbeitet dann mit dieser den Scriptcode ab. Die WSH-Umgebung stellt zwei Script Hosts zur Verfügung: das konsolenbasierte CScript und das GUI-basierte WScript. Diese bieten beide eine nahezu identische Funktionalität. In den meisten Fällen ist es vollkommen egal welcher Script Hosts Ihr Script ausführt. Die zwei einzigen Unterschiede zwischen den Script Hosts betreffen die Interaktion mit dem Script - also den Weg, wie Sie Informationen an das Script übergeben (Eingaben) oder von diesem erhalten (Ausgaben). Die Ein- und Ausgaben von CScript erfolgen über die Eingabeaufforderung. Im Gegensatz dazu werden bei WScript die Ein- und Ausgaben über Nachrichtenfenster durchgeführt. Ansonsten sind die beiden Script Hosts identisch. Wenn Sie ein Script schreiben, dass keine Interaktion mit dem Benutzer erfordert, dann können Sie es sowohl mit CScript als auch mit WScript ausführen. Das folgende Script ordnet zum Beispiel eine Netzwerkfreigabe einem Laufwerksbuchstaben zu. Es wird unter beiden Script Hosts exakt gleich ausgeführt: Set objNetwork = Wscript.CreateObject("WScript.Network") objNetwork.MapNetworkDrive "g:", "\\atl-fs-01\Sales"
Ein Script, das Informationen ausgibt, wird hingegen unter CScript ganz anders ausgeführt (die Ausgaben werden zeilenweise in der Eingabeaufforderung vorgenommen), als unter WScript (die Ausgaben werden als Nachrichtenfenster angezeigt). Weitere Informationen zum Ausführen von Scripts mit CScript und WScript finden Sie weiter unten im Abschnitt WSHScripte ausführen in diesem Kapitel.
Scripting Language Engines Obwohl der Script Host für die Initiierung und Koordinierung der Scriptausführung verantwortlich ist, kann er trotzdem keine Scriptsprache interpretieren. Diese Aufgabe wurde in der WSH-Umgebung in eine andere Komponente ausgelagert. Durch diese Teilung ist der WSH in der Lage mit mehreren Scriptsprachen zu arbeiten. Er beherrscht selbst keine einzige Scriptsprache, sondern gibt die Interpretation des Scripts an die entsprechende Language Engine weiter. Sie können VBScript nur darum verwenden, weil die VBScript Language Engine bereits installiert ist. Wenn eine neue Scriptsprache installiert wird, dann wird in der Registrierung mindestens eine neue Zuordnung eingerichtet. Diese verbindet eine Dateierweiterung mit der DLL (Dynamic Link Library), über die die Scriptsprache implementiert wird. Der Scripthost erkennt die in einem Script verwendete Scriptsprache normalerweise an der Dateierweiterung des Scripts. Dann prüft er in der Registrierung, welche Scripting Language Engine (als DLL) dieser Erweiterung zugeordnet ist, und verwendet die Engine. Anmerkung: Sie können den Script Host dazu zwingen eine bestimmte Scripting Language Engine zu verwenden, indem Sie den Kommandozeilenschalter //E: verwenden (siehe Tabelle 3.2). Auf diese Art können Sie jede beliebige Dateierweiterung für Ihre Scriptdateien verwenden. Seite 112 von 394
COM-Objekte Der WSH stellt das Objekt WScript und drei COM-basierte Objekte zur Verfügung: WshShell, WshNetwork und WshController. Auch wenn diese standardmäßig bereits in der WSHUmgebung vorhanden sind, handelt es sich doch um COM-Objekte. Sie werden also auch genauso wie COM-Objekte verwendet. Die WSH-COM-Objekte bieten natürlich nicht die gleichen administrativen Möglichkeiten wie WMI oder ADSI. Trotzdem sind sie in einigen Situationen sehr nützlich: •
•
•
Wenn Sie Aufgaben ausführen müssen, die nicht über ein anderes Automatisationsobjekt durchgeführt werden können. Das Objekt WshNetwork erlaubt es Ihnen beispielsweise Netzlaufwerke zu Laufwerksbuchstaben zuzuordnen - dies ist mit WMI oder ADSI nicht möglich. Wenn Sie mit Clients arbeiten müssen, die WMI oder ADSI nicht zur Verfügung stellen. Mit ADSI können Sie zum Beispiel den Namen des lokalen Computers abfragen - es sei denn, es handelt sich um einen Computer unter Windows 98. In diesem Fall müssen Sie das Objekt WshNetwork verwenden. Wenn sie ein Script auf einem Remotecomputer ausführen müssen. Weder WMI noch ADSI sind hierzu in der Lage. Mit dem Objekt WshController können Sie jedoch auch diese Aufgabe bewältigen.
Zusammenarbeit der einzelnen Komponenten der WSHUmgebung Die Komponenten der WSH-Umgebung müssen mit den anderen Komponenten zusammenarbeiten. Diese Zusammenarbeit sieht folgendermaßen aus: Script Host und Scriptdatei: Wenn Script Host für die Ausführung eines Scripts aufgerufen wird, prüft er die Dateierweiterung der Scriptdatei. Dann sucht der Script Host in der Registrierung nach der Scripting Language Engine, die der Erweiterung zugeordnet ist. Dies passiert alles, bevor eine einzige Zeile Scriptcode ausgeführt wird. Script Host und Scripting Language Engine: Nachdem die Scriptsprache festgestellt wurde, arbeitet der Script Host mit der entsprechenden Scripting Language Engine zusammen, um die Befehle im Script zu interpretieren. Der Script Host kommuniziert mit der Scripting Language Engine über die Windows-Script-Schnittstellen. Bevor die erste Scriptzeile ausgeführt wird, wird das gesamte Script eingelesen und auf syntaktische Fehler geprüft. Das folgende Script hat zum Beispiel eine fehlerhafte Anweisung in Zeile 3. Der IfThen-Befehl ist falsch: Wscript.Echo "Line 1." Wscript.Echo "Line 2." If x = 1 Wscript.Echo "X is equal to 1." End If
Vielleicht erwarten Sie, dass Zeile 1 und 2 ausgeführt werden, bevor der Fehler in Zeile 3 auftritt. Dies ist jedoch nicht so. Der Fehler wird bereits bei der Syntaxprüfung vor der Ausführung festgestellt. Daher wird mit der Scriptausführung gar nicht begonnen.
Seite 113 von 394
WScript-Bibliothek und COM-Objekte: Wenn im Script ein COM-Objekt verwendet wird, dann führt die WScript-Bibliothek die Interaktion mit diesem COM-Objekt durch. Anmerkung: Viele Scriptsprachen bieten die Möglichkeit direkt mit COM-Objekten zu interagieren. In diesem Fall ist WScript nicht an der Zusammenarbeit beteiligt. Da es in diesem Kapitel jedoch um den WSH geht, werden die Methoden CreateObject und GetObject von WScript verwendet. VBScript bietet Ihnen jedoch eine wesentlich einfachere Möglichkeit mit COM-Objekten zu arbeiten. Daher werden in fast allen anderen Scripten dieses Buches die VBScript-Funktionen verwendet. Eine genauere Betrachtung der beiden Möglichkeiten finden Sie im Kapitel VBScript Primer dieses Buches.
Das WSH-Objektmodell Meist wird von WSH als einzelnes Objekt gesprochen. In Wahrheit setzt sich der WSH jedoch aus mehreren Komponenten zusammen - zum Beispiel aus dem standardmäßig verfügbaren Objekt WScript und den drei COM-Objekten WshShell, WshNetwork und WshController. Diese Objekte werden zusammen als WSH-Objekte bezeichnet. In Abbildung 3.2 sehen Sie das WSH-Objektmodell. Die einzelnen Elemente des Diagrams werden in diesem Kapitel besprochen.
Seite 114 von 394
Abbildung 3.2: Diagram des WSH-Objektmodells
WSH-Script ausführen Wenn Sie mehrere Leute fragen wie sie Word starten, dann werden Sie wahrscheinlich einige unterschiedliche Antworten erhalten - zum Beispiel indem Sie im Startmenü auf Word klicken, indem Sie in einer Eingabeaufforderung winword.exe eingeben oder über eine Tastenkombination. Egal welche Methode Sie auch verwenden, dass Ergebnis ist immer das gleiche: Word wird gestartet. Auch für das Ausführen von WSH-Scripten gibt es mehrere Möglichkeiten. Administrative Scripte können zum Beispiel als Anmeldescript oder über einen geplanten Task ausgeführt werden. Andere Scripte können über eine Eingabeaufforderung oder über einen Doppelklick gestartet werden. In vielen Fällen macht es keinen Unterschied wie ein Script gestartet wird in anderen Fällen kann es jedoch ein sehr großer Unterschied sein. Wenn Sie die Beispiele in diesem Kapitel nachvollziehen, dann wird empfohlen die Scripte in einer Kommandozeile über CScript auszuführen. Dies geschieht aus zwei Gründen: Erstens können Scripte unter CScript externe Programme starten und deren Ausgaben weiterverarbeiten, und zweitens werden die Ausgaben im der Eingabeaufforderung statt in Seite 115 von 394
einem Nachrichtenfenster ausgegeben. Dies ist besonders bei Scripten wichtig, die Hunderte von Informationen ausgeben. In den nächsten Abschnitten dieses Kapitels wird das Ausführen von Scripten detaillierter besprochen.
Scripte über die Kommandozeile ausführen Auch wenn wir uns im Zeitalter der grafischen Benutzerschnittstellen befinden, arbeiten viele Administratoren oft mit einer Eingabeaufforderung statt mit der grafischen Benutzerschnittstelle. Mit dem WSH können Sie Script sowohl in der Eingabeaufforderung als auch über die GUI starten. In der Kommandozeile stehen Ihnen die gleichen Möglichkeiten wie über die GUI zur Verfügung - zusätzlich bietet Ihnen CScript jedoch zwei Vorteile: •
•
Es ist einfach, Argumente an das Script zu übergeben. Diese Argumente können im Script selbst verwendet werden (Sie können zum Beispiel den Ordner angeben, der vom Script gelöscht werden soll), oder der Script Host kann sie zur Ausführung des Scripts verwenden. Mit CScript ist es einfach die Scriptausführung abzubrechen - Sie schließen einfach das Kommandozeilenfenster oder drücken STRG+C. Wenn ein Script unter WScript ausgeführt wird, dann können Sie nur den Prozess Wscript.exe über den Taskmanager beenden.
Es gibt zwei Arten, über die Sie in einer Kommandozeile ein Script starten können: • •
Sie geben den Namen des Scripts inklusive der Dateierweiterung an: HardwareAudit.vbs Sie geben den Namen eines der beiden Script Hosts gefolgt vom Namen des Scripts an: cscript HardwareAudit.vbs wscript HardwareAudit.vbs
Bei der ersten Methode entscheidet der Kommandozeileninterpreter, welcher Script Host verwendet wird. In diesem Fall wird das Script unter dem auf dem Computer konfigurierten Standard-Script Host ausgeführt. Bei der zweiten Methode entscheiden Sie selbst, unter welchem Script Host das Script ausgeführt wird.
Script Host-Optionen Sowohl CScript als auch WScript akzeptieren einige Parameter. Diese legen fest, wie ein Script ausgeführt wird oder ändern die Konfiguration der WSH-Umgebung. Einige Optionen werden von beiden Script Host verwendet - CScript verfügt jedoch auch über ein paar Optionen, die von WScript nicht verwendet werden (zum Beispiel //logo und //nologo). Wenn der WSH installiert wird, dann ist WScript der Standardhost. Er wird verwendet, wenn Sie ein Script nicht explizit über CScript oder WScript starten. Um den Standardhost auf CScript zu konfigurieren, verwenden Sie folgenden Befehl: cscript //H:cscript
Um den Standardhost wieder auf WScript zu setzen, verwenden Sie diesen Befehl: wscript //H:wscript
Tabelle 3.2 zeigt die am häufigsten verwendeten WSH-Parameter. Tabelle 3.2: Script Host-Parameter Parameter Beschreibung Seite 116 von 394
Parameter Beschreibung
//B
Batch-Modus; unterdrückt alle Benutzereingaben und Scriptfehler. Wenn ein Script zum Beispiel eine Ausgabe über Wscript.Echo durchführt, dann wird diese im Batch-Modus nicht angezeigt. Auch VBScript-Funktionen wie Msgbox werden unterdrückt. Der Standardmodus ist der interaktive Modus.
//D
Aktiviert den Microsoft Script Debugger, wenn dieser installiert ist. Der Script Debugger wird zusammen mit Windows 2000 zur Verfügung gestellt - er wird standardmäßig jedoch nicht installiert. Windows XP stellt keinen Script Debugger zur Verfügung. Wenn der Script Debugger nicht installiert ist, tritt kein Fehler auf stattdessen wird das Script einfach weiter ausgeführt.
//E:engine Führt das Script über eine bestimmte Script Engine aus. Neben anderen Dingen haben Sie mit diesem Schalter die Möglichkeit, beliebige Dateierweiterungen für Ihre Scripte zu verwenden. Ohne den Parameter //E können Sie Script nur dann starten, wenn diese eine der registrierten Dateierweiterungen verwenden. Wenn Sie zum Beispiel versuchen den folgenden Befehl auszuführen, erhalten Sie eine Fehlermeldung: cscript test.admin Die Fehlermeldung sieht so aus: Eingabefehler: Für die Dateierweiterung '.admin' gibt es kein Skriptmodul. Wenn Sie das Script jedoch mit dem Parameter //E starten, wird es korrekt ausgeführt: cscript //E:vbscript test.admin Wenn Sie nicht die standardmäßigen Dateierweiterungen verwenden, dann schützen Sie sich dagegen, ein Script versehentlich durch einen Doppelklick zu starten. Sie müssen jedoch in diesem Fall bei jeder Ausführung des Scripts den Schalter //E verwenden. //H:CScript Konfiguriert Cscript.exe oder Wscript.exe als Standard-Script Host. oder //H:WScript //I
Interaktiver Modus; alle Ausgaben werden wie im Script vorgesehen angezeigt. Dies ist der Standardmodus - die Alternative wäre der Batch-Modus.
//logo
Zeigt ein Logo an, wenn das Script unter CScript ausgeführt wird (das Standardverhalten des WSH). Dieses Logo wird vor der Ausführung des Scripts angezeigt und sieht folgendermaßen aus: Microsoft (R) Windows Script Host, Version 5.6 Seite 117 von 394
Parameter Beschreibung
Copyright (C) Microsoft Corporation 1996-2001. Alle Rechte vorbehalten. //nologo
Verhindert die Anzeige des Logos. Die Option //nologo wird oft verwendet, wenn die Ausgaben des Scripts in eine Textdatei umgeleitet werden.
//S
Legt die Optionen Timeout und Logo dauerhaft für den aktuellen Benutzer fest. Der folgende Befehl stellt zum Beispiel sicher, dass das Logo unter CScript garnicht mehr angezeigt wird: cscript //nologo //S Diese Einstellung können Sie auch ändern, indem Sie mit der rechten Maustaste auf die Scriptdatei klicken und dann zur Registerkarte Eigenschaften wechseln.
//T:nn
Legt die maximale Anzahl an Sekunden fest, die ein Script ausgeführt wird (die Standardeinstellung ist ohne Einschränkung).
//X
Startet das Programm im Microsoft Script Debugger. Wenn der Script Debugger nicht installiert ist, dann wird das Script ganz normal ausgeführt.
//?
Zeigt eine Beschreibung der Kommandozeilenparameter an. Hierbei handelt es sich um die Informationen, die Sie in dieser Tabelle sehen.
Die Scriptausgaben in eine Textdatei umleiten Stellen Sie sich vor, Sie möchten ein Script ausführen, und die von diesem Script erhaltenen Informationen erst später analysieren. Zum Beispiel, wenn Sie ein Script verwenden, das alle auf einem Domänencontroller installierten Programm ausgibt, und Sie diese Liste erst benötigen, wenn Sie den Domänencontroller wiederherstellen müssen. In einem solchen Fall wäre es sehr praktisch, wenn Sie die Scriptausgaben in einer Datenbank oder einer Textdatei speichern könnten - beides ist möglich. Wenn Ihr Script die Ausgaben immer irgendwo speichern soll, dann werden Sie wahrscheinlich eine entsprechende Funktionalität in dieses Script einbauen. Was aber, wenn die Scriptausgaben nur einmalig gespeichert werden sollen? Wenn Ihr Script die Ausgaben über WScript.Echo durchführt, dann können Sie ein einem solchen Fall die Scriptausgaben in eine Textdatei umleiten. Die Ausgaben werden bei diesem Verfahren einfach in der Textdatei gespeichert und nicht auf dem Bildschirm angezeigt. Der Kommandozeileninterpreter bietet Ihnen hierzu zwei Wege an. Mit dem Zeichen gefolgt von einem Dateinamen werden die Ausgaben in einer Textdatei gespeichert. Alles, was sich in dieser Datei bereits befindet, wird überschrieben. Der folgende Kommandozeilenbefehl speichert zum Beispiel die Ausgaben des Scripts in der Textdatei c:\scripts\services.txt: cscript service_info.vbs > c:\scripts\services.txt
Seite 118 von 394
Mit dem Zeichen können Sie die Daten an die entsprechende Textdatei anhängen. In diesem Fall werden die vorhandenen Inhalte der Textdatei nicht überschrieben: cscript service_info.vbs >> c:\scripts\services.txt
Außerdem sollten Sie den Parameter //nologo verwenden, um zu verhindern dass das Logo in der Textdatei gespeichert wird: cscript //nologo service_info.vbs > c:\scripts\services.txt
Wenn Sie die Ausgaben eines Scripts in eine Textdatei umleiten, werden keine Informationen auf dem Bildschirm angezeigt. Stattdessen gehen alle Ausgaben (auch Fehlermeldung) in die Textdatei.
Ausführung von Scripten planen Mit dem Windows 2000 Taskplaner oder dem Kommandozeilentool At.exe können Sie die Ausführung von Scripten vorausplanen. Die Scripte werden dann ohne Ihr Zutun ausgeführt. Nehmen wir zum Beispiel einmal an, Sie verwenden ein Script, dass einmal in der Woche ausgeführt wird und die Ereignisprotokolle auf allen Domänencontrollern sichert und leert. Wenn sie dieses Script als Task planen, müssen Sie nicht mehr selbst daran denken das Script regelmäßig auszuführen. Geplante Tasks können Sie auch über WMI erstellen, löschen und verwalten (weitere Informationen hierzu finden Sie im Kapitel Creating Enterprise Scripts dieses Buches). Das folgende Script erstellt zum Beispiel einen Task, der das Script monitor.vbs jeden Montag, Mittwoch und Freitag um 12:30 Uhr ausführt: Set Service = GetObject("winmgmts:") Set objNewJob = Service.Get("Win32_ScheduledJob") errJobCreated = objNewJob.Create _ ("cscript c:\scripts\monitor.vbs", "********123000.000000-420", _ True , 1 OR 4 OR 16, , , JobID) Wscript.Echo errJobCreated
Andere Verfahren zum Starten eines Scripts In den meisten Fällen wird ein Systemadministrator ein Script wohl über die Kommandozeile oder über einen geplanten Task starten. Es gibt jedoch noch einige weitere Möglichkeiten: •
Scripte auf Remotecomputern starten: Das Objekt WshController ermöglicht es Ihnen WSH-basierte Scripte auf Remotecomputern auszuführen. Weitere Informationen hierzu finden Sie im Abschnitt WSHController Object weiter unten in diesem Kapitel.
•
Scripte über den Windows Explorer starten: Natürlich können Sie ein WSH-Script auch über einen Doppelklick im Windows Explorer starten. Es wird dann mit dem Standardhost ausgeführt. Wenn es sich beim Standardhost um CScript handelt, dann wird ein Kommandozeilenfenster geöffnet. Dieses wird wieder geschlossen, sobald das Script beendet ist.
•
Scripte über das Kontextmenü starten: Sie können ein WSH-Script starten, indem Sie mit der rechten Maustaste auf die Scriptdatei klicken, und die entsprechende Option auswählen.
•
Scripte über Drag and Drop starten: Sie können ein Script starten, indem Sie es auf eine oder mehrere Dateien oder Ordner ziehen. Hierbei wird dem Script Host automatisch der vollständige Pfad der Datei oder des Ordners übergeben, auf den Sie das Script gezogen haben. Seite 119 von 394
Das folgende Script zeigt zum Beispiel den Pfadnamen jedes Ordners oder jeder Datei an, auf den es gezogen wird: For Each strArgument in Wscript.Arguments Wscript.Echo strArgument Next
Diese Verfahren können Sie sehr gut verwenden, wenn Sie mit Scripten arbeiten, die Dateioder Ordnernamen als Parameter erwarten. Stellen Sie sich zum Beispiel vor, Sie ziehen mehrere Dateien auf ein Script, und das Script kopiert diese automatisch auf einen Remoteserver. Die Länge aller Parameter eines Scripts ist jedoch durch den Kommandozeileninterpreter auf 2048 Zeichen begrenzt.
WSH-Objekte In der WSH-Umgebung stehen Ihnen standardmäßig das Objekt WScript und drei COMObjekte zur Verfügung (WshShell, WshNetwork und WshController). Einige der Aufgaben, die Sie mit diesen Objekten durchführen können, lassen sich jedoch leichter mit anderen Technologien erledigen - zum Beispiel mit WMI und ADSI. Das Objekt WshShell stellt Ihnen zum Beispiel eine Methode mit dem Namen RegRead zur Verfügung. Über diese Methode können Sie einen Registrierungseintrag auslesen. Sie funktioniert sehr gut, wenn Sie den Registrierungsschlüssel, den das Script auslesen soll, vorher schon kennen - zum Beispiel wenn Sie feststellen möchten, welches Hintergrundbild ein Benutzer verwendet. Das folgende WSH-Script führt diese Aufgabe problemlos aus: Set WshShell = WScript.CreateObject("WScript.Shell") strWallpaper = WshShell.RegRead "HKCU\Control Panel\Desktop\Wallpaper" Wscript.Echo strWallpaper
Was aber, wenn es um eine etwas weniger genau definierte Aufgabe geht? Zum Beispiel um das Auflisten aller Einträge unter einem bestimmten Schlüssel? In einem solchen Fall nützt Ihnen WSH ziemlich wenig. Stattdessen sollten Sie den WMI-Anbieter StdRegistry verwenden - er listet alle Einträge unter einem bestimmten Schlüssel auf. Eine solche WMIAbfrage ist in diesem Fall deutlich einfacher umzusetzen. Außerdem funktioniert WMI in den meisten Fällen auf Remotecomputern genauso wie auf dem lokalen Computer. Der WSH arbeitet hingegen nur auf den lokalen Computern. Um WSH-Scripte auf Remotecomputern auszuführen, müssen Sie zwei Scripte erstellen: das Script, das ausgeführt werden soll, und ein Script, das dieses Script auf dem Remotecomputer startet. Trotzdem gibt es einige Fälle, in denen Sie möglicherweise dennoch auf die WSH-Objekte statt auf WMI zurückgreifen. Reine WSH-Scripte werden zum Beispiel auch von Computern unter Windows NT 4.0 und Windows 98 unterstützt - dies ist bei WMI und ADSI nicht der Fall.
Das Objekt WScript Das Objekt WScript bietet Ihnen eine große Menge von Funktionalitäten. Egal welche Scriptsprache Sie verwenden, einige Schlüsselaufgaben (zum Beispiel das Einbinden von
Seite 120 von 394
COM-Objekten und das Entgegennehmen von Kommandozeilenargumenten) können Sie so immer ausführen. Abhängig von der verwendeten Scriptsprache kann es sein, dass Sie das WScript-Objekt nicht benötigen. VBScript stellt zum Beispiel ebenfalls eine Funktion GetObject zur Verfügung, über die Sie COM-Objekte einbinden können. Da die Syntax dieser Funktion viel einfacher als ihr WScript-Equivalent ist, werden Sie diese wahrscheinlich viel häufiger verwenden. Andererseits gibt es in VBScript keine Funktionen zur Verarbeitung von Kommandozeilenargumenten. Es hindert Sie jedoch nichts daran, die entsprechenden WScript-Funktionen unter VBScript zu verwenden. Die Möglichkeit, die Methoden und Funktionen der Scriptsprache und von WScript gemischt zu verwenden, ist einer der Hauptvorteile der WSH-Umgebung. In Abbildung 3.3 sehen Sie die Eigenschaften und Methoden des WScript-Objekts und der Collections WshArguments, WshUnnamed und WshNamed.
Abbildung 3.3: Das Objektmodell von WScript Auf das Objekt WScript zugreifen Das Objekt WScript steht in allen WSH-Scripten zur Verfügung, ohne dass Sie es einbinden müssen. Die folgende Codezeile können Sie überall in Ihrem Script verwenden, ohne die Methode CreateObject aufrufen zu müssen: Seite 121 von 394
Wscript.Echo "Keine Bindung erforderlich."
Das Objekt steht deshalb immer zur Verfügung, weil die Methoden CreateObject und GetObject über dieses Objekt zur Verfügung gestellt werden. Wenn es nicht ständig zur Verfügung stände, dann könnten Sie ohne die beiden Methoden keine weiteren COM-Objekte einbinden. Funktionen von WScript Das WScript-Objekt soll Ihnen hauptsächlich einige grundlegende Funktionalitäten zur Verfügung stellen - diese Funktionalitäten sehen Sie in Tabelle 3.3. Die einzelnen Methoden und Eigenschaften werden in den folgenden Abschnitten dieses Kapitels genauer beschrieben. Tabelle 3.3: Funktionen, die Ihnen über das Objekt WScript zur Verfügung stehen Kategorie
Methoden oder Eigenschaften
Verwenden von COM-Objekten
CreateObject, GetObject
Eingaben und Ausgaben
Echo, StdOut, StdIn, StdErr
Arbeiten mit KommandozeilenargumentenArguments Scriptausführung steuern
Quit, Sleep, Timeout, Interactive
WSH-Umgebungsinformationen abfragen Application, BuildVersion, FullName, Name, Path, ScriptFullName, ScriptName, Version Ereignisse verarbeiten
CreateObject, GetObject, ConnectObject, DisconnectObject
COM-Objekte verwenden Wenn Sie eine Information benötigen, dann können Sie diese zum Beispiel in einer öffentlichen Bibliothek finden. Sie werden jedoch wahrscheinlich nicht in die Bibliothek gehen, ein zufälliges Buch auswählen, und in diesem Buch die gewünschte Information erwarten. Stattdessen müssen Sie das Buch finden, dass Ihnen diese Information zur Verfügung stellt. Genauso funktionieren auch COM-Objekte. Es gibt wahrscheinlich für fast alle administrativen Aufgaben ein passendes COM-Objekt. Sie können jedoch ein COM-Objekt nicht einfach verwenden. Objekte stellen nämlich nur ganz bestimmte Funktionalitäten zur Verfügung. Genauso, wie Sie ein Buch nicht lesen können, bevor Sie es gefunden haben, können Sie ein COM-Objekt nicht einfach verwenden. Stattdessen müssen Sie das Objekt erst in Ihr Script einbinden. Das WScript-Objekt stellt Ihnen hierzu zwei Methoden zur Verfügung: CreateObject und GetObject. Eine neue Instanz eines COM-Objektes erstellen Um eine neue Instanz eines COM-Objektes zu erstellen, können Sie die Methode CreateObject des Objektes WScript verwenden. Diese Methode erwartet als Parameter den Programmatic Identifier (ProgID) des COM-Objektes. Die genaue Syntax sieht so aus: WScript.CreateObject("ProgID")
Um die Analogie mit der Bibliothek wieder aufzugreifen: Die ProgID ist das Equivalent zur Ablagenummer des Buches. Wenn Sie in die Bibliothek gehen, geben Sie dem Bibliothekar diese Ablagenummer, und er oder sie sucht das Buch für Sie heraus. Wenn Sie dem Scripting Seite 122 von 394
Host die ProgID geben, dann kann der Host in der Registrierung nach dem COM-Objekt suchen, das Sie erstellen möchten. Woher wissen Sie nun die richtige ProgID eines COM-Objektes? Unglücklicherweise gibt es keine einfache Antwort auf diese Frage. Der einfachste Weg ist es, wenn Sie in die Dokumentation dieses Objektes schauen. Alle ProgIDs werden unter HKEY_CLASSES_ROOT in der Registrierung gespeichert. Ein Beispiel sehen Sie in Abbildung 3.4. Diese ProgID-Liste ist jedoch leider nur von eingeschränktem Nutzen, da nicht alle ProgIDs in Scripten verwendet werden können.
Abbildung 3.4: ProgIDs in der Regisrierung Um ein neu erstelltes Objekt verwenden zu können, muss Ihr Script eine Referenz auf das Objekt in einer Variablen speichern: SetobjVariable= WScript.CreateObject (" ProgID" )
Nachdem die Referenz in einer Variablen gespeichert wurde, kann das Script über die PunktNotation auf die Methoden und Eigenschaften des Objekts zugreifen (die Punkt-Notation wird im Kapitel VBScript Primer dieses Buches besprochen). Eine Methode können Sie über die folgende Syntax aufrufen: objVariable.Methodenname
Der Zugriff auf Eigenschaften verwendet die gleiche Syntax: objVariable.Eigenschaftsname
Script 3.1 erstellt eine neue Instanz des ADSI-Objektes ADSystemInfo und speichert die Referenz auf das Objekt in der Variablen objSysInfo. Dann zeigt es den DNS-Namen der Domäne des angemeldeten Benutzers an. Script 3.1: Ein COM-Objekt verwenden 1Set objSysInfo = Wscript.CreateObject("ADSystemInfo") 2Wscript.Echo "Domain DNS name: " & objSysInfo.DomainDNSName
Seite 123 von 394
Wie Sie in Abbildung 3.5 sehen, müssen Sie nur zwei Teile des Befehls ändern, wenn Sie ein anderes COM-Objekt verwenden möchten - die ProgID und den Namen der Referenzvariable (die Variable brauchen Sie nur zu ändern, wenn Ihr Script mehrere Objekte verwenden soll).
Abbildung 3.5: Elemente des Befehls, der ein COM-Objekt erstellt Das folgende Script bindet zum Beispiel mehrere COM-Objekte ein: Set Set Set Set
objReference objReference objReference objReference
= = = =
Wscript.CreateObject("Word.Application") Wscript.CreateObject("InternetExplorer.Application") Wscript.CreateObject("Scripting.Dictionary") Wscript.CreateObject("Wscript.Network")
Eine Verbindung zu einer bestehenden Instanz eines COM-Objektes aufbauen Wenn das COM-Objekt, das Sie verwenden möchten, bereits ausgeführt wird, können Sie auch dieses verwenden - Sie müssen in diesem Fall keine neue Instanz erstellen. Hierzu verwenden Sie die Methode GetObject des WScript-Objektes. Diese gibt Ihnen eine Referenz auf das bestehende Objekt zurück. Wenn Sie Scripte schreiben, die WMI oder ADSI verwenden, dann werden Sie normalerweise immer die Methode GetObject verwenden. Das liegt daran, dass WMI und ADSI standardmäßig bereits zur Verfügung stehen (den WMI-Dienst können Sie zwar auch anhalten, aber wenn Sie ein Script ausführen, das WMI verwendet, dann wird der Dienst automatisch neu gestartet). Auch wenn es Ausnahmen gibt, wird das typische WMI-Script wohl meist mit einer Zeile wie der folgenden anfangen: Set objWMIService = Wscript.GetObject("winmgmts:")
Vergleich der VBScript Funktionen CreateObject und GetObject mit den entsprechenden WSH-Funktionen Auch VBScript stellt die Funktionen CreateObject und GetObject zur Verfügung. Wenn sie mit der ProgID des COM-Objektes als Parameter aufgerufen werden, erstellt sowohl die VBScript-Funktion CreateObject als auch Ihr WScript-Equivalent eine Instanz des COMObjektes. Die folgenden zwei Codezeilen machen genau das Gleiche - die erste verwendet die VBScript-Version von CreateObject und die zweite die WSH-Version: Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFSO = Wscript.CreateObject("Scripting.FileSystemObject")
Beide Versionen von CreateObject akzeptieren auch einen zweiten Parameter - er wird jedoch bei beiden Methoden komplett anders interpretiert. Sehen Sie sich die folgenden zwei Codezeilen an. Wiederum wird in der ersten Zeile die VBScript-Version und in der zweiten Zeile die WSH-Version verwendet: Set objExcel = CreateObject("Excel.Application", "Parameter2") Set objExcel = Wscript.CreateObject("Excel.Application", "Parameter2")
Die CreateObject-Funktion von VBScript interpretiert den zweiten Parameter als Name eines Remotecomputers - in dem oben gezeigten Beispiel versucht die Methode eine Instanz von Microsoft Excel auf dem Remotecomputer mit dem Namen Parameter2 zu erstellen. Die WScript-Version von CreateObject interpretiert den zweiten Parameter hingegen als Prefix einer Subroutine, die verwendet werden soll, wenn Ereignisse für das Objekt auftreten. Seite 124 von 394
Um einfach nur ein COM-Objekt zu erstellen, können Sie beide Funktionen verwenden. Es gibt in diesem Fall keinen Unterschied. Wenn Sie jedoch ein Remoteobjekt erstellen möchten oder auf Ereignisse des Objektes reagieren müssen, dann sollten Sie die passende Version der Methode auswählen. Weitere Informationen zu den Funktionen CreateObject und GetObject von VBScript finden Sie im Kapitel VBScript Primer dieses Buches.
Eingaben und Ausgaben Scripte müssen mit Benutzern interagieren. Ein Script, das das Ereignisprotokoll nach bestimmten Ereignissen durchsucht, sollte auch mitteilen können, wenn passende Ereignisse gefunden wurden. Oder stellen Sie sich vor, Sie schreiben ein Script dass die Verbindung zwischen zwei Computern testet. Sie benötigen für dieses Script eine einfache Möglichkeit, die beiden Computernamen als Eingabe entgegenzunehmen. WSH stellt Mittel zur Interaktion mit dem Benutzer und - möglicherweise viel wichtiger Mittel für eine Eingabe und Ausgabe in der Kommandozeile zur Verfügung. Neben vielen anderen Dingen haben Sie so die Möglichkeit Scripte zu erstellen, die Sie wie Kommandozeilenwerkzeuge verwenden können. Ein einfacher Weg eine Ausgabe zu erzeugen, ist die Methode WScript.Echo. Sie erwartet einen Parameter (einen String, eine Zahl oder ein Datum) und gibt diesen Parameter auf dem Bildschirm aus. Script 32. verwendet die Methode WScript.Echo, um die auf dem Computer installierte WSH-Version auszugeben. Script 3.2: Anzeige der installierten WSH-Version 1Wscript.Echo "The version of WSH on this computer is: " & WScript.Version
Das WScript-Objekt verfügt außerdem über Eigenschaften, die es Ihrem Script ermöglicht, auf drei Objekte zuzugreifen, die Eingabe- und Ausgabefunktionalitäten zur Verfügung stellen: StdOut, StdIn und StdErr. Diese Objekte verfügen wiederum über eigene Methoden und Eigenschaften - diese sehen Sie in Abbildung 3.6. Ein Script kann normalerweise nur dann auf die Methoden StdOut, StdIn und StdErr zugreifen, wenn es unter CScript ausgeführt wird.
Seite 125 von 394
Abbildung 3.6: Methoden und Eigenschaften der TextStream-Objekte Nachrichten ausgeben Ein primärer Zweck von administrativen Scripten ist das Abfragen von Informationen. Wie viel freier Festplattenplatz steht noch zur Verfügung? Unter welchem Konto werden die Internetinformationsdienste ausgeführt? Welche Prozesse verbrauchen den gesamten freien Speicher auf dem Mailserver? Ein Script, das solche Fragen beantwortet, muss eine Möglichkeit haben, mit dem Benutzer zu kommunizieren. Auch wenn es die Informationen auch in einer Textdatei oder einer Datenbank speichern könnte, ist es doch einfacher, wenn sie direkt auf dem Bildschirm ausgegeben werden. Der WSH kann solche Bildschirmausgaben über die Methode Echo durchführen. Wenn ein Script unter CScript ausgeführt wird, dann gibt die Methode WScript.Echo die Informationen in der Eingabeaufforderung aus. Unter WScript werden die Informationen hingegen in einem Nachrichtenfenster angezeigt. Die folgende Codezeile gibt zum Beispiel das Ergebnis einer Berechnung aus: Wscript.Echo 2 + 2
Die Echo-Methode ist einfach zu implementieren. Sie geben ihr einfach die Informationen als Parameter mit, die auf dem Bildschirm angezeigt werden sollen - zum Beispiel über eine Variable: Seite 126 von 394
Wscript.Echo strMyVariable
Auch die Inhalte von VBScript, wie zum Beispiel Now und Time, können Sie ausgeben: Wscript.Echo Now
Um einen String auszugeben, schließen Sie diesen in Anführungsstriche ein: Wscript.Echo "This is my string."
Das einzig wirklich Wichtige, das Sie bei der Arbeit mit WScript.Echo berücksichtigen müssen, ist das unterschiedliche Verhalten unter WScript und CScript. Script 3.3 gibt zum Beispiel drei Nachrichten über WScript.Echo aus: Script 3.3: Ausgabe von drei Nachrichten über die Methode Echo 1Wscript.Echo "Prüfung der Systemlaufwerke " 2Wscript.Echo "Abfragen des freien Speicherplatzes " 3Wscript.Echo "Zuordnen von Netzlaufwerken "
Wenn Sie das Script unter CScript ausführen, sehen Sie im Kommandozeilenfenster die folgende Ausgabe: Prüfung der Systemlaufwerke Abfragen des freien Speicherplatzes Zuordnen von Netzlaufwerken
Wenn Sie das Script unter WScript ausführen, produziert es die drei einzelnen Nachrichtenfenster, die Sie in Abbildung 2.7 sehen. Der Benutzer muss in jedem dieser Nachrichtenfenster auf OK klicken, damit das Script weiter ausgeführt wird.
Abbildung 3.7: Echo-Methode unter WScript Unter WScript führt jeder Aufruf von WScript.Echo zu einem neuen Nachrichtenfenster. Abhängig vom Script kann das sehr mühsam werden. Bei einem kleinen Script, das den freien Speicherplatz auf Laufwerk C abfragt, ist es normalerweise egal, ob die Ausgabe in der Kommandozeile oder in einem Nachrichtenfenster angezeigt wird. Wenn des Script jedoch den Status aller Dienste zurückgibt, dann müssen Sie im Zweifelsfall in vielen Dutzend Nachrichtenfenstern auf OK klicken. Unter CScript würden alle Informationen ohne Aktion des Benutzers im Kommandozeilenfenster ausgegeben.
Texteingaben und -ausgaben Kommandozeilenwerkzeuge arbeiten typischerweise mit drei standardmäßigen Eingabe- und Ausgabemöglichkeiten - diese werden auch als Standard In (StdIn), Standard Out (StdOut) und Standard Error (StdErr) bezeichnet. Wenn sie es nicht anders festgelegt haben, geht der Kommandozeilenprozessor davon aus, dass die Eingaben über die Tastatur (StdIn), die Ausgaben (StdOut) und Fehlernachrichten (StdErr) in der Kommandozeile ausgegeben werden sollen.
Seite 127 von 394
StdIn, StdOut und StdErr stehen nur zur Verfügung, wenn das Script unter CScript ausgeführt wird. Über diese drei Objekte können Sie die drei unterschiedlichen Ein- und Ausgabemöglichkeiten in Ihren Scripten nutzen: • • •
Sie können Ausgaben im Kommandozeilenfenster durchführen. Sie können im Kommandozeilenfenster Eingaben durch den Benutzer einlesen. Sie können Fehlerinformationen in Ihrem Script entgegennehmen, die von anderen Scripten, Batchdateien oder Kommandozeilentools ausgegeben werden.
Ausgaben über StdOut durchführen Über StdOut können sie Ausgaben im Kommandozeilenfenster durchführen. Die einzelnen Methoden von StdOut sehen Sie in Tabelle 3.4. Tabelle 3.4: Methode von StdOut Methode
Beschreibung
Write
Gibt den übergebenen Parameter auf dem Bildschirm aus. Hängt jedoch keinen Zeilenumbruch an. Das folgende Script führt zum Beispiel vier Ausgaben durch: Wscript.StdOut.Write "ABCD" Wscript.StdOut.Write "EFGHIJKLMN" Wscript.StdOut.Write "OPQRSTUV" Wscript.StdOut.Write "WXYZ" Die Ausgabe des Scripts sieht so aus: ABCDEFGHIJKLMNOPQRSTUVWXYZ
WriteLine
WriteLine arbeitet genauso wie WScript.Echo. Der angehängte Parameter wird auf dem Bildschirm ausgegeben, und es wird ein Zeilenumbruch angehängt. Das folgende Script führt wiederum vier Ausgaben durch: Wscript.StdOut.WriteLine "ABCD" Wscript.StdOut.WriteLine "EFGHIJKLMN" Wscript.StdOut.WriteLine "OPQRSTUV" Wscript.StdOut.WriteLine "WXYZ" Die Ausgabe des Scripts sieht dieses Mal jedoch so aus: ABCD EFGHIJKLMN OPQRSTUV WXYZ
WriteBlankLinesGibt Leerzeilen aus. Als Parameter benötigt WriteBlankLines die Anzahl der auszugebenden Leerzeilen:
Seite 128 von 394
Methode
Beschreibung
Wscript.StdOut.WriteLine "ABCD" Wscript.StdOut.WriteBlankLines 1 Wscript.StdOut.WriteLine "EFGHIJKLMN" Wscript.StdOut.WriteLine "OPQRSTUV" Wscript.StdOut.WriteBlankLines 2 Wscript.StdOut.WriteLine "WXYZ" Das Script produziert die folgende Ausgabe: ABCD EFGHIJKLMN OPQRSTUV WXYZ Script 3.4 zeigt eine Nachricht über die Methoden Write und WriteLine des Objektes StdOut.TextStream an. Script 3.4: Verwendung der Methoden Write und WriteLine im Kommandozeilenfenster 1 Set objNetwork = Wscript.CreateObject("Wscript.Network") 2 Set objStdOut = WScript.StdOut 3 objStdOut.Write "Benutzer: " 4 objStdOut.Write objNetwork.UserDomain 5 objStdOut.Write "\" 6 objStdOut.Write objNetwork.UserName 7 objStdOut.WriteBlankLines(1) 8 objStdOut.WriteLine objNetwork.ComputerName 9 objStdOut.Write "Informationen abgefragt." 10objStdOut.Close
Die Ausgabe von Script 3.4 sieht so aus: Benutzer: FABRIKAM\kenmyer atl-wk-01 Informationen abgefragt.
Wenn Sie statt der Methode StdOut die Methode Wscript.Echo verwenden würden, sähe die Ausgabe so aus: Benutzer: FABRIKAM \ kenmeyer atl-wk-01 Informationen abgefragt.
Eingaben über StdIn entgegennehmen
Seite 129 von 394
Ein Weg, um in einem Script Eingaben entgegenzunehmen, sind Argumente (diese werden weiter unten in diesem Kapitel besprochen). Der folgende Befehl führt ein Script mit dem Namen DeleteUser.vbs aus und übergibt als Argument den zu löschenden Benutzernamen: cscript DeleteUser.vbs kenmyer
Die Verwendung von Argumenten ist schnell und einfach umzusetzen. Andererseits muss der Benutzer bei der Verwendung des Scripts wissen, welche Argumente es gibt und wie er diese benutzen kann. Es gibt jedoch eine Alternative - fordern Sie den Benutzer zu Eingabe der benötigten Informationen auf, nachdem das Script gestartet wurde: C:\Scripts\cscript DeleteUser.vbs Bitte geben Sie den Namen des zu löschenden Benutzerkontos an: _
Mit StdIn können Sie Eingaben in der Kommandozeile entgegennehmen. Die Methoden und Eigenschaften von StdIn sehen Sie in Tabelle 3.5. Tabelle 3.5: Methoden und Eigenschaften von StdIn Methode/EigenschaftBeschreibung
Read
Liest die angegebene Anzahl an Zeichen ein. Der folgende Befehl liest zum Beispiel jeweils drei Zeichen über StdIn ein und gibt diese wieder aus, bis ein Zeilenumbruch (ENTER) eingegeben wurde: Do Until Wscript.StdIn.AtEndOfLine strInput = Wscript.StdIn.Read(3) Wscript.Echo strInput Loop Wenn Sie 'abcdefghijklmnopqrstuvwxyz' eingeben, wird die Ausgabe so aussehen: abc def ghi jkl mno pqr stu vwx yz
ReadLine
Liest eine Zeile ein. ReadLine ist besonders dann sehr nützlich, wenn der Benutzer Eingaben machen soll. Es liest alle Eingaben, bis der Benutzer die Taste ENTER drückt: strInput = Wscript.StdIn.ReadLine Wscript.Echo strInput Wenn Sie 'abcdefghijklmnopqrstuvwxyz' eingeben, sieht die Ausgabe des Scripts so aus: abcdefghijklmnopqrstuvwxyz
ReadAll
Liest die gesamten Ausgaben eines Kommandozeilentools, einer Batchdatei oder eines Shellbefehls ein.
Skip
Überspringt die angegebene Anzahl von Zeichen. Das folgende Script überspringt zum Beispiel die ersten 23 Zeichen der Eingabe Seite 130 von 394
Methode/EigenschaftBeschreibung
und liest die verbleibenden Zeichen auf einmal ein:
Wscript.StdIn.Skip(23) Do Until Wscript.StdIn.AtEndOfLine strInput = Wscript.StdIn.Read(1) Wscript.Echo strInput Bei der Eingabe 'abcdefghijklmnopqrstuvwxyz' sieht die Ausgabe des Scripts so aus: x y z SkipLine
Überspringt eine Zeile.
AtEndOfLine
Ein Boolean-Wert (ein Wert, der nur zwei Zustände haben kann - typischerweise True und False), der anzeigt, ob das Ende einer Zeile erreicht wurde. Über diese Eigenschaft kann das Script erkennen, ob die gesamte Zeile eingelesen wurde in diesem Fall hat sie den Wert True. Do Until Wscript.StdIn.AtEndOfLine strInput = Wscript.StdIn.Read(1) Wscript.Echo strInput Loop
AtEndOfStream
Ein Boolean-Wert, der anzeigt, ob das Ende der Eingabe erreicht wurde. Er zeigt einem Script zum Beispiel das Ende von einem Kommandozeilentool, von einer Batchdatei oder den von einem über einen Shellbefehl ausgegebenen Text an.
Mit StdIn können Sie Eingaben von Benutzern entgegennehmen. Hierzu gehen Sie folgendermaßen vor: 1.Verwenden Sie die Methode Write, um eine Eingabeaufforderung anzuzeigen (zum Beispiel "Bitte geben Sie Ihren Namen ein:"). Mit dieser Methode stellen Sie sicher, dass die Eingabeaufforderung und die Eingabe des Benutzers in derselben Zeile angezeigt werden (bei einer Ausgabe mit Write wird kein Zeilenumbruch an die Ausgabe angehängt). Eine solche Ausgabe sieht dann zum Beispiel so aus (der Unterstrich repräsentiert den Cursor): C:\Scripts Please enter your name: _ 2.Verwenden Sie dann die Methode ReadLine, um die Eingaben des Benutzers einzulesen, und speichern Sie diese Eingaben in einer Variablen. Mit ReadLine werden die Eingaben so lange eingelesen, bis der Benutzer ENTER drückt. Wenn Sie zum Beispiel Klaus Meyer eingeben und dann ENTER drücken, dann wird in der Variable der Wert "Klaus Meyer" gespeichert. Mit ReadLine können Sie allerdings nicht mehr als 254 Zeichen am Stück einlesen. Stellen Sie sich zum Beispiel vor, Sie möchten ein Script erstellen, das Zahlen aus dem dezimalen Format in das hexadezimale Format konvertiert. Sie müssen den Benutzer eine Seite 131 von 394
dezimale Zahl eingeben lassen. Script 3.5 verwendet hierzu die Methode ReadLine des WScript-Objekts StdIn. Der eingelesene Wert wird dann in der Variablen strDecimal gespeichert. Die Funktion Hex von VBScript konvertiert den gespeicherten dezimalen Wert dann in einen hexadezimalen Wert - dieser wird dann über die Methode WriteLine des WScript-Objekts StdOut ausgegeben. Script 3.5: Konvertierung eines dezimalen Wertes in einen hexadezimalen Wert 1Wscript.StdOut.Write "Geben Sie eine Dezimalzahl ein: " 2strDecimal = Wscript.StdIn.ReadLine 3 4Wscript.StdOut.WriteLine strDecimal & " entspricht dem Wert " & _ 5 Hex(strDecimal) & " in hexadezimaler Schreibweise."
Wenn Sie das Script unter CScript ausführen, sieht die Ausgabe so aus: C:\>cscript test.vbs Microsoft (R) Windows Script Host, Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. Alle Rechte vorbehalten. Geben Sie eine Dezimalzahl ein: 254 254 entspricht dem Wert FE in hexadezimaler Schreibweise.
Verwenden von Kommandozeilenargumenten Argumente sind Werte, die Sie beim Start des Scripts in der Kommandozeile angeben können. Wenn Sie bereits mit Kommandozeilentools gearbeitet haben, dann kennen Sie das Konzept wahrscheinlich. Wenn Sie zum Beispiel ping.exe ausführen, dann müssen Sie mindestens einen Hostnamen oder eine IP-Adresse als Argument (auch Parameter genannt) angeben: ping 192.168.1.1 Das Aufrufen eines Scripts mit einem Argument ist in den Fällen sehr praktisch, in denen Sie bei jedem Aufruf des Scripts einen anderen Wert übergeben möchten. Wenn Sie den entsprechenden Wert fest in das Script eintragen würden, dann müssten Sie das Script vor jedem Aufruf bearbeiten. In diesen Fällen sparen Sie sich also mit Kommandozeilenargumenten eine Menge Zeit. Die Arbeit mit Script-Argumenten unterscheidet sich nicht von der Arbeit mit Kommandozeilentools. Sie können Script-Argumente genauso verwenden, wie Sie zum Beispiel Argumente (Parameter) bei ping.exe angeben (IP-Adresse, -t, usw.). Argumente werden in einer Collection mit dem Namen WshArguments gespeichert. Über die Eigenschaft Arguments des Objekts WScript können Sie auf diese Collection zugreifen - dies sehen Sie in Abbildung 3.8. Außerdem werden die Argumente automatisch nach deren Format gefiltert und zusätzlich in die beiden Collections WshNamed und WshUnnamed einsortiert.
Seite 132 von 394
Abbildung 3.8: Collections mit Kommandozeilenargumenten Ablage und Filterung der Argumente Wenn Sie ein Script mit Argumenten starten, dann speichert der WSH diese Argumente in der Collection WshArguments - und zwar in der Reihenfolge, in der Sie beim Start des Scripts angegeben wurden. WScript.Arguments.Item(0) enthält das erste Argument, WScript.Arguments.Item(1) enthält das zweite usw. Die Argumente werden jedoch zusätzlich in zwei weiteren Collections gespeichert: WshNamed und WshUnnamed. Argumente, die im Format /name:wert angegeben wurden, werden in WshNamed gespeichert, und alle Argumente, die nicht dieses Format verwenden, werden in WshUnnamed gespeichert. Der Sinn dieser zusätzlichen gefilterten Speicherung wird weiter unten in diesem Abschnitt genauer besprochen. Wenn Sie zum Beispiel den folgenden Befehl ausführen, dann starten Sie das Script ServerStats.vbs mit den drei Argumenten /s:atl-dc-01, /u:admin und perf. cscript serverstats.vbs /s:atl-dc-01 /u:admin perf Die Argumente werden nach dem Scriptnamen angegeben und müssen durch ein oder mehrere Leerzeichen getrennt werden. Argumente, die selbst Leerzeichen enthalten, müssen in Anführungszeichen eingeschlossen werden. Ein Beispiel hierfür ist das dritte Argument ("perf monitor")im folgenden Befehl: Seite 133 von 394
cscript serverstats.vbs /s:atl-dc-01 /u:admin "perf monitor" Ohne die Anführungsstriche würden perf und monitor als zwei einzelne Argumente behandelt werden. In Abbildung 3.9 sehen Sie die Inhalte der drei Argument-Collections, nachdem Sie ein Script mit dem Namen status.vbs und den drei Argumenten /s:atl-dc-01, /u:admin und perf gestartet haben.
Abbildung 3.9: Inhalte der Argument-Collections In der Collection WshArguments werden alle Argumente genauso gespeichert, wie Sie in der Kommandozeile angegeben wurden. Die Methode Count und die Eigenschaft Length geben die Gesamtzahl der übergebenen Argumente zurück. In der Collection WshNamed sind die zwei Argumente gespeichert, die zusätzlich 'Werte' angeben. Solche Argumente bestehen immer aus zwei Teilen: dem Argument selbst (zum Beispiel /u) und dem Wert für dieses Argument (zum Beispiel admin). Argument und Wert werden hierbei durch einen Doppelpunkt getrennt (zum Beispiel /u:admin). Das Argument muss in diesem Fall immer mit einem Slash (/) angegeben werden. Es ist nicht möglich, alternativ ein Minuszeichen (-) zu verwenden. Der folgende Befehl führt zum Beispiel nicht dazu, dass das Argument in der Collection WshNamed gespeichert wird. Stattdessen wird das Argument in der Collection WSHUnnamed gespeichert: cscript serverstats.vbs -Server:atl-dc-01 Wenn Sie sich Abbildung 3.9 genau ansehen, dann werden Sie feststellen, dass die Argumente in der Collection WshNamed vor deren Speicherung verändert werden. Der 'Argument-Teil' des Arguments (also zum Beispiel der Teil /s) wird zum Index und kann über die Eigenschaft Item der Collection WshNamed verwendet werden, um den 'Wert-Teil' (zum Seite 134 von 394
Beispiel at1-dc-01) des Arguments abzufragen. Auch für die Methode Exists der Collection WshNamed können Sie den 'Argument-Teil' verwenden. Diese Methode prüft, ob das entsprechende Argument beim Scriptstart angegeben wurde. Wie Sie in Abbildung 3.9 sehen, werden Slash und Doppelpunkt verworfen, und nur der tatsächliche Parametername (s) und der Wert des Parameters (at-dc-01) werden in der Collection gespeichert. Wie in der Collection WshArguments auch, können Sie in der Collection WshNamed über die Methoden Count und die Eigenschaft Length die Anzahl der Argumente in der Collection abrufen. Es gibt also drei Wege, um auf die Kommandozeilenargumente zuzugreifen: • • •
Sie können über die Collection WshArguments auf alle Argumente zugreifen. Über die Collection WshNamed können Sie nur auf die Argumente zugreifen, bei denen ein zusätzlicher Wert angegeben wurde (zum Beispiel /s:at-dc-01). Über die Collection WshUnnamed können Sie auf die Argumente zugreifen, bei denen keine zusätzlichen Werte angegeben wurden (zum Beispiel /u).
Verwenden der standardmäßigen Argument-Collection WshArguments Auf die Collection WshArguments greifen Sie über Eigenschaft Arguments des Objekts WScript zu. Die Collection WshArguments stellt zwei Methoden und vier Eigenschaften für die Arbeit mit Argumenten zur Verfügung. Methoden •
Count - Gibt die Gesamtzahl der Argumente in der Collection WshArguments zurück.
•
ShowUsage - Gibt Informationen zur Verwendung des Scripts aus. Diese Informationen werden dem Abschnitt runtime einer WSF-Datei (Windows Script File - .wsf) entnommen. WSF-Dateien werden in diesem Buch nicht besprochen.
Eigenschaften •
Named - Stellt einen Zugriff auf die Collection WshNamed zur Verfügung. WshNamed ist eine gefilterte Collection, in der alle Argumente im Format /name:wert gespeichert sind. Solche Argumente werden im WSH auch 'Named Arguments' (benannte Argumente) genannt.
•
Unnamed - Stellt einen Zugriff auf die Collection WshUnnamed zur Verfügung. WshUnnamed ist eine gefilterte Collection mit den Argumenten, die nicht im Format /name:wert angegeben wurden. Solche Argumente werden im WSH auch 'Unnamed Arguments' (unbenannte Argumente) genannt.
•
Length - Gibt die Gesamtzahl der Argumente in der Collection WshArguments an.
Anmerkung: Vielleicht ist Ihnen aufgefallen, das die Eigenschaft Length mit der Methode Count gleichbedeutend ist. Die Eigenschaft Length wurde nur aufgenommen, um der ECMAScript-Sprachspezifikation zu entsprechen (Standard ECMA-262), auf der auch JScript basiert. Auch wenn Sie unter VBScript sowohl Count als auch Length verwenden können, müssen Sie unter JScript explizit Length verwenden. •
Item(n) - Gibt das Element mit der angegebenen Indexnummer (n) der Collection WshArguments zurück. Seite 135 von 394
Mit dem folgenden Befehl wird ein Script mit dem Namen GetEvents.vbs und drei Argumenten ausgeführt: cscript getevents.vbs atl-dc-01 "Directory Service" 1130 WSH speichert die drei Argumente in der Reihenfolge in der Collection WshArguments, in der sie angegeben wurden. Um die drei Argumente auszulesen, verwenden Sie die Eigenschaft Arguments des Objekts WScript in Kombination der Eigenschaft WshArguments.Item: ServerName = WScript.Arguments.Item(0) EventLog = WScript.Arguments.Item(1) EventID = WScript.Arguments.Item(2)
Da alle Collections bei 0 beginnen, erhalten Sie mit WScript.Arguments.Item(0) das erste Argument zurück. WScript.Arguments.Item(1) liefert Ihnen das zweite Argument (dieses Argument wurde in Anführungszeichen eingeschlossen, da es Leerzeichen enthält). WScript.Arguments.Item(2) liefert Ihnen das dritte Argument zurück. Die Inhalte der Collection sehen Sie in Tabelle 3.6. Tabelle 3.6: Beispielinhalte der Collection WSHArguments ItemWert
0
atl-dc-01
1
Directory Service
2
1130
Wenn das Script mit weiteren Argumenten aufgerufen worden wäre, dann würde WScript.Arguments.Item(3) das vierte Argument zurückliefern usw. Es gibt keine Beschränkung für die Anzahl der Argumente, die in der Collection WshArguments gespeichert werden können. Die gesamte Befehlszeile darf jedoch die maximale Länge der Kommandozeile nicht überschreiten. Sie können sich viel Arbeit sparen, wenn Sie die Collection WshArguments einer Variablen zuweisen. So brauchen Sie nicht jedes Mal WScript.Arguments schreiben, wenn Sie auf ein Argument zugreifen möchten, sondern können den Namen der Variable nutzen. Verwenden Sie hierzu das VBScript-Schlüsselwort Set, gefolgt von dem Variablennamen. Das Schlüsselwort Set ist erforderlich, da es sich bei Collections um normale COM-Objekte handelt. Das folgende Beispiel macht genau das Gleiche, wie das vorhergehende Beispiel. Es verwendet jedoch zum Zugriff auf die Collection eine Variable: Set args = WScript.Arguments ServerName = args.Item(0) EventLog = args.Item(1) EventID = args.Item(2)
Die letzten drei Beispiele gehen alle davon aus, dass mit GetEvents.vbs immer drei Argumente übergeben werden. Bei einem eben schnell geschriebenen Ad-Hoc-Script können Sie möglicherweise darauf verzichten, die Anzahl der übergebenen Argumente zu überprüfen, bei anderen Scripten kann dies jedoch schnell zu Fehlern führen - ganz besonders dann, wenn auch andere Benutzer das Script verwenden sollen. Die folgende Zeile startet das Script GetEvents.vbs ohne Argumente:
Seite 136 von 394
cscript getevents.vbs In diesem Beispiel würden alle drei oben verwendeten Beispielscripte zu folgendem Laufzeitfehler führen: C:\test.vbs(2, 1) Laufzeitfehler in Microsoft VBScript: Index außerhalb des gültigen Bereichs
Wenn Sie versuchen auf ein Item zuzugreifen, das nicht existiert, kommt es zu diesem Laufzeitfehler. In Script 3.6 sehen Sie, wie Sie über die Methode WshArguments.Count sicherstellen können, dass beim Scriptstart die richtige Anzahl von Argumenten verwendet wurde. Script 3.6: Anzahl der übergebenen Argumente über Count prüfen 1If WScript.Arguments.Count = 3 Then 2 ServerName = WScript.Arguments.Item(0) 3 EventLog = WScript.Arguments.Item(1) 4 EventID = WScript.Arguments.Item(2) 5Else 6 Wscript.Echo "Verwendung: GetEvents.vbs ServerName EventLog EventID" 7 Wscript.Quit 8End If
In Zeile 1 des Scripts wird die Methode WshArguments.Count verwendet, um die Anzahl der übergebenen Argumente abzufragen. Wenn es sich um drei Argumente handelt, dann weist das Script diese den Variablen ServerName, EventLog und EventID zu. Andernfalls gibt das Script eine Information zur Verwendung des Scripts aus. Unbenannte Kommandozeilenargumente Unbenannte (Unnamed) Argumente bestehen nur aus einem einzelnen Wert. Solange die Reihenfolge der Argumente nicht egal ist (zum Beispiel, wenn drei Computernamen angegeben werden und es egal ist, welcher Computer als Erstes abgefragt wird), müssen die Argumente in der vom Script erwarteten Reihenfolge angegeben werden. Unbenannte Argumente können Sie in den folgenden Fällen verwenden: • •
Wenn das Script nur ein oder zwei Argumente verwendet - zum Beispiel den Namen eines Ordners. In diesem Fall gibt es keinen Grund, benannte (Named) Argumente zu verwenden. Wenn im Script alle Argumente gleich behandelt werden - wenn Sie zum Beispiel die Ereignisprotokolle auf bestimmten Computern abfragen wollen, dann können Sie für die Computernamen unbenannte Argumente verwenden.
Wenn Sie ein Script mit unbenannten Argumenten ausführen, dann werden diese von WSH in der Collection WshArguments gespeichert - und zwar in der Reihenfolge, in der Sie bei Scriptstart angegeben wurden. Sie können im Script über die Indexnummern auf die Argumente zugreifen. Diese beginnen bei 0. Script 3.7 verwendet die Collection-Arguments zur Anzeige der verwendeten Argumente. Script 3.7: Abfragen der Kommandozeilenargumente über die Collection Arguments 1strServer = WScript.Arguments.Item(0) 2strPacketSize = WScript.Arguments.Item(1) 3strTimeout = WScript.Arguments.Item(2) 4
Seite 137 von 394
5Wscript.Echo "Anzupingender Server: " & strServer 6Wscript.Echo "Paketgröße: " & strPacketSize 7Wscript.Echo "Timeout: " & strTimeout
Die folgende Befehlszeile führt das Script mit Argumenten aus - und zwar in der Reihenfolge, in der das Script die Argumente erwartet: EchoUnnamedArgs.vbs DCServer01 100 5000 Das Script erzeugt die folgende Ausgabe: Anzupingender Server: DCServer01 Paketgröße: 100 Timeout: 5000
Die folgende Befehlszeile führt das gleiche Script aus. Die Argumente werden jedoch in einer falschen Reihenfolge angegeben: EchoUnnamedArgs.vbs 100 DCServer01 5000 Das Script produziert die folgende Ausgabe: Anzupingender Server: 100 Paketgröße: DCServer01 Timeout: 5000
Die Ausgabe im ersten Versuch ist korrekt - die Ausgabe im zweiten Versuch ist hingegen falsch. Da das Script mit unbenannten Argumenten arbeitet, und diese nicht über Ihren Namen, sondern nur in der Eingabereihenfolge ausgelesen werden können, ist das Script darauf angewiesen, dass die Argumente in der korrekten Reihenfolge angegeben werden. Wenn das Script im zweiten Versuch ping.exe mit den Argumenten ausgeführt hätte, dann hätte dies zu einem Fehler geführt - es gibt keinen Server mit dem Namen 100 oder eine Paketgröße von DCServer01. Benannte Kommandozeilenargumente Wie das vorhergehende Beispiel zeigt, ist die Reihenfolge der Argumente bei unbenannten Argumenten für die erfolgreiche Ausführung des Scripts entscheidend. Der Aufruf des Scripts wird so unnötig kompliziert. Der Benutzer muss sich nicht nur an die Parameter, sondern auch noch an deren Reihenfolge erinnern. Kommt es in diesem Zusammenhang zu einem Fehler, schlägt das Script fehl. Um den Aufruf von Scripten mit mehreren Argumenten zu vereinfachen, können Sie benannte Argumente verwenden. Benannte Argumente sind die Argumente, die aus einem Namen und einem dazugehörigen Wert bestehen (zum Beispiel /s:DCServer01). Sie können in jeder beliebigen Reihenfolge angegeben werden. Ein benanntes Argument beginnt im mit einem Slash (/) und dem Namen des Arguments. Dann folgt durch einen Doppelpunkt vom Namen getrennt der Wert des Arguments. Die folgende Befehlszeile führt ein Script mit dem Namen ServerTest.vbs mit zwei benannten Argumenten aus: ServerTest.vbs /Server:HRServer01 /Timeout:3000 Der Name des ersten Arguments lautet Server - sein Wert ist HRServer01. Der Name des zweiten Arguments lautet Timeout und sein Wert ist 3000. Diese beiden Argumente könnten auch in umgekehrter Reihenfolge angegeben werden: Seite 138 von 394
ServerTest.vbs /Timeout:3000 /Server:HRServer01 Die benannten Argumente eines Scripts werden in der Collection WshNamed gespeichert. Script 3.8 verwendet diese Collection, um die Argumente anzuzeigen. Script 3.8: Abfragen von Argumenten über die Collection WshNamed 1Set colNamedArguments = WScript.Arguments.Named 2 3strServer = colNamedArguments.Item("Server") 4strPacketSize = colNamedArguments.Item("PacketSize") 5strTimeout = colNamedArguments.Item("Timeout") 6Wscript.Echo "Servername: " & strServer 7Wscript.Echo "Paketgröße: " & strPacketSize 8Wscript.Echo "Timeout (ms): " & strTimeout
Über die folgende Befehlszeile wird das Script zusammen mit drei benannten Argumenten ausgeführt: EchoNamedArgs.vbs /Server:HRServer01 /PacketSize:300 /Timeout:8000 Das Script erzeugt die folgende Ausgabe Servername: HRServer01 Paketgröße: 300 Timeout (ms): 8000
Die nächste Befehlszeile führt dasselbe Script aus. Die Argumente werden jedoch in einer anderen Reihenfolge angegeben: EchoNamedArgs.vbs /Timeout:8000 /PacketSize:300 /Server:HRServer01 Trotzdem erzeugt auch dieses Script die folgende Ausgabe Servername: HRServer01 Paketgröße: 300 Timeout (ms): 8000
Die Ausgabe ist bei beiden Befehlszeilen gleich. Sie können die Argumente in jeder beliebigen Reihenfolge angeben. Auch wenn Sie optionale Parameter verwenden wollen, sind benannte Argumente sinnvoll. Script 3.9 entspricht Script 3.8. Das Argument PacketSize ist jedoch optional. Wenn der Benutzer keine Paketgröße angibt, verwendet das Script eine Standardpaketgröße. Das Script verwendet die Methode Exists um zu prüfen, ob das Argument PacketSize angegeben wurde. Wenn das Ergebnis der Prüfung Wahr (True) ist, dann fährt das Script mit Zeile 7 fort. Wenn das Ergebnis der Prüfung Falsch (False) ist, dann geht es mit Zeile 9 weiter. In Zeile 9 wird die Variable PacketSize über die Konstante DEFAULT_PACKET_SIZE auf den Wert 100 gesetzt. Script 3.9: Kommandozeilenargumente und Standardwerte 1 2 3 4 5 6
Const DEFAULT_PACKET_SIZE = 100 Set colNamedArguments = WScript.Arguments.Named strServer = colNamedArguments.Item("Server") If colNamedArguments.Exists("PacketSize") Then
Seite 139 von 394
7 strPacketSize = colNamedArguments.Item("PacketSize") 8 Else 9 strPacketSize = DEFAULT_PACKET_SIZE 10End If 11strTimeout = colNamedArguments.Item("Timeout") 12 13Wscript.Echo "Server Name: " & strServer 14If colNamedArguments.Exists("PacketSize") Then 15 Wscript.Echo "Packet Size :" & strPacketSize 16Else 17 Wscript.Echo "Packet Size [default]: " & strPacketSize 18End If 19Wscript.Echo "Timeout (ms): " & strTimeout
Unbenannte und benannte Argumente verwenden Normalerweise sollten Sie benannte und unbenannte Argumente nicht zusammen in einem Script verwenden. Der Scriptaufruf wird so nur unnötig kompliziert. Wenn Sie zwei oder ein Argument verwenden, sollten Sie unbenannte Argumente nutzen. Bei mehr als zwei Argumenten ist es besser benannte Argumente zu verwenden. Wenn Sie jedoch beide Argumenttypen zusammen verwenden, dann müssen die unbenannten Argumente beim Scriptaufruf zuerst angegeben werde: CheckServer.vbs HRServer01 /timeout:200 /logfile:serverlog.txt /verbose:true Überprüfen von Kommandozeilenargumenten Viele Scripte benötigen eine bestimmte Anzahl von Argumenten. Um sicherzustellen, dass die Argumente angegeben wurden, haben Sie zwei Möglichkeiten: • •
Sie prüfen, ob die Zahl der angegeben Argumente der erforderlichen Argumentzahl entspricht. Sie prüfen, ob die erforderlichen Argumente angegeben wurden.
Die Anzahl der angegebenen Argumente prüfen Stellen Sie sich vor, Sie schreiben ein Script, das Dateien von einem Computer zum anderen kopiert. Ein solches Script erfordert wahrscheinlich genau zwei Argumente: den Namen des Quellcomputers und den Namen des Zielcomputers. In einem solchen Fall sollten Sie in Ihrem Script als Erstes prüfen, ob die erforderliche Argumentzahl angegeben wurde. Dies können Sie am einfachsten über die Eigenschaft Count der entsprechenden Collection durchführen. Wenn Wscript.Arguments.Count den Wert 2 hat, dann gibt es zwei Argumente. Script 3.10 prüft die Anzahl der übergebenen Argumente. Das Script akzeptiert bis zu vier Argumente - zwei von diesen sind optional. Daher muss der Benutzer mindestens zwei und höchstens vier Argumente angeben. Script 3.10: Überprüfen der angegebenen Argumente 1iNumberOfArguments = WScript.Arguments.Count 2If iNumberOfArguments >= 2 And iNumberOfArguments <= 4 Then 3 Wscript.Echo iNumberOfArguments & " arguments entered. " & _ 4 "Gültige Argumentzahl."
Seite 140 von 394
5Else 6 Wscript.Echo "Fehler: Falsche Argumentzahl angegeben. " & _ 7 "Bitte geben Sie 2, 3 oder 4 Argumente an." 8 Wscript.Quit 9End If
In Zeile 1 des Scripts wird über die Eigenschaft WScript.Arguments.Count festgestellt, wie viele Argumente angegeben wurden. Das Script speichert diese Zahl in der Variable iNumberOfArguments. In Zeile 2 verwendet das Script die Bedingung iNumberOfArguments =2 And iNumberOfArguments =4 um zu prüfen, ob der Benutzer 2, 3 oder 4 Argumente angegeben hat: • •
Wenn die Bedingung Wahr (True) ist, dann fährt das Script mit Zeile 3 fort und gibt einen Text aus, der anzeigt, dass die Argumentzahl gültig war. Wenn die Bedingung Falsch (False) ist, dann fährt das Script mit Zeile 6 fort und gibt einen Text aus, der anzeigt, dass die Argumentzahl ungültig war.
Die folgende Befehlszeile führt das Script mit mehr als vier Argumenten aus: CheckNumArgs.vbs HRServer01 RASServer01 SQLServer01 1000 300 Das Script produziert die folgende Ausgabe: Fehler: Falsche Argumentzahl angegeben. Bitte geben Sie 2, 3 oder 4 Argumente an.'
Diese Befehlszeile führt das Script mit drei Argumenten aus: CheckNumArgs.vbs HRServer01 1000 300 Das Script produziert die folgende Ausgabe: Gültige Argumentzahl.
Überprüfen, ob alle erforderlichen Argumente angegeben wurden Wenn Sie Argumente in Ihrem Script verwenden, dann sollte das Script überprüfen, ob alle erforderlichen Argumente angegeben wurden. Unglücklicherweise gibt es keine Möglichkeit WSH mitzuteilen, dass ein bestimmtes Argument zwingend erforderlich ist. Sie müssen sich also selbst darum kümmern, dass die erforderlichen Argumente angegeben werden. Wenn bestimmte Argumente für Ihr Script erforderlich sind, dann sollten Sie benannte Argumente verwenden. Ein Vorteil von benannten Argumenten ist, dass Sie mit der Methode Exists prüfen können, ob das Argument angegeben wurde. Die folgende Codezeile prüft, ob das Argument mit dem Namen FolderName angegeben wurde. Wenn es vorhanden ist, dann wird der Wert-1 (True) ausgeben - andernfalls lautet die Ausgabe 0 (False): Wscript.Echo colNamedArguments.Exists("FolderName")
Script 3.11 baut auf Script 3.10 auf. In Zeile 4 verwendet das Script die Bedingung Not colNamedArgument.Exists("Server") um zu testen, ob der Benutzer das benannte Argument Server angegeben hat. •
Wenn der Test feststellt, dass das Argument nicht angegeben wurde, fährt das Script mit Zeile 5 fort. Dort wird eine Nachricht ausgegeben, die dem Benutzer mitteilt, dass das Argument Server fehlt. In Zeile 6 wird der Befehl Wscript.Quit verwendet, um das Script Seite 141 von 394
anzuhalten. •
Wenn der Test feststellt, dass das Argument angegeben wurde, fährt das Script mit Zeile 7 fort. In den Zeilen 9 und 10 wird der Befehl colNamedArguments.Item("Server") verwendet, um den Wert des Argumentes Server abzufragen. Dann zeigt das Script eine Nachricht an.
Script 3.11: Überprüfen, ob alle erforderlichen Argumente angegeben wurden 1 iNumberOfArguments = WScript.Arguments.Count 2 Set colNamedArguments = WScript.Arguments.Named 3 4 If Not colNamedArguments.Exists("Server") Then 5 Wscript.Echo "Verwendung: /Server:<servername> ist 6 erforderlich." 7 Wscript.Quit 8 ElseIf iNumberOfArguments >= 2 Or iNumberOfArguments <= 4 Then 9 Wscript.Echo iNumberOfArguments & " Argumente angegeben" 10 Wscript.Echo "inklusive Servername: " & _ 11 colNamedArguments.Item("Server") 12Else 13 Wscript.Echo "Verwendung: Geben Sie 2 bis 4 Argumente an." 14 Wscript.Quit End If
Die folgende Befehlszeile versucht das Script ohne das Argument Server auszuführen: CheckReqArgs.vbs 1000 300 Das Script produziert die folgende Ausgabe: Verwendung: /Server:<servername> ist erforderlich.
Die nächste Befehlszeile startet das Script mit dem erforderlichen Argument: CheckReqArgs.vbs /Server:HRServer01 1000 300 Das Script produziert die folgende Ausgabe: 3 Argumente angegeben Inklusive Servername: HRServer01
Die Scriptausführung steuern Wenn Sie ein Script ausführen, werden die Befehle im Script normalerweise von oben nach unten ausgeführt. Es gibt keine Pausen, und das Script ist erst beendet, wenn die letzte Befehlszeile abgearbeitet wurde. Das Objekt WScript stellt zwei Eigenschaften (Timeout und Interactive) und zwei Methoden (Sleep und Quit) zur Verfügung, über die Sie die Scriptausführung kontrollieren können. Sie haben folgende Möglichkeiten: • • •
Das Script für einen bestimmen Zeitraum anhalten. Die Scriptausführung sofort beenden. Die Scriptausführung nach einer bestimmen Zeitspanne beenden.
Ein Script anhalten
Seite 142 von 394
Normalerweise führt WSH ein Script so schnell wie möglich aus. Meist ist dies auch das gewünschte Verhalten - je schneller das Script beendet ist, desto besser. Es gibt jedoch auch Fälle, in denen Sie nicht möchten, dass das Script so schnell wie möglich ausgeführt wird. Zum Beispiel dann, wenn Sie zum Beispiel alle 10 Sekunden die Prozessorauslastung abfragen möchten - und zwar so lange, bis 100 Messungen vorgenommen wurden. Sie können die Ausführung eines Scripts mit der Methode Sleep des Objekts WScript für einen bestimmen Zeitraum anhalten. Die Methode erwartet als Parameter den Zeitraum in Millisekunden, für den das Script angehalten werden soll (eine Sekunde hat 1.000 Millisekunden und eine Minute hat 60.000 Millisekunden). In folgenden Situationen können Sie die Methode Sleep verwenden: • • •
Wenn Sie auf ein bestimmtes Ereignis warten müssen. Wenn Sei möchten, dass Ihr Script einen bestimmten Status in regelmäßigen Zeitabständen prüft (zum Beispiel alle 10 Sekunden die Prozessorauslastung). Wenn Sie die Interaktion mit dem Benutzer auf ein vernünftiges Maß herunterbremsen möchten. Wenn Sie zum Beispiel die Einträge des Ereignisprotokolls anzeigen lassen, möchten Sie möglicherweise, dass eine kurze Pause zwischen den einzelnen Ausgaben liegt.
Script 3.12 prüft den freien Festplattenplatz aller Laufwerke des Computers. Dann wartet das Script 5 Minuten (300.000 Millisekunden) und prüft wiederum den freien Festplattenplatz. Script 3.12: Das Script mit der Methode Sleep anhalten 1 strComputer = "." 2 Set objWMIService = GetObject("winmgmts:" _ 3 & "{impersonationLevel=impersonate}!\\" & strComputer & 4 "\root\cimv2") 5 Do While True 6 Set colDisks = objWMIService.ExecQuery _ 7 ("SELECT * FROM Win32_LogicalDisk") 8 For Each objDisk in colDisks 9 Wscript.Echo "DeviceID: " & objDisk.DeviceID 10 Wscript.Echo "Freier Festplattenplatz: " & objDisk.FreeSpace 11 Next 12 Wscript.Sleep 300000 Loop
Anmerkung: Das Script wird unendlich ausgeführt. Um es zu beenden, müssen Sie den Prozess beenden, unter dem das Script ausgeführt wird (WScript.exe oder CScript.exe). Ein Script beenden Im Allgemeinen wird ein Script erst dann beendet, wenn die letzte Zeile ausgeführt wurde. Es mag jedoch Situationen geben, in denen Sie das Script abbrechen möchten, bevor die letzte Scriptezeile ausgeführt wurde. Zum Beispiel, wenn Sie ein Script schreiben, das zwei Argumente benötigt. Wenn der Benutzer nur ein Argument angibt, soll das Script abgebrochen werden.
Seite 143 von 394
Sie können ein Script über die Methode WScript.Quit beenden. Mit der Quit-Methode wird das Script sofort beendet. Das folgende Beispielscript gibt zum Beispiel drei Nachrichten aus und wird dann beendet. Die letzten drei Befehle werden niemals ausgeführt: Wscript.Echo Wscript.Echo Wscript.Echo Wscript.Quit Wscript.Echo Wscript.Echo Wscript.Echo
"1" "2" "3" "4" "5" "6"
Einen Time-Out-Wert für ein Script verwenden Auch wenn eigentlich jedes Script einen Time-Out-Wert verwenden sollte, ist dies ganz besonders für Scripte wichtig, die möglicherweise unendlich ausgeführt werden können. Stellen Sie sich zum Beispiel vor, ein Script muss sich als Erstes mit einem Remotecomputer verbinden. Was passiert, wenn diese Verbindung nicht aufgebaut werden kann? Das Script könnte zum Beispiel 60 Sekunden warten und einen neuen Verbindungsversuch starten. Dies könnte das Script so lange fortführen, bis die Verbindung zustande kommt. Was aber, wenn der Zielcomputer permanent aus dem Netzwerk entfernt wurde? In diesem Fall wird das Script theoretisch für immer ausgeführt. Hier könnte es also praktisch sein, einen Time-Out-Wert zu definieren. Wenn eine bestimmte Aufgabe im definierten Zeitraum nicht ausgeführt werden kann, dann soll das Script beendet werden. Die Eigenschaft WScript.Timeout definiert einen solchen Zeitraum in Sekunden (standardmäßig wird das Script unendlich ausgeführt). Script 3.13 setzt den Time-Out auf 5 Sekunden. Wenn das Script also nach fünf Sekunden nicht von selbst beendet wurde, wird es automatisch beendet. Da im Script in Zeile 2 eine Pause von 60 Sekunden definiert ist, wird es immer durch den Time-Out abgebrochen. Zeile 3 wird also niemals ausgeführt. Script 3.13: Verwendung des Time-Out in einem Script 1Wscript.Timeout = 5 2Wscript.Sleep 60000 3Wscript.Echo "Script is finished."
WSH-Umgebungsvariablen abfragen Das Objekt WScript stellt über einige Eigenschaften Informationen zur Verfügung, die Ihnen zum Beispiel mitteilen, welcher Script Host verwenden wurde und welches Script ausgeführt wurde. Diese Metadaten sehen Sie in Tabelle 3.7. Tabelle 3.7: WSH-Umgebungsinformationen Eigenschaft
Beschreibung
ScriptFullNameVollständiger Pfad des aktiven Scripts (zum Beispiel C:\Scripts\Monitor.vbs). ScriptName
Dateiname des aktiven Scripts (zum Beispiel Monitor.vbs).
Version
Die WSH-Version (zum Beispiel 5.6).
Build
Die WSH-Buildnummer. Die vollständige Versionsnummer des Windows Script Hosts setzt sich aus der Versionsnummer Seite 144 von 394
Eigenschaft
Beschreibung
und der Build-Versionsnummer zusammen. Wenn die Windows Script-Host-Versionsnummer zum Beispiel 5.6 und die Buildversion 6626 ist, dann ist die vollständige Versionsnummer 5.6.6626. Name
Gibt immer den String Windows Script Host zurück.
FullName
Pfad und Name des Script Hosts (entweder Wscript.exe oder CScript.exe).
Path
Der vollständige Pfad des Ordners, in dem sich der Script Host befindet - ohne Dateiname.
Diese Metadaten können sehr nützlich sein. Über die Versionsnummer können Sie zum Beispiel feststellen, ob die korrekte WSH-Version installiert ist. Wenn Ihr Script beispielsweise die WSH 5.6 voraussetzt, können Sie dies über den folgenden Scriptcode testen: If Wscript.Version <> "5.6" Then Wscript.Echo "Dieses Script muss unter WSH 5.6. ausgeführt werden." Wscript.Quit End If
Oder stellen Sie sich vor, Ihr Script führt eine große Menge an Ausgaben über Wscript.Echo durch. In diesem Fall möchten Sie möglicherweise nicht, dass das Script unter WScript ausgeführt wird - dies könnte zu hunderten von Nachrichtenfenstern führen, die alle per Hand geschlossen werden müssten. Sie können die verwendete Script Host-Version über die Eigenschaft FullName abfragen. Es reicht allerdings aus, die letzten 11 Zeichen des Wertes zu überprüfen (um sicherzustellen, dass die Überprüfung funktioniert, sollten Sie die Zeichen mit der Funktion UCase in Großbuchstaben umwandeln). Wenn die letzten 11 Zeichen 'WSCRIPT.EXE' sind, wird das Script unter WScript ausgeführt - bei 'CSCRIPT.EXE' handelt es sich beim Script Host um CScript. Anmerkung: Warum nur die letzten 11 Zeichen? Der Wert von FullName hängt davon ab, wo der WSH installiert wurde - er könnte "C:\Winnt\System32\CScript.exe" oder auch "E:\Windows\System32\Wscript.exe" sein. Die letzten 11 Zeichen enthalten jedoch immer den Text 'Wscript.exe' oder 'Cscript.exe'. Das folgende Codestück prüft, welcher Script Host installiert ist und beendet das Script, wenn es sich im WSCRIPT.EXE handelt: If UCase(Right(Wscript.FullName, 11)) = "WSCRIPT.EXE" Then Wscript.Echo "Dieses Script muss unter CScript ausgeführt werden." Wscript.Quit End If
Script 3.14 zeigt alle verfügbaren WSH-Umgebungsinformationen an. Script 3.14: Anzeigen der verfügbaren WSH-Umgebungsinformationen 1Wscript.Echo 2Wscript.Echo 3Wscript.Echo 4Wscript.Echo 5Wscript.Echo
"Script Full Name: " & Wscript.ScriptFullName "Script Name: " & Wscript.ScriptName "Version: " & WScript.Version "Build: " & Wscript.BuildVersion "Name: " & Wscript.Name
Seite 145 von 394
6Wscript.Echo "Full Name: " & Wscript.FullName 7Wscript.Echo "Path: " & Wscript.Path
Die Ausgabe des Scripts sieht so oder ähnlich aus: Script Full Name: C:\test.vbs Script Name: test.vbs Version: 5.6 Build: 8515 Name: Windows Script Host Full Name: C:\WINDOWS\system32\cscript.exe Path: C:\WINDOWS\system32
Auf Ereignisse reagieren Windows ist ein ereignisbasiertes Betriebssystem. Die Ereignisse, die unter Windows generiert werden, sind oftmals das Ergebnis einer Benutzeraktion - zum Beispiel das Klicken auf einen OK-Schalter oder eine Bewegung der Maus. Die meisten Windows-Programme reagieren auf diese Ereignisse. Wenn Sie zum Beispiel eine Anwendung wie Microsoft Word starten, dann wird die Anwendung geladen und wartet darauf, dass Sie etwas tun (zum Beispiel etwas eingeben). Wenn Sie kein Ereignis auslösen, auf das reagiert werden kann, dann wartet Word unendlich. Außerdem ermöglicht dieser Ereignismechanismus verschiedenen Softwarekomponenten miteinander zu kommunizieren. Wenn ein Ereignis in einer Komponente auftritt (auslösende Komponente), dann wird eine andere Komponente (reagierende Komponente) davon benachrichtigt. Die reagierende Komponente kann dann die notwendige Aktion ausführen. Die Ereignismechanismen des WSH werden in gewöhnlichen administrativen Scripten normalerweise nicht verwendet. Diese Scripte sind meist prozedurgesteuert. Das bedeutet, wenn Sie einmal gestartet wurden, laufen sie einfach weiter - und zwar ohne auf äußere Ereignisse zu reagieren. Anmerkung: Die Möglichkeit Ressourcen zu überwachen und auf deren Ereignisse zu reagieren ist sehr wichtig. Eine solche Ereignisüberwachung führen Sie jedoch am besten über WMI durch. Außerdem kann die Ereignisüberwachung für die Automatisation von GUIAnwendungen wichtig sein. Scripte können zum Beispiel den Internet Explorer als Benutzerschnittstelle verwenden. In diesem Fall sollte das Script auch auf Ereignisse des Internet Explorers reagieren können. Weitere Informationen zu diesem Thema finden Sie im Kapitel Creating Enterprise Scripts dieses Buches.
Das Objekt WshShell Die Shell ist die Windows-Komponente, die die Benutzerschnittstelle zur Verfügung stellt. Sie ist für Elemente wie den Desktop, den Windows Explorer, das Startmenü, Verknüpfungen und Desktop-Schemata zuständig. Das Objekt WshShell gibt Ihnen die Möglichkeit mit der Windows-Shell zu arbeiten. Ihre Scripte können über das Objekt eine Vielzahl von administrativen Aufgaben durchführen inklusive Programme ausführen, Informationen in der Registrierung lesen und schreiben und Erstellen von Verknüpfungen. Die Methoden und Eigenschaften des WshShell-Objekts sehen Sie in Abbildung 3.10. Seite 146 von 394
Abbildung 3.10: Das WshShell-Objektmodell Auf das WshShell-Object zugreifen Das WshShell-Object ist ein COM-Objekt. Mit der folgenden Codezeile können Sie eine Instanz des Objekts erstellen: Set objShell = WScript.CreateObject("WScript.Shell")
Funktionalitäten von WshShell Das WshShell-Objekt ermöglicht Ihrem Script die Automatisierung von Windows-Shellbezogenen Aufgaben. In Tabelle 3.8 sehen Sie die Methoden und Eigenschaften des Objekts. Tabelle 3.8: Methoden und Eigenschaften des WshShell-Objekts Seite 147 von 394
Kategorie
Methode oder Eigenschaft
Programme ausführen
Run, Exec
Arbeiten mit Spezialordnern
SpecialFolders
Arbeiten mit Verknüpfungen
CreateShortcut
Arbeiten mit Umgebungsvariablen
Environment, ExpandEnvironmentStrings
Arbeiten mit dem Ereignisprotokoll
LogEvent
Arbeiten mit der Registrierung
RegRead, RegWrite, RegDelete
Tastendrücke an Anwendungen sendenAppActivate, SendKeys Abfragen des aktuellen Verzeichnisses des Scripts
CurrentDirectory
Zeitgesteuerte Dialogfenster erstellen Popup
Programme ausführen Eine wichtige Lektion, die Sie über Scripting lernen müssen ist: Scripting ist nicht die Antwort auf Ihre gesamten administrativen Anforderungen. Erstens deckt Scripting nicht 100 Prozent aller administrativen Aufgaben ab - Sie sind zum Beispiel nicht in der Lage, die Offline-Eigenschaften von Ordnern festzulegen. Eine solche Aufgabe müssen Sie weiterhin über die GUI oder über Net.exe durchführen. Und zweitens gibt es keinen Grund ein Script zu schreiben, wenn es bereits ein Tool gibt, das Ihren Bedürfnissen entspricht. Nehmen wir an, Sie benötigen eine Liste der in einem Ordner auf dem lokalen Computer gespeicherten Dateien. Sie können entweder viel Zeit aufwenden, um ein entsprechendes Script zu schreiben - oder Sie können den Befehl dir eingeben. WSH-Scripte können zwar Aufgaben automatisieren, sie ersetzten jedoch nicht alle Kommandozeilentools und Batchdateien, die Sie bis jetzt verwendet haben. Wenn Sie eine Batchdatei für eine Aufgabe haben, dann gibt es keinen Grund ein Script zu erstellen, das die gleiche Aufgabe ausführt. Andererseits möchten Sie möglicherweise die Fähigkeiten einer Batchdatei mit den Möglichkeiten der WSH-Scriptumgebung kombinieren. Stellen Sie sich zum Beispiel vor, dass Sie ein Programm zum Aufräumen der Festplatte nur dann ausführen wollen, wenn der freie Platz auf der Festplatte unter einen bestimmten Wert sinkt. Dies ist mit einer Batchdatei schwer möglich - mit einem WSH-Script jedoch ganz einfach. Den freien Festplattenplatz können Sie über das Objekte FileSystemObject oder über WMI abfragen und dann das Programm zum Aufräumen der Festplatte starten. Ein Vorteil des WSH ist es, dass Sie sich nicht zwischen WSH oder Batchdatei und Kommandozeilentools entscheiden müssen. Mit dem WSH schließen sich die beiden Ansätze nicht gegenseitig aus - stattdessen ergänzen sie sich gegenseitig. Sie können zum Beispiel die Methoden Run und Exec des Objekts WshShell verwenden, um Batchdateien oder Kommandozeilentools auszuführen.
Seite 148 von 394
Die beiden Methoden sind nicht auf Batchdateien oder Kommandozeilentools eingeschränkt. Sie können auf diese Art jede beliebige ausführbare Datei aus Ihrem Script heraus starten. Unterschiede zwischen Run und Exec Es gibt zwei Wege, Programme aus Ihrem Script heraus zu starten - welchen sollen Sie verwenden? Die Antwort zu dieser Frage hängt von Ihrem Script und von dem, was Sie mit dem Script erreichen wollen, ab. Sie verwenden die Methoden Run und Exec fast genauso wie Sie ein Programm über das Dialogfenster Ausführen im Startmenü ausführen. Egal welche Methode Sie verwenden: Das Programm wird in einem neuen Prozess gestartet. Wenn Sie die Methode Run verwenden, hat Ihr Script jedoch keinen Zugriff auf die Standard-Eingabe-, -Ausgabe- und -Fehlerkanäle des gestarteten Programms. Stellen Sie sich zum Beispiel vor, Sie möchten ping.exe starten und seine Ausgaben in Ihrem Script weiter verarbeiten. Über Run ist dies nicht möglich. Sie müssten stattdessen ping.exe starten, dessen Ausgaben in einer Datei speichern und dieses Datei mit Ihrem Script einlesen. Das folgende Script verwendet die Methode Run, um ping.exe zu starten. Die Ausgaben von ping.exe werden in eine temporäre Datei umgeleitet. Das Script öffnet dann diese Textdatei, prüft, ob der Ping-Befehl erfolgreich ausgeführt wurde (ob Zeilen der Ausgabe mit dem Wort 'Antwort' beginnen) und löscht die Datei dann: Set objFSO = Wscript.CreateObject("Scripting.FileSystemObject") Set objShell = Wscript.CreateObject("Wscript.Shell") objName = objFSO.GetTempName objTempFile = objName objShell.Run "cmd /c ping -n 3 -w 1000 157.59.0.1 >" & objTempFile, 0, True Set objTextFile = objFSO.OpenTextFile(objTempFile, 1) Do While objTextFile.AtEndOfStream <> True strText = objTextFile.ReadLine If Instr(strText, "Antwort") > 0 Then Wscript.Echo "Antwort erhalten." Exit Do End If Loop objTextFile.Close objFSO.DeleteFile(objTempFile)
Auch wenn dieser Ansatz durchaus funktioniert, ist er doch etwas kompliziert. Wenn Sie auf die Ausgaben eines Programms zugreifen müssen, dann sollten Sie die Methode Exec verwenden. Auch das folgende Script interpretiert die Ausgaben von ping.exe. Es verwendet hierzu jedoch die Methode Exec. So kann es die Ausgaben direkt einlesen - es muss keine temporäre Datei erstellen, öffnen, lesen und löschen. Das Script ist so nur noch 9 Zeilen statt 15 Zeilen lang: Set objShell = WScript.CreateObject("WScript.Shell") Set objExecObject = objShell.Exec("cmd /c ping -n 3 -w 1000 157.59.0.1") Do While Not objExecObject.StdOut.AtEndOfStream strText = objExecObject.StdOut.ReadLine() If Instr(strText, "Antwort") > 0 Then Wscript.Echo "Antwort erhalten." Exit Do End If Loop
In vielen Fällen ist die Methode Exec die bessere Wahl. Aber auch die Run-Methode ist in einigen Situationen ganz nützlich: Seite 149 von 394
•
• •
Wenn Sie eine Anwendung in einem bestimmten Fenstertyp, zum Beispiel einem minimierten Fenster, öffnen möchten, dann stellt Ihnen Run die in Tabelle 3.9 zu sehenden Optionen zur Verfügung. Wenn Sie ein Script auf einem Computer ausführen müssen, auf dem der WSH 5.6 nicht installiert ist. Exec wird erst ab WSH 5.6 unterstützt. Wenn Sie vor der weiteren Scriptausführung warten möchten, bis die gestartete Anwendung wieder beendet wurde. Dies ist zwar auch über Exec möglich, mit Run ist es jedoch einfacher.
Ausführen von Programmen Die Methode Run verwendet drei Parameter. Nur der erste Parameter ist jedoch zwingend erforderlich - er definiert den Namen des Programms, das Sie ausführen möchten. Wenn sich das Programm im gleichen Ordner wie das Script befindet, reicht der Name der Programmdatei aus (zum Beispiel calc.exe). Andernfalls müssen Sie den vollständigen Pfad angeben (zum Beispiel C:\Admin\Monitoring\DiskSpace.exe). Beim zweiten Parameter handelt es sich um einen Integer-Wert. Dieser legt den Fensterstil fest, mit dem das Programm gestartet wird (wenn das Programm in einem Fenster ausgeführt wird). Die möglichen Werte dieses Parameters sehen Sie in Tabelle 3.9. Tabelle 3.9: Werte für den Parameter Window-Stil der Methode Run WertBeschreibung
0
Verstecktes Fenster
1
Aktiviert das Fenster und zeigt es an. Wenn das Fenster minimiert oder maximiert ist, werden Originalgröße und -position des Fensters wiederhergestellt.
2
Aktiviert das Fenster und zeigt es minimiert an.
3
Aktiviert das Fenster und zeigt es maximiert an.
4
Zeigt das Fenster mit seiner letzten Größe und Position an. Das aktive Fenster bleibt aktiv.
5
Aktiviert das Fenster und zeigt es mit seiner aktuellen Größe und Position an.
6
Minimiert das Fenster und aktiviert das nächste Fenster in Z-Reihenfolge. Bei der Z-Reihenfolge handelt es sich um eine Liste, in der die Fenster aktiviert werden. Sie können die Liste sehen, wenn Sie ALT+TAB drücken.
7
Zeigt das Fenster minimiert an. Das aktive Fenster bleibt aktiv.
8
Zeigt das Fenster in seinem Zustand an. Das aktive Fenster bleibt aktiv.
9
Aktiviert das Fenster und zeigt es an. Wen das Fenster minimiert oder maximiert ist, dann werden seine Originalgröße und -position wiederhergestellt.
10 Setzt den Anzeigezustand des Fensters auf Basis des Seite 150 von 394
WertBeschreibung
Anzeigezustands des Programms, das die Anwendung gestartet hat. Script 3.15 startet Notepad. In Zeile 1 definiert das Script die Konstante MAXIMIZE_WINDOW mit dem Wert 3 (aktiviert und maximiert). In Zeile 3 verwendet das Script die Methode Run des Objekts WshShell, um Notepad zu starten. Hierbei übergibt es den Wert der Konstante MAXIMIZE_WINDOW als zweiten Parameter. Script 3.15: Ein Programm über die Methode Run starten 1Const MAXIMIZE_WINDOW = 3 2Set objShell = WScript.CreateObject("WScript.Shell") 3objShell.Run "notepad.exe", MAXIMIZE_WINDOW
Anmerkung: Der Parameter Windows-Stil wird nicht von allen Anwendungen verarbeitet. Die Systemsteuerung (control.exe) wird zum Beispiel immer in der gleichen Form geöffnet egal welcher Fensterstil im Script definiert wurde. Die Methode Run akzeptiert noch einen dritten Parameter. Dieser muss ein Boolean-Wert (also nur True oder False) sein. Wenn der Wert False ist (die Standardeinstellung), dann kümmert sich Run nicht darum, ob das Programm ausgeführt wird. Wenn der Wert auf True gesetzt ist, dann wartet das Script darauf, dass das Programm beendet wurde. Wenn Sie den Wert auf False setzen, dann können Sie also gleich mehrere Programme starten. Das folgende Script führt den Windows-Taschenrechner aus und wartet dann, bis dieser beendet wurde. Erst dann wird die nächste Zeile ausgeführt. Wenn der Taschenrechner nicht geschlossen wird, dann wird Zeile 3 nicht ausgeführt: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("calc.exe"),1,True Wscript.Echo "Script completed."
Anmerkung: Der WSH verfolgt die gestarteten Instanzen von Programmen. Wenn Sie den Taschenrechner zum Beispiel über ein Script starten, und dann den Taschenrechner noch einmal per Hand starten, dann wird das Script erst dann weiter ausgeführt, wenn Sie die vom Script gestartete Instanz des Taschenrechners beenden. Ausführen von Kommandozeilentools Das Ausführen von Kommandozeilentools ist sowohl über Run als auch über Exec möglich in diesem Fall müssen Sie jedoch eine leicht andere Syntax als bei GUI-Programmen verwenden. Sie müssen den Kommandozeilentools einen der folgenden Werte voranstellen: • •
%comspec% /k %comspec% /c
Bei %comspec% handelt es sich um eine Umgebungsvariable, die den Kommandozeilenprozessor zurückgibt. Mit ihr können Sie Scripte erstellen, die sowohl unter Windows 98 (hier ist der Kommandozeilenprozessor command.exe) und unter Windows 2000 (hier ist der Kommandozeilenprozessor cmd.exe) ausgeführt werden können. Sie müssen die Variable %comspec% nicht verwenden - sie bietet jedoch den Vorteil, dass das Kommandozeilenfenster nach der Ausführung des Tools geöffnet bleibt (ohne dass Sie den Kommandozeilenprozessor mit angeben wird das Fenster geöffnet, das Tool wird Seite 151 von 394
ausgeführt, und das Fenster wird wieder geschlossen - Sie haben also nicht viel Zeit, die Ausgaben zu lesen). Außerdem ist die Variable %comspec% der einzige Weg, Kommandozeilenbefehle wie zum Beispiel dir zu verwenden (dies liegt daran, dass dir kein Standardtool ist - es gibt kein Programm mit dem Namen dir.exe). Das folgende Script produziert daher nur einen Fehler: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("dir"), 1, True
Dieses Script startet als Erstes den Kommandointerpreter und führt den dir-Befehl dann im Kommandointerpreter aus: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("%comspec% /K dir"), 1, True
Über die Paramter /k und /c können Sie angeben, dass das Kommandozeilenfenster nach der Scriptausführung geöffnet bleibt (/k) oder geschlossen wird (/c). Das folgende Script startet zum Beispiel das Tool Cacls.exe. Dies zeigt in diesem Fall die Berechtigung des Ordners C:\Scripts an. Das Script sorgt dafür, dass das Fenster geöffnet bleibt, so dass Sie die Ausgaben lesen können: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("%comspec% /c sc.exe stop alerter"), 1, True
Das folgende Script startet das Tool Sc.exe und hält den Warndienst an. Sobald das Script ausgeführt wurde, wird auch das Fenster wieder geschlossen: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("%comspec% /k sc.exe getkeyname "Upload Manager""), 1, True
Leerzeichen in Kommandozeilenparametern Wenn die an ein Kommandozeilentool übergebenen Parameter Leerzeichen enthalten, dann müssen diese Parameter in Anführungszeichen eingeschlossen werden. Wenn Sie zum Beispiel das Tool Sc.exe zum Beenden des Dienstes Upload Manager verwenden möchten, dann müssen Sie die folgende Syntax verwenden: sc.exe getkeyname "Upload Manager" Um denselben Befehl in einem Script auszuführen, müssen Sie ebenfalls den Parameter in Anführungszeichen einschließen. Dies ist jedoch nicht ganz so einfach. Wenn Sie zum Beispiel versuchen die folgende Codezeile zu verwenden, dann erhalten Sie einen Fehler: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("%comspec% /k sc.exe getkeyname "Upload Manager"), 1, True
Die Fehlermeldung sieht so aus:
Abbildung 3.11: Falsche Angabe der Parameter Seite 152 von 394
Diese Fehlermeldung scheint erst einmal unsinnig - sie sagt, es würde die schließende Klammer fehlen. Im Scriptcode ist jedoch eine schließende Klammer vorhanden. Wie sich herausstellt, liegt das wahre Problem nicht in der Klammer, sondern in den Anführungszeichen. Der WSH sieht das erste Anführungszeichen (das sich direkt vor %comspec% befindet) als Beginn des Kommandostrings der der Methode Run übergeben werden soll. Das zweite Anführungszeichen markiert für den WSH logischerweise das Ende des Kommandostrings. Alles dazwischen ist für den WSH der Befehl, den Sie ausführen möchten. Die Zeile sieht für den WSH also so aus: objShell.Run("%comspec% /k sc.exe getkeyname "
Da diese Syntax natürlich nicht korrekt ist (die rechte Klammer fehlt ja tatsächlich) kommt es zur Fehlermeldung. Wenn Sie Anführungszeichen in einem String verwenden möchten, müssen Sie jedes Mal zwei Anführungszeichen schreiben: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("%comspec% /k sc.exe getkeyname ""Upload Manager"""), 1, True
Ein Programm ausführen und auf dessen Ausgaben zugreifen Um ein Programm auszuführen und auf dessen Ausgaben zuzugreifen, verwenden Sie die Exec-Methode von WshShell. Sie gibt ein Objekt zurück, das Ihnen einen Zugriff auf die Standard-Eingabe-, -Ausgabe- und -Fehlerkanäle des Programms ermöglicht. Script 3.16 führt das Kommandozeilentoll ipconfig.exe aus. Dieses Tool fragt Informationen über die Netzwerkkonfiguration des Computers ab - inklusive der aktuellen IP-Adresse des Computers. Das Script fragt dann die Ausgaben des Tools über dessen Ausgabekanal ab und geht diese Ausgabe zeilenweise durch. Wenn es in einer Zeile das Wort 'Adresse' findet, gibt es die Zeile aus. Script 3.16: Ausführen einer Anwendung und Zugriff auf deren Ausgabe 1 Set objShell = WScript.CreateObject("WScript.Shell") 2 Set objExecObject = objShell.Exec("%comspec% /c ipconfig.exe") 3 4 Do Until objExecObject.StdOut.AtEndOfStream strLine = objExecObject.StdOut.ReadLine() 5 6 strIP = Instr(strLine,"Adresse") 7 If strIP <> 0 Then Wscript.Echo strLine 8 9 End If 10Loop
In Zeile 2 verwendet das Script die Methode Exec, um das Kommandozeilentool ipconfig.exe auszuführen. In der Variable objExecObject wird eine Referenz auf das Tool gespeichert. In Zeile 4 nimmt das Script in einer Schleife die einzelnen Zeilen der Ausgabe des Tools entgegen - und zwar so lange, bis das Ende der Ausgabe erreicht ist. In Zeile 6 verwendet das Script die Funktion Instr von VBScript, um zu prüfen ob in der aktuellen Zeile das Wort 'Adresse' enthalten ist. Wenn das Wort gefunden wurde, dann wird die Zeile über die Methode Echo ausgegeben. Wenn Sie ipconfig.exe in einer Kommandozeile alleine ausführen, erhalten Sie eine Ausgabe wie diese: Windows-IP-Konfiguration
Seite 153 von 394
Ethernetadapter VMware Network Adapter VMnet8: Verbindungsspezifisches DNS-Suffix: IP-Adresse. . . . . . . . . . . . : 192.168.184.1 Subnetzmaske. . . . . . . . . . . : 255.255.255.0 Standardgateway . . . . . . . . . : 192.168.184.100
Wenn Sie das Script ausführen sieht die Ausgabe so aus: IP-Adresse. . . . . . . . . . . . : 192.168.184.1
Arbeiten mit Verknüpfungen Verknüpfungen sind Links zu Programmen, Dateien, Ordnern, Computern oder Internetadressen. Eine Verknüpfung ist eine Datei mit der Endung .lnk oder .url. Diese beiden Dateierweiterungen werden für Standardverknüpfungen (lnk) und URLs (Uniform Resource Locator) verwendet. Verknüpfungen werden an vielen Stellen verwendet (Shell, Menüs, Werkzeugleisten, usw.). Das Startmenü besteht zum Beispiel aus Verknüpfungen - und die Verknüpfungen aus der Schnellstartleiste werden im folgenden Ordner gespeichert: C:\Dokumente und Einstellungen\{Name des Benutzerprofils}\Anwendungsdaten\Microsoft\Internet Explorer\Quick Launch Ihre Scripte können den Desktop und die Menüs eines Benutzers über Verknüpfungen anpassen. Sie können zum Beispiel ein Script erstellen, das unterschiedlichen Benutzergruppen unterschiedliche Menüoptionen zur Verfügung stellt. In Tabelle 3.10 sehen Sie die Eigenschaften des Objekts WshShortcut. Tabelle 3.10: Eigenschaften des Objekts WshShortcut Eigenschaft
Beschreibung
Arguments
Zusätzliche Kommandozeilenargumente, die Sie beim Start der Anwendung verwenden können.
Description
Beschreibung der Verknüpfung.
FullName
Nur-Lese-Eigenschaft, die den vollständigen Pfad zur Zielanwendung angibt.
HotKey
Tastenkombination: Eine Tastenkombination, über die die Anwendung gestartet werden kann. Tastenkombinationen setzen sich normalerweise aus einer der folgenden Tasten plus einem Buchstaben (a-z), einer Zahl (0-9) oder einer Funktionstaste (F1-F12) zusammensetzen: ALT STRG HOCHSTELLTASTE Wenn Sie die Eigenschaft angeben, müssen Sie für die Taste STRG den Wert CTRL und für die Hochstelltaste den Wert SHIFT angeben. Für die Tastenkombination STRG und 9 verwenden Sie beispielsweise den folgenden Wert: Seite 154 von 394
Eigenschaft
Beschreibung
Ctrl + 9 Wenn die Tastenkombination bereits verwendet wird, dann wird sie überschrieben und der neuen Verknüpfung zugewiesen. IconLocation
Ermöglicht Ihnen die Angabe einen Symbols und eines Symbolindexes für die Verknüpfung. Wenn nichts angegeben wird, dann wird das Standardsymbol der Anwendung verwendet.
TargetPath
Vollständiger Pfad zur Zielanwendung. Sie müssen einen vollständigen Pfad (inklusive des Laufwerkbuchstabens) oder einen UNC-Pfad angeben. Der angegeben Wert wird nicht überprüft.
WindowStyle
Fenstertyp der gestarteten Anwendung. Die möglichen Werte entsprechen den Werten der Methode Run in Tabelle 3.9.
WorkingDirectoryArbeitsverzeichnis der Anwendung. Erstellen von Standardverknüpfungen Zum Erstellen von Standardverknüpfungen sind drei Schritte erforderlich: 1.Eine Instanz des Objekts WshShortcut über die Methode CreateShortcut() erstellen. Der Methode muss der Pfad für die neue Verknüpfungsdatei übergeben werden. Auch wenn Verknüpfungen überall im Dateisystem angelegt werden können, werden sie normalerweise jedoch in Spezialordnern wie AllUsersDesktop und StartMenu angelegt. Spezialordner werden weiter unten in diesem Kapitel besprochen. 2.Festlegen der Eigenschaften des neuen WshShortcut-Objekts. 3.Aufrufen der Methode WshShortcut.Save. Wenn Sie die Save-Methode nicht aufrufen, dann wird die Verknüpfung nicht erstellt. Script 3.17 erstellt eine Verknüpfung zur IIS-Verwaltung (Internetinformationsdienste Internet Information Services) auf dem Desktop. Die Verknüpfung wird für alle Benutzer angezeigt und kann entweder über einen Doppelklick oder über die Tastenkombination STRG-HOCHSTELLTASTE+I geöffnet werden. Script 3.17: Erstellen einer Verknüpfung auf dem Desktop 1Set objShell = WScript.CreateObject("WScript.Shell") 2strDesktopFolder = objShell.SpecialFolders("AllUsersDesktop") 3Set objShortCut = objShell.CreateShortcut(strDesktopFolder & _ 4 "\IIS Manager.lnk") 5objShortCut.TargetPath = "%SystemRoot%\System32\Inetsrv\iis.msc" 6objShortCut.Description = "IIS-Verwaltung starten." 7objShortCut.HotKey = "Ctrl+Shift+I" 8objShortCut.Save
In Zeile 2 wird der Pfad zum Spezialordner Desktop über die Eigenschaft WshShell.SpecialFolders abgefragt und in der Variable strDesktopFolder gespeichert.
Seite 155 von 394
In den Zeilen 3 und 4 wird über die Methode CreateShortcut eine neue Verknüpfungsdatei mit dem Namen IISManager.lnk im Desktopordner erstellt. In den Zeilen 5-7 füllt das Script die Eigenschaften TargetPath, Description und HotKey des WshShortcut-Objekts mit Werten. In Zeile 8 erstellt das Script die Verknüpfung dann über die Methode WshShortcut.Save. Anmerkung: Auch wenn Sie keine Eigenschaften angeben, wird ein Symbol auf dem Desktop erstellt. Hierbei handelt es sich dann jedoch nicht um eine funktionierende Verknüpfung - wenn Sie doppelt auf das Symbol klicken passiert nichts (Sie erhalten nicht einmal eine Fehlermeldung). Für eine funktionierende Verknüpfung müssen Sie mindestens die Eigenschaft TargetPath definieren. URL-Verknüpfungen erstellen Zum Erstellen von URL-Verknüpfungen sind die gleichen drei Schritte erforderlich: 1.Eine Instanz des Objekts WshURLShortcut über die Methode CreateShortcut() erstellen. Der Methode muss der Pfad für die neue Verknüpfungsdatei übergeben werden. 2.Festlegen der Eigenschaften des neuen WshURLShortcut-Objekts. 3.Aufrufen der Methode WshURLShortcut.Save. Script 3.18 erstellt eine Verknüpfung auf dem Desktop, die auf die MSDN®-Website verweist. Diese Verknüpfung ist nur für die Benutzer sichtbar, die das Script ausgeführt haben. Das liegt daran, dass sie im Desktopordner des angemeldeten Benutzers, und nicht im Desktopordner für alle Benutzer erstellt wird. Script 3.18: Erstellen eine URL-Verknüpfung auf dem Desktop 1Set objShell = WScript.CreateObject("WScript.Shell") 2strDesktopFld = objShell.SpecialFolders("Desktop") 3Set objURLShortcut = objShell.CreateShortcut(strDesktopFld & "\MSDN.url") 4objURLShortcut.TargetPath = "http://msdn.microsoft.com" 5objURLShortcut.Save
Ein Element zur Schnellstartleiste hinzufügen Script 3.19 verwendet eine URL-Verknüpfung, um ein Element zur Schnellstartleiste hinzuzufügen, über das die Microsoft® TechNet-Website geöffnet wird. Da die neue Verknüpfung im Schnellstartordner des angemeldeten Benutzers erstellt wird, ist das neue Element nur für die Benutzer sichtbar, die das Script ausgeführt haben. Script 3.19: Ein neues Element in der Schnellstartleiste erstellen 1Set objShell = WScript.CreateObject("WScript.Shell") 2Set colEnvironmentVariables = objShell.Environment("Volatile") 3 4strQLFolder = colEnvironmentVariables.Item("APPDATA") & _ 5 "\Microsoft\Internet Explorer\Quick Launch" 6 7Set objURLShortcut = objShell.CreateShortcut(strQLFolder & "\TechNet.url") 8objURLShortcut.TargetPath = "http://www.microsoft.com/germany/technet" 9objURLShortcut.Save
Seite 156 von 394
Löschen von Verknüpfungen Verknüpfungsdateien können genauso gelöscht werden wie alle anderen Dateien. Das folgende Script löscht zum Beispiel die mit Script 3.19 erstellte Verknüpfung: Set objShell = WScript.CreateObject("WScript.Shell") Set colEnvironmentVariables = objShell.Environment("Volatile") Set objFSO = CreateObject("Scripting.FileSystemObject") strQLFolder = colEnvironmentVariables.Item("APPDATA") & _ "\Microsoft\Internet Explorer\Quick Launch\TechNet.URL" objFSO.DeleteFile(strQLFolder)
Weitere Informationen zur Arbeit mit Dateien in WSH-Scripten finden Sie im Kapitel Script Runtime Primer dieses Buches.
Arbeiten mit Spezialordnern Spezialordner sind Ordner, die es unter allen Windows-Computern gibt (oder zumindest geben könnte) - zum Beispiel Eigene Dateien, Fonts und Startmenü. Es gibt zwei Arten von Spezialordnern: Spezialordner mit Standardordnern und welche ohne. Der Ordner Favoriten befindet sich zum Beispiel in einem Standardordner - der Ordner Arbeitsplatz nicht. In der Collection WScript.SpecialFolders finden Sie die vollständigen Pfade zu allen Spezialordnern, die einen Standardpfad verwenden. In Tabelle 3.11 sehen Sie die Namen und die Inhalte dieser Ordner: Tabelle 3.11: Spezialordner Name
Inhalt
AllUsersDesktop Desktopinhalte, die allen Benutzern angezeigt werden. AllUsersStartMenuStartmenüeinträge, die allen Benutzern angezeigt werden. AllUsersPrograms Einträge im Menü Programme, die allen Benutzern angezeigt werden. AllUsersStartup
Programme, die für alle Benutzer beim Systemstart ausgeführt werden.
Desktop
Desktopinhalte, die nur dem aktuellen Benutzer angezeigt werden.
Favorites
Einträge in den Favoriten des aktuellen Benutzers.
Fonts
Installierte Schriften
MyDocuments
Ordner Meine Dokumente des aktuellen Benutzers.
NetHood
Objekte, die im Ordner Netzwerkumgebung angezeigt werden.
PrintHood
Drucker
Recent
Objekte, die unter dem Menüpunkt Dokumente im Startmenü angezeigt werden (für den aktuellen Benutzer).
SendTo
Optionen im Menü Senden an im Kontextmenü nach einem Rechtsklick im Windows Explorer.
StartMenu
Startmenü des aktuellen Benutzers.
Seite 157 von 394
Name
Inhalt
Startup
Programme, die für den aktuellen Benutzer beim Systemstart ausgeführt werden.
Templates
Anwendungsvorlagen des aktuellen Benutzers.
Anmerkung: Über die Collection SpecialFolders können Sie zwar den Pfad des Ordners abfragen, Sie haben jedoch keine Möglichkeit, diese Ordner oder deren Inhalte zu ändern. Hierzu können Sie zum Beispiel das Objekt FileSystemObject verwenden. Weitere Informationen zu diesem Objekt finden Sie im Kapitel Script Runtime Primer. Den Speicherort von Spezialordner abfragen Script 3.20 fragt den Speicherort des Spezialordners Fonts ab. Dann gibt das Script diesen Speicherort aus. Script 3.20: Abfragen des Speicherortes des Spezialordners Fonts 1Set objShell = WScript.CreateObject("WScript.Shell") 2strFontDirectoryPath = objShell.SpecialFolders.Item("Fonts") 3Wscript.Echo "Pfad zum Ordner Font: " & strFontDirectoryPath
Eine Verknüpfung in einem Spezialordner erstellen Wenn Sie eine Anwendung installieren, dann weist sich diese oft einer Dateierweiterung zu. Wenn Sie zum Beispiel Microsoft® FrontPage® erstellen, weist sich Frontpage der Dateierweiterung .htm zu. Wenn Sie mit der rechten Maustaste auf eine .htm-Datei klicken und Bearbeiten auswählen, dann wird die Datei mit Frontpage geöffnet. Manchmal möchten Sie die .htm-Datei aber vielleicht nur einfach in Notepad anzeigen. Hierzu gibt es unter Windows einen Spezialordner mit dem Namen SendTo (Senden an). In diesem Ordner finden Sie alle Anwendungen, über die Sie über das Kontextmenü im Windows Explorer eine Datei öffnen können. Über den Spezialordner SendTo können Sie diesem Ordner Verknüpfungen hinzufügen. Wenn Sie das nächste Mal eine .htm-Datei mit Notepad öffnen möchten, dann klicken Sie einfach mit der rechten Maustaste auf die Datei und wählen im Kontextmenü Senden an und Notepad aus. Script 3.21 erstellt eine Verknüpfung zur Anwendung Notepad im Spezialordner SendTo. Script 3.21: Notepad zum Menü Senden an hinzufügen. 1Set objShell = WScript.CreateObject("WScript.Shell") 2strSendToFolder = objShell.SpecialFolders("SendTo") 3strPathToNotepad = objShell.ExpandEnvironmentStrings _ 4 ("%SystemRoot%/system32/notepad.exe") 5 6Set objShortcut = objShell.CreateShortcut(strSendToFolder & _ 7 "\notepad.lnk") 8objShortcut.TargetPath = strPathToNotepad 9objShortcut.Save
Umgebungsvariablen Der Windows-Shellprozess verwendet einige Umgebungsvariablen, deren Inhalte Ihnen in Ihrem Script nützen können: Seite 158 von 394
• • • •
Ordner, in denen nach Programmen gesucht wird, wenn Sie in der Eingabeaufforderung nur den Programmnamen angeben. Anzahl der CPUs, CPU-Hersteller und CPU-Architektur. Speicherort des Benutzerprofils. Speicherorte der temporären Ordner.
Wenn Sich ein Benutzer unter Windows anmeldet, dann lädt die Shell die computerspezifischen Umgebungsvariablen aus dem Pfad HKEY_LOCAL_MACHINE und die benutzerspezifischen Umgebungsvariablen aus dem Pfad HKEY_CURRENT_USER der Registrierung. Zusätzlich zu diesen Umgebungsvariablen werden bei jeder Anmeldung noch ProzessUmgebungsvariablen generiert. Tabelle 3.12: Umgebungsvariablen und deren Speicherorte Typ
Beschreibung
Speicherort in der Registrierung
User
Spezifisch für den angemeldeten HKCU\Environment Benutzer. Werden zwischen Ab- und Anmeldung des Benutzers gespeichert.
System Gelten für alle Benutzer des Computers HKLM\System\CurrentControlSet\ und werden zwischen An- und Control\Session Manager\Environment Abmeldung gespeichert. VolatileGelten nur für die aktuelle Sitzung. Werden nicht gespeichert.
HKCU\VolatileEnvironment
Process Gelten nur für den aktuellen Prozess.
Werden nicht in der Registrierung gespeichert.
Die Eigenschaft Environment des Objekts WshShell gibt die Collection WshEnvironment zurück, über die Ihr Script Umgebungsvariablen abfragen, einrichten und verändern kann. Abfragen von Umgebungsvariablen Um eine Collection mit den Umgebungsvariablen eines bestimmten Typs zu erhalten, verwenden Sie die Eigenschaft WshShell.Environment. Ihr übergeben Sie eine String, der den gewünschten Variablentyp enthält: system, user, process oder volatile. Danach kann das Script über die Collection WshEnvironment über den Namen der Umgebungsvariablen auf diese zugreifen. Tabelle 3.13: Umgebungsvariablen Name
SystemUserProcessProcess (nur Windows 98/Me)
NUMBER_OF_PROCESSORS PROCESSOR_ARCHITECTURE PROCESSOR_IDENTIFIER PROCESSOR_LEVEL Seite 159 von 394
Name
SystemUserProcessProcess (nur Windows 98/Me)
PROCESSOR_REVISION OS COMSPEC HOMEDRIVE HOMEPATH PATH PATHEXT PROMPT SYSTEMDRIVE SYSTEMROOT WINDIR TEMP TMP Die Umgebungsvariablen in der Tabelle gibt es unter allen Windows-Computern. Es kann jedoch zusätzliche benutzerspezifische Variablen geben. Wenn Sie die Namen dieser zusätzlichen Variablen nicht kennen, dann können Sie in der Eingabeaufforderung eine komplette Liste über den Befehl Set abrufen. Script 3.22 ruft die benutzerspezifische und die computerspezifische Umgebungsvariable PATH ab. Script 3.22: Anzeigen der Umgebungsvariable PATH 1Set objShell = WScript.CreateObject("WScript.Shell") 2Set colSystemEnvVars = objShell.Environment("System") 3Set colUserEnvVars = objShell.Environment("User") 4Wscript.Echo "Computer-specific PATH Environment Variable" 5Wscript.Echo colSystemEnvVars("PATH") 6Wscript.Echo "User-specific PATH Environment Variable" 7Wscript.Echo colUserEnvVars("PATH")
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: Computer-specific PATH Environment Variable C:\Programme\PTP2002;%SystemRoot%\system32;%SystemRoot%;%SystemRoot%\System 32\Wbem User-specific PATH Environment Variable C:\Programme\PTP2002;;
Umgebungsvariablen erstellen Um Umgebungsvariablen zu erstellen, gehen Sie anfangs genauso vor wie beim Abfragen einer Umgebungsvariable: Sie benötigen eine Referenz auf die Collection mit den Umgebungsvariablen. Erst dann könnten Sie eine neue Umgebungsvariable erstellen.
Seite 160 von 394
Die folgende Codezeile erstellt zum Beispiel eine Umgebungsvariable mit dem Namen MeineVariable mit den Anfangswert 0. colUsrEnvVars("MeineVariable") = 0
Sie können über Ihre Scripts Variablen vom Typ process oder volatile erzeugen - diese sind jedoch nicht sonderlich nützlich, da beide Variablentypen bei der Abmeldung des Benutzers nicht gespeichert werden. Daher finden Sie Variablen vom Typ user und system wahrscheinlich deutlich praktischer - diese werden bei der Abmeldung des Benutzers gespeichert. Script 3.23 erstellt eine Umgebungsvariable vom Typ user mit dem Namen APP_VARIABLE und setzt deren Wert auf Installiert. Danach fragt das Script den Wert der neu erstellen Variable ab, um sicherzustellen, dass diese auch erstellt wurde. Script 3.23: Erstellen einer Umgebungsvariable vom Typ user 1Set objShell = WScript.CreateObject("WScript.Shell") 2Set colUsrEnvVars = objShell.Environment("USER") 3colUsrEnvVars("APP_VARIABLE") = "Installiert" 4Wscript.Echo colUsrEnvVars("APP_VARIABLE")
Ändern von Umgebungsvariablen Auch bei der Änderung von Umgebungsvariablen beginnen Sie, indem Sie sich eine Referenz auf die Collection mit den Umgebungsvariablen erstellen. Script 3.24 ändert den Wert der Umgebungsvariable von Typ user mit dem Namen APP_VARIABLE auf den Wert Aktualisiert. Script 3.24: Änderung einer Umgebungsvariable von Typ user 1Set objShell = WScript.CreateObject("WScript.Shell") 2Set colUsrEnvVars = objShell.Environment("USER") 3strCurrentValue = colUsrEnvVars("APP_VARIABLE") 4colUsrEnvVars("APP_VARIABLE") = "Aktualisiert" 5Wscript.Echo colUsrEnvVars("APP_VARIABLE")
Auswerten von Umgebungsvariablen Über Umgebungsvariablen können Sie Systeminformationen abrufen. Wenn Sie zum Beispiel auf den temporären Ordner des Benutzers zugreifen möchten, dann brauchen Sie die Pfadangabe für diesen Ordner (beispielsweise C:\Temp). Um den Wert einer Umgebungsvariable abzufragen, verwenden Sie die Methode WshShell.ExpandEnvironmentStrings. Diese Methode erwartet als Parameter den in Prozentzeichen (%) eingeschlossenen Namen der Umgebungsvariable als String und gibt deren Wert zurück. Script 3.25 gibt erst den Wert einer Umgebungsvariable und dann die ausgewertete Umgebungsvariable aus. Script 3.25: Erstellen und Anzeigen einer Umgebungsvariable 1Set objShell = WScript.CreateObject("WScript.Shell") 2Set colEnvVars = objShell.Environment("User") 3Wscript.Echo "temporärer Ordner:" 4Wscript.Echo colEnvVars("TEMP") & vbCrLf 5Wscript.Echo "temporärer Ordner (ausgewertet) "
Seite 161 von 394
6Wscript.Echo objShell.ExpandEnvironmentStrings("%TEMP%")
Wenn Sie das Script unter Cscript ausführen, erzeugt es eine Ausgabe wie die folgende. temporärer Ordner: %USERPROFILE%\Lokale Einstellungen\Temp temporärer Ordner (ausgewertet) C:\DOKUME~1\ADMINI~1\LOKALE~1\Temp
Einträge im Ereignisprotokoll erzeugen Die Fehlersuche in Anwendungen und bei Diensten wird dadurch vereinfacht, dass diese wichtige Ereignisse in das Ereignisprotokoll eintragen. Ihre Scripte können das ebenfalls so machen. Das WshShell-Objekt stellt Ihnen hierzu die Methode LogEvent zur Verfügung - sie erzeugt einen Eintrag im Anwendungsprotokoll. Anmerkung: Wenn Sie Ereignisprotkolleinträge lesen oder verarbeiten möchten, dann müssen Sie WMI verwenden. Weitere Informationen hierzu finden Sie im Kapitel Logs. LogEvent erwartet zwei Parameter. Der erste Parameter ist ein Integerwert, der das Ereignis definiert, dass Sie in das Ereignisprotokoll eintragen möchten. Die möglichen Werte sehen Sie in Tabelle 3.14. Tabelle 3.14: Ereignistypen WertEreignistyp
0
Erfolgreich
1
Fehler
2
Warnung
4
Information
8
Erfolgsüberwachung
16 Fehlerüberwachung Der zweite Parameter gibt den Text für den Protokolleintrag an. Als dritten Parameter können Sie optional den Computernamen angeben - zum Beispiel, wenn das Ereignis nicht auf dem Computer eingetragen wird, auf dem das Script ausgeführt wird, sondern auf einem zentralen Computer (dieser Parameter wird unter Windows 95 und Windows 98 ignoriert). Script 3.26 erzeugt jeweils ein Ereignis der in Tabelle 3.14 angegebenen Ereignistypen mit einer entsprechenden Beschreibung. Script 3.26: Ereignisse im Ereignisprotokoll erzeugen 1Set objShell = WScript.CreateObject("Wscript.Shell") 2objShell.LogEvent 0,"Test Erfolgreich" 3objShell.LogEvent 1,"Test Fehler" 4objShell.LogEvent 2,"Test Warnung " 5objShell.LogEvent 4, "Test Information " 6objShell.LogEvent 8, "Test Erfolgsüberwachung " 7objShell.LogEvent 16, "Test Fehlerüberwachung "
Seite 162 von 394
Mit der LogEvent-Methode können Sie Ereignisse nur im Anwendungsprotokoll erzeugen. Sie können keine bestimmte Ereignis-ID oder Quelle angeben. Die Quelle für alle Ereignisse ist automatisch der WSH - als Ereignis-ID werden die Werte aus Tabelle 3.14 verwendet.
Schreiben und Lesen in der lokalen Registrierungsdatenbank Im Allgemeinen ist es besser die Registrierung über die entsprechenden Tools (zum Beispiel Regedit.exe) zu verwalten. Diese sind zwar auch nicht idiotensicher, verfügen jedoch über eingebaute Sicherheitsoptionen, um potentielle Beschädigungen der Registrierung zu verhindern. Andererseits ist es über solche Tools oft schwer die Registrierung auf mehreren Computern zu bearbeiten. In einem solchen Fall bietet Ihnen das WshShell-Objekt die Möglichkeit, Einträge in der Registrierung zu lesen, zu schreiben und diese zu verändern. Vorsicht: Wenn Sie mit Ihrem Script Änderungen an der Registrierung vornehmen, dann kann dies zu Problemen führen oder sogar eine Neuinstallation von Windows erforderlich machen. Daher sollten Sie vor einer Registrierungsänderung eine Sicherung vornehmen. Weitere Informationen finden Sie in der Registrierungsreferenz im Microsoft Windows 2000 Server Resource Kit unter http://www.microsoft.com/windows2000/techinfo/reskit/enus/default.asp?url=/windows2000/techinfo/reskit/en-us/regentry/default.asp (englischsprachig). Registrierungseinträge lesen Die Registrierung ist die primäre Konfigurationsdatenbank von Windows - die korrekte Ausführung vieler Betriebssystemkomponenten hängt von Registrierungseinstellungen ab. Als Systemadministrator verbringen Sie wahrscheinlich des Öfteren Zeit damit, Werte in der Registrierung zu überprüfen. Diese Registrierungswerte können Sie entweder direkt über ein Tool wie regedit.exe oder über ein Script mit der Methode WshShell.RegRead auslesen. In den meisten Fällen benötigen Sie hierzu nur zwei Dinge: Erstens eine Instanz des Objekts Wscript.Shell und zweitens die Methode RegRead. Die Betriebssystemversion wird zum Beispiel unter dem Registeriungsschlüssel HKLM\Software\Microsoft\Windows NT\CurrentVersion\CurrentVersion gespeichert - Sie können sie über das folgende Script abfragen: Set objShell = WScript.CreateObject("WScript.Shell") sngVersion = objShell.RegRead _ ("HKLM\Software\Microsoft\Windows NT\CurrentVersion\CurrentVersion") Wscript.Echo sngVersion
Datentypen in der Registrierung Jeder in der Registrierung gespeicherte Wert hat einen eindeutigen Datentyp. In Tabelle 3.15 sehen Sie die von WSH unterstützten Registrierungstypen und die VBScript-Datentypen, in die diese von RegRead übersetzt werden. Tabelle 3.15: Registrierungsdatentypen und die entsprechenden VBScript-Datentypen Name
Datentyp
VBScript-Datentyp
REG_SZ
String
String
REG_DWORD
Number
Integer
REG_BINARY
Binary Value
Integer-Array Seite 163 von 394
Name
Datentyp
VBScript-Datentyp
REG_EXPAND_SZExpandable StringString REG_MULTI_SZ Array of Strings String-Array Bei den Datentypen in der Tabelle handelt es sich nur um die gebräuchlichsten. Wenn Sie versuchen mit RegRead nicht unterstützte Datentypen auszulesen, führt dies zu einem Fehler. Anmerkung: Unglücklicherweise haben Sie mit dem WSH keine Möglichkeit, den Datentyp eines Registrierungseintrages vor dem Auslesen festzustellen - über WMI ist dies jedoch möglich. Script 3.27 verwendet die Methode RegRead, um einen Registrierungswert vom Datentyp Multistring (REG_MULTI_SZ) auszulesen. Die Informationen werden als String-Array zurückgegeben, der in einer For-Each-Schleife ausgegeben wird. Script 3.27: Auslesen eines Multistring-Wertes aus der Registrierung 1Set objShell = WScript.CreateObject("WScript.Shell") 2arrValues = objShell.RegRead _ 3 ("HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Security\Sources") 4For Each strValue In arrValues Wscript.Echo strValue 5 6Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: Spooler Security Account Manager SC Manager NetDDE Object LSA DS Security
Registrierungseinträge erstellen oder ändern Über die Methode RegWrite können Sie neue Registrierungseinträge erstellen oder bestehende Einträge ändern. Die Methode nimmt hierzu drei Parameter entgegen: Den Registrierungseintrag, den Sie erstellen oder ändern möchten, den Wert, den Sie dem Eintrag zuweisen möchten und (optional) den Datentyp des Eintrages. Script 3.28 verwendet die Methode RegWrite, um einen DWORD-Eintrag zu erstellen und dessen Wert auf 56 zu setzen. Script 3.28: Einen DWORD-Eintrag in der Registrierung erstellen 1Set objShell = WScript.CreateObject("WScript.Shell") 2objShell.RegWrite "HKCU\TestKey\Version", 56, "REG_DWORD"
Anmerkung: Der Datentyp REG_MULTI_SZ wird von der Methode RegWrite nicht unterstützt. Registrierungseinträge löschen
Seite 164 von 394
Mit der Methode RegDelete können Sie Schlüssel oder Einträge in der Registrierung löschen. Sie akzeptiert einen Parameter - dieser gibt den zu löschenden Schlüssel oder Eintrag an. Wenn Sie einen Schlüssel löschen, dann werden auch alle Einträge unter diesem Schlüssel gelöscht. Script 3.29 verwendet die Methode RegDelete, um einen DWORD-Eintrag aus der Registrierung zu löschen. Script 3.29: Löschen eines DWORD-Eintrags aus der Registrierung 1Set objShell = WScript.CreateObject("WScript.Shell") 2objShell.RegDelete "HKCU\TestKey\Version"
Tastatureingaben an ein Programm schicken Der WSH ermöglicht Ihnen eine Automatisierung von COM-fähigen Anwendungen, indem er Ihnen einen Zugriff auf die jeweiligen COM-Objekte zur Verfügung stellt. Unglücklicherweise sind einige Anwendungen nicht COM-fähig - dies betriff spezielle ältere Anwendungen. Um auch die Arbeit mit solchen Anwendungen automatisieren zu können, haben Sie die Möglichkeit, Tastatureingaben an die Anwendungen zu schicken. Mit der Methode WshShell.SendKeys imitiert Ihr Script die Tastatureingaben durch einen Benutzer. Um ein einzelnes Zeichen zu simulieren, übergeben Sie der Methode SendKeys das Zeichen einfach als Parameter. Sondertasten (zum Beispiel STRG oder ALT) können Sie über bestimmte Zeichenkombinationen angeben. In Tabelle 3.16 sehen Sie alle oft verwendeten Sondertasten und die entsprechenden Zeichenkombinationen. Tabelle 3.16: Zeichenkombinationen für Sondertasten mit SendKeys Taste
Wert für SendKeys
Rückschritt
{BACKSPACE}, {BS}, oder {BKSP}
Pause
{BREAK}
Feststelltaste
{CAPSLOCK}
Entf. oder Entfernen{DELETE} oder {DEL} Pfeil runter
{DOWN}
Ende
{END}
Eingabe
{ENTER} oder ~
ESC
{ESC}
Hilfe
{HELP}
Pos1
{HOME}
Einf. oder Einfügen {INSERT} oder {INS} Pfeil Links
{LEFT}
Seite 165 von 394
Taste
Wert für SendKeys
NUM
{NUMLOCK}
Bild runter
{PGDN}
Bild hoch
{PGUP}
Druck
{PRTSC}
Pfeil links
{RIGHT}
Rollen
{SCROLLLOCK}
Tabulator
{TAB}
Pfeil hoch
{UP}
Hochstelltaste
+
Steuerung (Strg)
^
Alt
%
Alle Funktionstasten werden über den Tastennamen in geschweiften Klammern angegeben zum Beispiel {F1} für die Taste F1 usw. Das folgende Script startet zum Beispiel Notepad und schreibt den Satz 'Dies ist ein Test.': Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run("Notepad.exe") objShell.AppActivate "Editor" objShell.SendKeys "Dies ist ein Test."
Wenn Sie das Script ausführen, wird Notepad geöffnet. Dann wird der Beispielsatz in Notepad geschrieben. Abbildung 3.12: Notepad über SendKeys steuern Anmerkung: Mit SendKeys können Sie auch wiederholte Tastatureingaben simulieren. Wenn Sie zum Beispiel 10x die Taste a simulieren möchten, dann können Sie {a 10} als Parameter verwenden. Zwischen dem Zeichen und der Zahl muss sich in diesem Fall zwingend ein Leerzeichen befinden. Es ist jedoch nicht möglich mehrere Tastatureingaben zu wiederholen. Der Parameter {dog 10} führt also zu einem Fehler. Sie sollten daran denken, dass eine Tastatureingabe nicht die optimale Möglichkeit ist, um eine Anwendung zu kontrollieren. Wenn Sie mit einer Anwendung arbeiten, die nicht COMfähig ist, dann leisten sie natürlich gute Dienste - und in allen anderen Fällen sollten Sie jedoch versuchen, die COM-Objekte zu verwenden. Mit SendKeys können eine Menge unterschiedlicher Probleme auftreten: • •
Es kann schwer sein, die Tastatureingaben an das richtige Fenster zu schicken. Da die Zielanwendung im GUI-Modus ausgeführt wird, ist es möglich, dass ein Benutzer die Anwendung einfach schließt. Unglücklicherweise bricht das Script in einem solchen Fall jedoch nicht ab - die Tastatureingaben werden einfach an eine falsche Anwendung geschickt. Seite 166 von 394
•
Das Script ist schwer mit der Anwendung zu synchronisieren.
Das Timing-Problem ist ganz besonders schwer zu lösen - ganz einfach darum, weil die meisten Scripte sehr viel schneller ausgeführt werden, als die meisten GUI-basierten Anwendungen. Das folgende Script startet zum Beispiel den Windows-Taschenrechner und schickt dann die Ziffer 2 an diese Anwendung. Wahrscheinlich wird es zwar korrekt ausgeführt, die Zahl 2 ist im Taschenrechner jedoch nicht zu sehen: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run "Calc.exe" objShell.AppActivate "Rechner" objShell.SendKeys "2"
Das unerwartete Ergebnis liegt nicht an einem Fehler im Script, sondern an einem TimingProblem. Das Script führt die folgenden Befehle so schnell wie möglich aus: 1.Windows-Taschenrechner starten. 2.Focus auf den Taschenrechner verschieben (über die Methode AppActivate). 3.Die Zahl 2 an den Taschenrechner schicken. Das Script wird jedoch viel schneller ausgeführt, als der Windows-Taschenrechner geladen werden kann. Das führt dazu, dass die Zahl 2 schon gesendet wird, wenn der Taschenrechner noch nicht einmal vollständig geladen wurde - natürlich kann er in diesem Zustand noch keine Tastatureingaben entgegennehmen oder gar verarbeiten. Zur Lösung dieses Problems gibt es zwei Wege. Bei der ersten Möglichkeit stellen Sie fest, wie lange die Anwendung zum Laden braucht, und halten dann das Script für diesen Zeitraum an. Das folgende Script verwendet zum Beispiel die Methode Run, und wartet dann 5 Sekunden auf das Laden des Taschenrechners: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run "Calc.exe" Wscript.Sleep 5000 objShell.AppActivate "Rechner" objShell.SendKeys "2"
In einigen Fällen kann es natürlich schwer einzuschätzen sein, wie lange eine Anwendung zum Laden benötigt. In einem solchen Fall können Sie das zweite Verfahren nutzen: Sie verwenden die Methode AppActivate, um den Rückgabewert der Anwendung zu überprüfen. Verwendung von AppActivate Bevor Sie Tastatureingaben an eine Anwendung schicken, müssen Sie sicherstellen, dass die Anwendung ausgeführt wird und über den Focus verfügt (das Anwendungsfenster muss also das aktive Fenster sein) - hierzu können Sie die Methode AppActivate verwenden. AppActivate erwartet einen Parameter, bei dem es sich entweder um einen String mit dem Titel der Anwendung (wird ganz oben in der Titelleiste der Anwendung angezeigt) oder um die Prozess-ID der Anwendung handeln kann. Die Methode gibt einen Boolean-Wert zurück, der anzeigt, ob die Methode erfolgreich ausgeführt wurde oder nicht. Der Rückgabewert False bedeutet, dass AppActivate nicht in der Lage war, den Focus auf die Anwendung zu verschieben (zum Beispiel, wenn die Anwendung noch nicht vollständig geladen ist). Sie können eine Schleife verwenden, in der AppActivate so lange aufgerufen wird, bis ihr Rückgabewert True ist. Dann wissen Sie, dass die Anwendung vollständig geladen wurde und bereit für Tastatureingaben ist. Seite 167 von 394
Das folgende Script prüft den Rückgabewert von AppActivate. Wenn dieser Wert False ist, wartet das Script für eine Sekunde ab und prüft den Wert dann noch einmal. Dieser Vorgang wird so lange wiederholt, bis der Rückgabewert True ist. Erst dann fährt das Script mit den Tastatureingaben fort: Set objShell = WScript.CreateObject("WScript.Shell") objShell.Run "Calc.exe" Do Until Success = True Success = objShell.AppActivate("Rechner") Wscript.Sleep 1000 Loop objShell.SendKeys "2"
Mit AppActivate wird der übergebene String mit allen Fenstertiteln verglichen. Wenn keine Übereinstimmung gefunden wird, setzt AppActivate den Focus auf das Fenster, dessen Titel mit dem übergebenen String beginnt. Wenn auch hier keine Übereinstimmung gefunden werden kann, wird der Focus auf das Fenster gesetzt, dessen Titel mit dem String endet. So wird sichergestellt, dass AppActivate auch mit Anwendungen funktioniert, in denen Dokumente geöffnet werden können (wenn Sie zum Beispiel Notepad starten und ein neue Dokument öffnen, dann lautet der Fenstertitel von Notepad Unbenannt -Editor und nicht Editor. Das bedeutet, dass Sie die folgenden Codezeilen verwenden können, um den Focus auf den Taschenrechner zu verschieben: objShell.AppActivate "Rechner" objShell.AppActivate "Rech" objShell.AppActivate "R"
Die 'Kurzmethode' kann natürlich auch zu Problemen führen. Stellen Sie sich vor, Sie verwenden die folgende Codezeile: objShell.AppActivate "Rech"
Wenn Sie in diesem Fall ein Word-Dokument mit dem Namen Rechnungen.doc geöffnet haben, könnten die Tasteneingaben versehentlich an dieses Fenster geschickt werden. Script 3.30 zeigt eine mehr praktische Anwendung der Methode SendKeys. Es startet die Microsoft Management Console (MMC) und verschiebt den Focus auf die Anwendung. Dann schickt es die Tastatureingaben an die Anwendung, die erforderlich sind, um den Dialog Snap-In hinzufügen/entfernen und das Dialogfenster Eigenständiges Snap-In hinzufügen anzuzeigen. Script 3.30: Tastatureingaben an eine GUI-Anwendung schicken 1 Const iNormalFocus = 1 2 Set objShell = WScript.CreateObject("WScript.Shell") 3 objShell.Run "mmc.exe",iNormalFocus 4 5 Wscript.Sleep 300 6 7 objShell.AppActivate "Konsole1" 8 Wscript.Sleep 100 9 objShell.SendKeys "^m" 10Wscript.Sleep 100 11objShell.SendKeys "{TAB}" 12Wscript.Sleep 100 13objShell.SendKeys "{TAB}" 14Wscript.Sleep 100 15objShell.SendKeys "{TAB}"
Seite 168 von 394
16Wscript.Sleep 100 17objShell.SendKeys "{ENTER}"
Das aktuelle Arbeitsverzeichnis eines Scripts abfragen und ändern Das Arbeitsverzeichnis eines Scripts ist standardmäßig das Verzeichnis, in dem das Script gestartet wurde. Dies ist nicht notwendigerweise das Verzeichnis, in dem das Script gespeichert ist. Wenn Sie sich in Verzeichnis C:\Temp befindenund das Script c:\scripts\report.vbs starten, dann ist das Arbeitsverzeichnis des Scripts c:\temp. Das Objekt WshShell verfügt über eine Eigenschaft mit dem Namen CurrentDirectory. Über diese Eigenschaft können Sie das Arbeitsverzeichnis abfragen und verändern. Hierfür kann es mehrere Gründe geben: • •
Sie möchten, dass das Script eine Protokolldatei in dem Ordner erstellt, in dem das Script gespeichert ist. Sie möchten feststellen, ob das Script lokal oder über das Netzwerk gestartet wurde - wenn es über das Netzwerk gestartet wurde, beginnt das Arbeitsverzeichnis mit zwei Backslashes (\\).
Um das Arbeitsverzeichnis abzufragen, erstellen Sie eine Instanz von WshShell und greifen dann auf die Eigenschaft CurrentDirectory zu. Um das Arbeitsverzeichnis zu ändern, weisen Sie der Eigenschaft einfach einen neuen Wert zu. Script 3.31: Konfigurieren und Abfragen des aktuellen Arbeitsverzeichnisses 1Set objShell = WScript.CreateObject("WScript.Shell") 2 3Wscript.Echo "Aktuelles Arbeitsverzeichnis:" 4Wscript.Echo objShell.CurrentDirectory 5 6objShell.CurrentDirectory = "C:\" 7 8Wscript.Echo "Arbeitsverzeichnis nach Änderung:" 9Wscript.Echo objShell.CurrentDirectory
Wenn Sie das Script aus dem Ordner c:\temp unter CScript starten, erhalten Sie die folgende Ausgabe: Aktuelles Arbeitsverzeichnis: C:\Temp Arbeitsverzeichnis nach Änderung: C:\
Zeitgesteuerte Nachrichtenfenster anzeigen In einer perfekten Welt trifft Ihr Script bei jeder Ausführung auf perfekte Bedingungen - es führt seine vorgesehene Aufgabe schnell und zuverlässig aus. In der echten Welt funktionieren einige Dinge jedoch nicht immer so zuverlässig. Manchmal starten Sie ein Script, und es werden Entscheidungen erforderlich; zum Beispiel, wenn Sie eine Verbindung mit einem Remotecomputer aufbauen wollen und dieser nicht erreichbar ist. Soll das Script einen neuen Versuch unternehmen, den Computer zu erreichen oder das Problem ignorieren? Oder soll das Script möglicherweise einfach aufgeben?
Seite 169 von 394
In solchen Fällen können Sie die Methode WshShell.Popup verwenden. Sie zeigt ein Nachrichtenfenster an, und liefert als Rückgabewerte den Schalter, auf den der Benutzer geklickt hat. Sie können zum Beispiel ein Nachrichtenfenster mit einen Ja- und einem NeinSchalter anzeigen lassen, und Ihr Script kann dann auf die Auswahl des Benutzers reagieren. 'Soll ein weiterer Verbindungsversuch unternommen werden? Ja/Nein'. Ein weiterer Vorteil der Methode Popup ist, dass Sie einen Timeout-Wert definieren können und Symbol und Titel des Nachrichtenfensters festlegen können. Nachrichtenfenster, die über die Methode Wscript.Echo erzeugt werden, bieten Ihnen diese Möglichkeiten nicht - sie haben immer den Titel Windows Script Host. In Abbildung 3.13 sehen Sie ein Beispiel für ein Nachrichtenfenster, das über die Methode Popup erzeugt wurde.
Abbildung 3.13: Popup-Nachrichtenfenster Vergleich der Methoden Echo und Popup Wenn Sie ein Script unter WScript ausführen, können Sie ein Nachrichtenfenster statt mit der Methode WshShell.Popup auch über die Methode Wscript.Echo anzeigen. Mit Wscript.Echo haben die Benutzer jedoch nur die Möglichkeit auf OK zu klicken. Außerdem hält das Script an, wenn eine Nachricht über Wscript.Echo angezeigt wird. Erst nachdem der Benutzer auf OK geklickt hat, wird es weiter ausgeführt.
Abbildung 3.14: Mit Wscript.Echo unter WScript erzeugtes Nachrichtenfenster Anmerkung: Im Gegensatz zu Wscript.Echo zeigt die Methode Popup immer ein Nachrichtenfenster an - egal, ob das Script unter CScript oder WScript ausgeführt wird. Ein Nachrichtenfenster mit Time-Out erstellen Script 3.32 verwendet die Methode Popup, um drei Nachrichtenfenster zu erstellen. Jedes Fenster verfügt über einen OK-Schalter und wird für maximal 5 Sekunden angezeigt. Wenn der Benutzer nicht auf OK klickt, dann wird das Nachrichtenfenster automatisch nach 5 Sekunden geschlossen. Script 3.32: Nachrichtenfenster mit Time-Out 1Const TIMEOUT = 5
Seite 170 von 394
2Set objShell = 3 4objShell.Popup 5objShell.Popup 6objShell.Popup
WScript.CreateObject("WScript.Shell") "Festplattenprüfung durchgeführt", TIMEOUT "Speicherprüfung durchgeführt", TIMEOUT "CPU-Prüfung durchgeführt", TIMEOUT
Solche Nachrichtenfenster mit Time-Out sind in zwei Situationen sehr praktisch. Erstens, wenn Sie Informationen ausgeben wollen, ohne das Script zu unterbrechen - zum Beispiel könnte Script 3.32 ja tatsächlich die drei Prüfungen durchführen. Nachdem eine Prüfung durchgeführt wurde, würde das Script den Benutzer über die Popup-Methode benachrichtigen, jedoch bereits während der Anzeige mit der nächsten Prüfung fortfahren. Zweitens können Sie dem Benutzer die Möglichkeit geben eine Entscheidung zu treffen oder das Script einfach weiter laufen zu lassen. Stellen Sie sich vor, Sie schreiben ein Script, das einige Aufgaben durchführt und dann Dateien auf einen Computer im Netzwerk kopiert. Da das Kopieren möglicherweise sehr lange dauert, können Sie den Benutzer vorher fragen, ob er die Aktion durchführen möchte. Wenn dieser nicht reagiert, könnten Sie automatisch mit dem Kopieren anfangen. Symbole und Schalter in Nachrichtenfenstern Sie haben die Möglichkeit, unterschiedliche Schalter und Symbole im Nachrichtenfenster anzeigen zu lassen (zum Beispiel die Schalter Ja und Nein oder die Schalter Abbrechen, Wiederholen, Ignorieren). Die Angezeigten Schalter und Symbole definieren Sie mit dem vierten Parameter der Methode Popup. Er setzt sich auch einer Kombination von vordefinierten Konstanten zusammen - die beiden Werte werden einfach addiert. Die verfügbaren Symbole und die entsprechenden Werte sehen Sie in Tabelle 3.17. Tabelle 3.17: Konstanten für Symbole Symbol
Konstante
Wert
Stop
vbCritical
16
Fragezeichen
vbQuestion
32
AusrufungszeichenvbExclamation48 Information
vbInformation 64
In Tabelle 3.18 sehen Sie die zur Verfügung stehenden Schalter und die entsprechenden Konstanten. Tabelle 3.18: Konstanten für Schalter Schalter
Konstante
Wert
OK
vbOKOnly
0
OK und Abbrechen
vbOKCancel
1
Abbrechen, Wiederholen und IgnorierenvbAbortRetryIgnore2 Ja, Nein und Abbrechen
vbYesNoCancel
3
Ja und Nein
vbYesNo
4 Seite 171 von 394
Schalter
Konstante
Wert
Wiederholen und Abbrechen
vbRetryCancel
5
Auch wenn Sie in Ihrem Script sowohl die Konstanten als auch die Werte verwenden können, sollten Sie bei den Konstanten bleiben. Ihre Scripte werden so leichter nachvollziehbar und lesbar. Script 3.33 zeigt einige Nachrichtenfenster an, die jeweils unterschiedliche Symbole und Schalter verwenden. Wenn Sie auf einen Schalter im Nachrichtenfenster klicken, wird das nächste Nachrichtenfenster angezeigt - dies geschieht auch, wenn Sie einfach 5 Sekunden abwarten. Script 3.33: Kombinationen von Symbolen und Schaltern anzeigen 1 Const TIMEOUT = 5 2 Const POPUP_TITLE = "Symbole und Schalter " 3 Set objShell = WScript.CreateObject("WScript.Shell") 4 objShell.Popup "Stop / Abbrechen, Wiederholen und Ignorieren ", _ 5 TIMEOUT,POPUP_TITLE,vbCritical+vbAbortRetryIgnore 6 7 objShell.Popup "Fragezeichen/ Ja, Nein und Abbrechen ", _ 8 TIMEOUT,POPUP_TITLE,vbQuestion+vbYesNoCancel 9 10objShell.Popup "Ausrufungszeichen / Ja und Nein ", _ 11 TIMEOUT,POPUP_TITLE,vbExclamation+vbYesNo 12 13objShell.Popup "Information / Wiederholen und Abbrechen ", _ 14 TIMEOUT,POPUP_TITLE,vbInformation+vbRetryCancel
In den Zeilen 4-14 wird die Methode WshShell.Popup vier Mal aufgerufen, um Nachrichtenfenstern mit unterschiedlichen Symbolen und Schaltern anzuzeigen. Als dritter Parameter wird immer die Konstante POPUP_TITLE übergeben - der Titel der Nachrichtenfenster lautet daher immer Symbole und Schalter. Als vierter Parameter werden Konstanten für die unterschiedlichen Schalter und Symbole übergeben. Die beiden Konstanten werden einfach addiert. Einen Standardschalter auswählen Mit der Methode WshShell.Popup haben Sie die Möglichkeit, einen Standardschalter anzugeben. Der Standardschalter ist der Schalter, der den Focus besitzt und ausgewählt wird, wenn der Benutzer einfach nur die Eingabetaste drückt. Es ist für einen Benutzer nicht unüblich, dass er einfach die Eingabetaste drückt, wenn eine Fehlermeldung angezeigt wird. Daher sollten Sie bei der Auswahl des Standardschalters vorsichtig sein. Sie können zum Beispiel den Schalter verwenden, der fast immer ausgewählt wird. Oder Sie verwenden den 'sichersten' Schalter. Ihr Nachrichtenfenster könnte zum Beispiel lauten: 'Sind Sie sicher, dass Sie alle Dateien auf der Festplatte löschen möchten?'. In einem solchen Fall sollten Sie als Standardschalter den Schalter Nein festlegen - so werden keine Dateien gelöscht, wenn der Benutzer versehentlich die Eingabetaste drückt. Den Standardschalter legen Sie fest, indem Sie eine weitere Konstante zum vierten Parameter addieren - Tabelle 3.19 zeigt Ihnen die möglichen Werte. Wenn Sie einen Schalter auswählen, der nicht angezeigt wird (zum Beispiel den mittleren Schalter, wenn nur ein Schalter angezeigt wird), dann ist der erste Schalter der Standardschalter. Tabelle 3.19: Konstanten für den Standardschalter Seite 172 von 394
Standardschalter Konstante
Wert
Linker Schalter vbDefaultButton10 Mittlerer SchaltervbDefaultButton2256 Rechter Schalter vbDefaultButton3512 Script 3.34 zeigt zwei Nachrichtenfenster an. Beide Fenster verwenden die Schalter Abbrechen, Wiederholen und Ignorieren. Beim ersten Fenster wird kein Standardschalter angegeben - daher hat der erste Schalter den Focus. Beim zweiten Fenster wird der Standardschalter mit der Konstante vbDefaultButton2 festgelegt - daher hat der Schalter Wiederholen den Focus. Script 3.34: Schalter Wiederholen als Standardschalter definieren 1Const TIMEOUT = 5 2Set objShell = WScript.CreateObject("WScript.Shell") 3 4objShell.Popup "Kein Standarschalter." _ 5 ,TIMEOUT,,vbAbortRetryIgnore 6 7objShell.Popup "Wiederholen ist der Standardschalter." _ 8 ,TIMEOUT,,vbAbortRetryIgnore+vbDefaultButton2
Benutzereingaben abfragen Ein Vorteil der Methode Popup ist, dass Sie dem Benutzer eine Auswahl ermöglichen können. Das bedeutet natürlich auch, dass sich Ihr Script darum kümmern muss, auf welchen Schalter der Benutzer geklickt hat. Popup gibt hierzu einen Integerwert zurück (wenn das Fenster durch den Timeout-Wert geschlossen wird, ist der Rückgabewert -1). Eine Auflistung der Rückgabewerte für die einzelnen Schalter finden Sie in Tabelle 3.20. Die folgenden beiden Codezeilen prüfen beide, ob der Benutzer auf den Schalter OK geklickt hat: If intClicked = 1 If intClicked = vbOK
Tabelle 3.20: Konstanten für Schalter WertKonstanteSchalter
1
VbOK
OK
2
VbCancel Abbrechen (bei OK, Abbrechen)
3
VbAbort Abbrechen (bei Wiederholen, Abbrechen, Ignorieren)
4
VbRetry Wiederholen
5
VbIgnore Ignorieren
6
VbYes
Ja
7
VbNo
Nein
Script 3.35 zeigt ein Nachrichtenfenster mit den Schaltern Ja und Nein an. Es stellt dem Benutzer über das Objekt FileSystemObject Informationen über den Host zur Verfügung, über
Seite 173 von 394
den das Script ausgeführt wird. Dann fragt es den Benutzer, ob dieser mehr Informationen über die Datei erhalten möchte. Script 3.35: Benutzereingaben über ein Nachrichtenfenster entgegennehmen 1 Const TIMEOUT = 7 2 Set objShell = WScript.CreateObject("WScript.Shell") 3 Set objFS = WScript.CreateObject("Scripting.FileSystemObject") 4 5 strPath = Wscript.FullName 6 strFileVersion = objFS.GetFileVersion(strPath) 7 8 iRetVal = objShell.Popup(Wscript.FullName & vbCrLf & _ "File Version: " & _ 9 strFileVersion & vbCrLf & _ 10 "Möchten Sie mehr wissen?" _ 11 ,TIMEOUT,"Weitere Informationen?",vbYesNo + vbQuestion) 12 13 14Select Case iRetVal Case vbYes 15 Set objFile = objFS.GetFile(strPath) 16 objShell.Popup WScript.FullName & vbCrLf & vbCrLf & _ 17 "Dateiversion: " & strFileVersion & vbCrLf & _ 18 "Dateigröße: " & Round((objFile.Size/1024),2) & _ 19 " KB" & vbCrLf & _ 20 "Erstellungsdatum: " & objFile.DateCreated & vbCrLf & _ 21 22 "Letzte Änderung: " & objFile.DateLastModified & _ 23 vbCrLf,TIMEOUT 24 Wscript.Quit 25 Case vbNo 26 Wscript.Quit 27 Case -1 28 WScript.StdOut.WriteLine "Time-Out" 29 Wscript.Quit 30End Select
Das Objekt WshNetwork Das Objekt WshNetwork ermöglicht es Ihren Scripten mit Netzwerklaufwerken und -druckern zu arbeiten. Außerdem können Sie den Namen des Computers, dessen Domäne und den Namen des angemeldeten Benutzers abfragen. Das WshNetwork-Objekt kann für einige Aufgaben verwendet werden, die Sie auch über das Kommandozeiletool net.exe ausführen können. Der Befehl net name gibt zum Beispiel den Namen des Benutzers und den Namen des Computers zurück. Mit dem Befehl net use können Sie ein Netzlaufwerk erstellen. Diese Aufgaben können Sie auch über das Objekt WshNetwork ausführen, das somit zu einem idealen Werkzeug für Anmeldescripte wird. In Tabelle 3.21 sehen Sie die Funktionalitäten von WshNetwork und dessen Methoden und Eigenschaften. Tabelle 3.21: Methoden und Eigenschaften des Objekts WshNetwork Funktionalität
Methode und Eigenschaft
Arbeiten mit Netzlaufwerken
MapNetworkDrive EnumNetworkDrives Seite 174 von 394
Funktionalität
Methode und Eigenschaft
RemoveNetworkDrive Arbeiten mit Netzwerkdruckern
AddPrinterConnection AddWindowsPrinterConnection EnumPrinterConnections SetDefaultPrinter RemovePrinterConnection
Informationen über den angemeldetenComputerName Benutzer abrufen UserDomain UserName WshNetwork ist Teil des Windows Script Host-Objektmodells (wshom.ocx). Das Objektmodell von WshNetwork sehen Sie in Abbildung 3.15. Über die Methoden EnumNetworkDrives und EnumPrinterConnections des Objekts erhalten Sie unter anderem das Objekt WshCollection zurück.
Abbildung 3.15: Das Objektmodell von WshNetwork Auf das Objekt WshNetwork zugreifen Bei WshNetwork handelt es sich um ein COM-Objekt. Mit dem folgenden Ausdruck können Sie eine Instanz des Objekts erstellen: Seite 175 von 394
Set objNetwork = WScript.CreateObject("WScript.Network")
Verwalten von Netzlaufwerken Netzlaufwerke sind ein wichtiger Teil einer Netzwerkumgebung. Benutzer ziehen es normalerweise vor, mit Netzlaufwerken statt mit UNC-Pfaden (Universal Naming Convention) zu arbeiten - es ist einfach, sich daran zu erinnern, dass die Finanzdaten unter Laufwerk X gespeichert sind, als sich an die Netzwerkfreigabe \\atl-fs01\departments\accounting\admin\financial_records\2002_archive zu erinnern. Das Erstellen, Ändern und Löschen von Netzwerkfreigaben zu Laufwerksbuchstaben ist eine der wenigen Aufgaben, die Sie nicht über WMI durchführen können - das Auflisten von Netzwerklaufwerken ist hingegen sowohl mit dem WSH-Objekt WshNetwork alsauch über WMI (über die Klasse Win32_MappedLogicalDisk) möglich. Zur Arbeit mit Netzlaufwerken stellt Ihnen WshNetwork drei Methoden zur Verfügung: MapNetworkDrive, RemoveNetworkDrive und EnumNetworkDrives. Zuordnen von Netzwerklaufwerken Über die Methode MapNetworkDrive ordnen Sie einen Laufwerksbuchstaben zu einer Netzwerkfreigabe zu. Die Methode hat zwei zwingende und drei optionale Argumente - diese sehen Sie in Tabelle 3.22. Tabelle 3.22: Parameter von MapNetworkDrive Parameter
Type
ErforderlichStandardwertBeschreibung
LocalName String
Keiner
Der Laufwerksbuchstabe gefolgt von einem Doppelpunkt, der für das Netzlaufwerk verwendet werden soll (zum Beispiel H:)
RemoteName String
Keiner
Der UNC-Pfad der Netzwerkfreigabe - zum Beispiel \\ServerName\ShareName oder \\ServerName\ShareName\FolderName.
UpdateProfileBoolean
False
Ein Boolean-Wert, der angibt, ob das neue Laufwerk im aktuellen Benutzerprofil gespeichert werden soll. Mit dem Wert True wird das neue Laufwerk gespeichert.
UserName
String
Keiner
Verbindet das Netzlaufwerk unter Verwendung eines alternativen Benutzernamens.
Password
String
Keiner
Das Passwort zum alternativen Benutzernamen.
Das folgende Script zeigt, wie Sie mit MapNetworkDrive zwei neue Netzlaufwerke erstellen: Set objNetwork = Wscript.CreateObject("WScript.Network") objNetwork.MapNetworkDrive "G:", "\\atl-fs-01\Sales" objNetwork.MapNetworkDrive "H:", "\\atl-fs-01\Users$\lewjudy"
Standardmäßig verwendet MapNetworkDrive die Anmeldeinformationen des aktuellen Benutzers. Da Sie jedoch ein Netzlaufwerk nur dann zuordnen können, wenn Sie über die erforderlichen Berechtigungen verfügen, haben Sie die Möglichkeit, über die Parameter Seite 176 von 394
UserName und Password alternative Anmeldeinformationen anzugeben - zum Beispiel für administrative Scripte. Es gibt zwei Gründe, aus denen die Methode MapNetworkDrive fehlschlagen kann: • •
Der Benutzer, der das Script ausführt verfügt nicht über ausrechende Berechtigungen um ein Netzwerklaufwerk zu erstellen. Der lokale Laufwerksbuchstabe wird bereits verwendet.
Um Fehler durch fehlende Berechtigungen zu vermeiden, sollten Sie eine Fehlerbehandlung über den Befehl On Error Resume Next implementieren. Einen Fehler durch einen bereits verwendeten Laufwerksbuchstaben können Sie zum Beispiel umgehen, indem Sie das Laufwerk trennen, das im Moment den Laufwerksbuchstaben verwendet. Ein Netzlaufwerk trennen Über die Methode RemoveNetworkDrive können Sie ein Netzlaufwerk trennen. Sie benötigt als einzigen Parameter den lokalen Laufwerksbuchstaben von dem Netzlaufwerk, das Sie trennen möchten. Script 3.36: Trennen eines Netzlaufwerkes 1Set objNetwork = WScript.CreateObject("Wscript.Network") 2objNetwork.RemoveNetworkDrive "G:"
Die Methode akzeptiert zwei weitere optionale Parameter: • •
Einen Boolean-Wert - Wenn dieser auf True gesetzt ist, dann wird das Netzlaufwerk auch dann getrennt, wenn es gerade verwendet wird. Einen weiteren Boolean-Wert - Wenn dieser auf True gesetzt ist, dann wird das Netzlaufwerk auch aus dem Benutzerprofil entfernt.
Auflisten der aktuellen Netzlaufwerke Mit der Methode EnumNetworkDrives können Sie die aktuell zugeordneten Netzlaufwerke auflisten. Sie gibt eine Collection zurück, in der jeweils in Paaren die lokalen Laufwerksbuchstaben und die entsprechenden UNC-Pfade gespeichert sind. Der Index der Collection beginnt bei 0 - die Elemente mit einer geraden Indexnummer enthalten jeweils die lokalen Laufwerksbuchstaben, und die ungeraden Elemente enthalten die UNC-Pfade. In Tabelle 3.23 sehen Sie ein Beispiel für die Collection. Tabelle 3.23: Beispiel für die Collection mit den Netzwerklaufwerken IndexWert
0
D:
1
\\atl-fs-01\users\kmyer
2
E:
3
\\atl-fs-02\accounting
4
F: Seite 177 von 394
IndexWert
5
\\atl-fs-03\public
Wenn Sie die einzelnen Netzlaufwerke aus der Collection abfragen möchten, dann sollten Sie in jedem Schleifendurchlauf jeweils zwei Elemente (den Laufwerksbuchstaben und den UNCPfad) abfragen. Ein Beispiel hierfür sehen Sie in Script 3.37. Listing 3.37: Abfragen der aktuellen Netzlaufwerke 1Set objNetwork = WScript.CreateObject("WScript.Network") 2Set colDrives = objNetwork.EnumNetworkDrives 3For i = 0 to colDrives.Count-1 Step 2 4 Wscript.Echo colDrives.Item(i) & vbTab & colDrives.Item (i + 1) 5Next
Verwalten von Netzwerkdruckern Mit dem WshNetwork-Objekt haben Sie die Möglichkeit Netzwerkdrucker hinzuzufügen oder zu entfernen, den Standarddrucker zu definieren und die Netzwerkdrucker aufzulisten. Netzwerkdrucker hinzufügen Es gibt zwei Methoden zum Hinzufügen von Netzwerkdruckern: AddWindowsPrinterConnection und AddPrinterConnection. Mit der Methode AddWindowsPrinterConnection fügen Sie Windows-basierte Drucker hinzu (zum Beispiel so wie über den Assistenten Drucker hinzufügen der Systemsteuerung), und mit der Methode AddPrinterConnection fügen Sie eine MS-DOS-basierten Drucker hinzu. Windows-basierte Netzwerkdrucker hinzufügen Mit der Methode AddWindowsPrinterConnection können Sie Drucker unter Windows 2000, Windows Server 2003, Windows XP oder Windows NT hinzufügen. Als einzigen Parameter müssen Sie hier den UNC-Pfad des Druckers angeben. Script 3.38: Hinzufügen einer Windows-basierten Druckerverbindung 1Set objNetwork = Wscript.CreateObject("WScript.Network") 2objNetwork.AddWindowsPrinterConnection "\\HRServer01\Printer1"
Wenn Sie die Methode unter Windows 95, Windows 98 oder Windows Me verwenden, dann müssen sie zwei Parameter angeben: den UNC-Pfad des Druckers und den Namen des Druckertreibers (dieser muss bereits installiert sein). Außerdem akzeptiert die Methode unter diesen Betriebssystemen einen dritten optionalen Parameter - den lokalen Port für den Drucker. Hinzufügen von MS-DOS-basierten Druckerverbindungen Mit der Methode AddPrinterConnection können Sie MS-DOS-basierte Druckerverbindungen hinzufügen. Sie benötigt zwei Parameter: den lokalen Port für den Druck und den UNC-Pfad des Netzwerkdruckers. Außerdem haben Sie die Möglichkeit, die neue Druckerverbindung zum Profil des Benutzers hinzuzufügen. Hierzu verwenden Sie einen optionalen dritten Parameter. Wenn dieser auf True gesetzt ist, dann wird die neue Druckerverbindung im Profil gespeichert. Seite 178 von 394
Entfernen einer Druckerverbindung Um eine Druckerverbindung zu entfernen, verwenden Sie die Methode RemovePrinterConnection. WshNetwork.RemovePrinterConnection(printerName, [forced], [updateProfile])
Der erste Parameter definiert den freigegebenen Drucker. Die beiden anderen Parameter sind optional: • •
Soll die Verbindung auch dann getrennt werden, wenn der Drucker gerade verwendet wird? Soll die Verbindung auch aus dem Benutzerprofil gelöscht werden?
Die Methode entfernt sowohl Windows- als auch MS-DOS-basierte Druckerverbindungen. Wenn der Drucker über die Methode AddPrinterConnection eingerichtet wurde, dann muss als Druckername der lokale Port angegeben werden. Wenn der Drucker mit der Methode AddWindowsPrinterConnection angelegt wurde, dann muss als Druckername der UNC-Pfad des Druckers angegeben werden. Der folgende Befehl entfernt zum Beispiel den Drucker \\atl-ps-01\colorprinter. Set objNetwork = WScript.CreateObject("WScript.Network") objNetwork.RemovePrinterConnection "\\atl-ps-01\colorprinter"
Auflisten der verfügbaren Drucker Die Abfrage der verfügbaren Netzwerkdrucker funktioniert genauso wie die Abfrage der zugeordneten Netzwerklaufwerke. Mit der Methode EnumPrinterConnections erhalten Sie eine Collection mit den Netzwerkdruckern. In ihr sind paarweise die lokalen Druckernamen bzw. die Ports und die UNC-Pfade gespeichert. Script 3.39: Auflisten der verfügbaren Drucker 1Set objNetwork = WScript.CreateObject("WScript.Network") 2Set colPrinters = objNetwork.EnumPrinterConnections 3For i = 0 to colPrinters.Count -1 Step 2 4 Wscript.Echo colPrinters.Item(i) & vbTab & colPrinters.Item (i + 1) 5Next
Wenn Sie das Script unter CScript ausführen, könnte die Ausgabe so aussehen: LPT1: Drucker Marketing-Abteilung XRX00034716DD75 \\atl-prn-xrx\plotter XRX0000AA622E89 \\atl-prn-xrx\farbdrucker
Definieren des Standarddruckers Viele Benutzer drucken Dokumente einfach über das Drucken-Symbol der Anwendung aus. In den meisten Fällen bedeutet das, dass das Dokument über den Standarddrucker ausgedruckt wird. Wenn Sie den verschiedenen Benutzern unterschiedliche Standarddrucker zuweisen, können Sie die Last auf verschiedene Drucker verteilen. Dies können Sie über die Methode SetDefaultPrinter durchführen. Die Methode verwendet die folgende Syntax: WshNetwork.SetDefaultPrinter(printerName)
Beim Parameter printerName handelt es sich um den UNC-Pfad des Druckers (wenn es sich um einen lokalen Drucker handelt, können Sie den Port des Druckers verwenden - zum Beispiel LPT1). Das folgende Script richtet den Drucker \\atl-ps-01\colorprinter als Standarddrucker ein: Set objNetwork = WScript.CreateObject("WScript.Network")
Seite 179 von 394
objNetwork.SetDefaultPrinter("\\atl-ps-01\colorprinter")
Den aktuellen Standarddrucker können Sie über SetDefaultPrinter leider nicht abfragen.
Informationen über den Benutzer und den Computer abfragen Das Objekt WshNetwork stellt drei Eigenschaften zur Verfügung: ComputerName, UserDomain und UserName - Sie können diesen Eigenschaften allerdings keinen Wert zuweisen (Nur-Lese-Eigenschaften). Abhängig von Ihren Anforderungen können Sie die gleichen Informationen auch über ADSI abrufen. Mit dem WSH können Sie außerdem nur den SAM-Kontonamen abrufen - mit ADSI haben Sie auch Zugriff auf Informationen wie dem eindeutigen Namen (Distinguished Name DN) des Objekts (zum Beispiel cn=Ulf Dornheck Busscher,ou=Human Resources,dc=fabrikam,dc=com). Mit dem DN können Sie dann direkt auf Active Directory zugreifen und Informationen wie zum Beispiel die Gruppenzugehörigkeit des Benutzers abrufen. Wenn Sie jedoch nur den Benutzernamen, den Domänennamen oder den Computernamen benötigen, dann reicht das Objekt WshNetwork vollkommen aus. Außerdem steht das WSHObjekt unter Windows 98 und Windows NT 4.0 standardmäßig zur Verfügung - dies ist bei ADSI nicht der Fall. Das WshNetwork-Objekt wird oft in Anmeldescripten verwendet - hier werden die Informationen zum Beispiel zum Zuordnen von Netzwerklaufwerken benötigt. Script 3.40 führt eine solche Zuordnung durch (N zu \\fileserver01\accounting ). Jedoch nur, wenn der Benutzer der Domäne ACT angehört. Wenn der Benutzer der Domäne DEV angehört, dann wird Laufwerk N zu \\fileserver01\development zugeordnet. Script 3.40: Zuordnen von Netzwerklaufwerken abhängig von der Domäne des Benutzers 1 Set objNetwork = WScript.CreateObject("WScript.Network") 2 strUserDomain = objNetwork.UserDomain 3 4 If strUserDomain = "ACCOUNTING" Then 5 objNetwork.MapNetworkDrive "N:", "\\fileserver01\accounting", True 6 ElseIf strUserDomain = "DEVELOPMENT" Then 7 objNetwork.MapNetworkDrive "N:", "\\fileserver01\development", True 8 Else Wscript.Echo "User " & objNetwork.UserName & _ 9 10 "not in ACCOUNTING or DEVELOPMENT. N: not mapped." 11End If
Das Objekt WshController Eine Einschränkung des WSH war es immer, dass Scripte nur lokal ausgeführt werden können. Es war nicht möglich ein Script gegen einen Remotecomputer auszuführen. Stellen Sie sich zum Beispiel vor, Sie möchten über ein Script auf einigen Remotecomputern eine Druckerverbindung hinzufügen. Sie müssten dieses Script auf allen Remotecomputern ausführen. In der Vergangenheit waren Sie nicht in der Lage ein Script auf Computer A auszuführen und in diesem Script auf Computer B eine Druckerverbindung hinzuzufügen. Daher wurde der WSH auch primär nur in Anmeldescripten verwendet.
Seite 180 von 394
Natürlich wäre es viel praktischer, wenn ein Script Änderungen auf einem oder mehreren Remotecomputern durchführen könnte. Hierzu stellt Ihnen ab WSH 5.6 das Objekt WshController zur Verfügung. Das Objekt WshController ermöglicht es Ihnen, ein Controller Script (Steuerungsscript)zu erstellen, das Worker Scripts (Arbeitsscripte) gegen andere Computer ausführen kann. Das Controller Script initiiert, überwacht und beendet das Worker Script. Das Worker Script ist hingegen einfach ein Script, das administrative Aufgaben ausführt - zum Beispiel das Hinzufügen einer Druckerverbindung oder eines Netzwerklaufwerkes. Das Worker Script muss sich nicht auf dem selben Computer befinden wie das Controller Script - sein Speicherort muss jedoch relativ zu dem Computer angegeben werden, auf dem das Controller Script ausgeführt wird. Sie können zum Beispiel ein Controller Script erstellen, das auf ein Worker Script in einer Netzwerkfreigabe auf einem anderen Computer zugreift und dieses Worker Script auf einem dritten Computer ausführt. Das Script, das auf dem Remotecomputer ausgeführt wird, wird hierbei niemals auf dem Remotecomputer gespeichert. Stattdessen wird es im WSH-Prozess des Remotecomputers gestartet. In Abbildung 3.16 sehen Sie eine Liste aller Objekte, Methoden, Eigenschaften und Ereignisse des WshController-Objektes.
Abbildung 3.16: Das Objektmodell von WshController Auf das Objekt WshController zugreifen Bei WshController handelt es sich um ein COM-Objekt. Über die folgende Codezeile können Sie eine Instanz des Objekts erstellen: Set objController = WScript.CreateObject("WshController")
Seite 181 von 394
Anmerkung: Die ProgID des Objekts WshController folgt nicht denselben Namenskonventionen wie den Objekten WshNetwork (WScript.Network) und WshShell (WScript.Shell). Sie verwendet keinen Punkt und beginnt nicht mit WScript. Vorbereiten der Remoteausführung von Scripten Bevor Sie das Objekt WshController verwenden können, müssen Sie Ihre Umgebung folgendermaßen konfigurieren: • •
Sowohl auf dem lokalen Computer als auch auf dem Remotecomputer muss der WSH in der Version 5.6 installiert sein. Sie müssen dem Registrierunsschlüssel HKEY_LOCAL_ MACHINE\SOFTWARE\Microsoft\Windows Script Host\Settings auf allen Zielcomputern einen String-Eintrag (REG_SZ) mit dem Namen Remotehinzufügen und dessen Wert auf 1 setzen. Auf dem lokalen Computer, von dem aus Sie das Script starten, ist dieser Eintrag nicht erforderlich.
Vorsicht: Wenn Sie die Registrierung über ein Script ändern, kann es leicht zu Problemen kommen - Ihr System könnte beschädigt werden oder es könnte sogar eine komplette Neuinstallation erforderlich werden. Bevor Sie Registrierungsänderungen über ein Script vornehmen, sollten Sie das Script ausführlich testen und die Registrierung auf den Zielcomputern sichern. Weitere Informationen zu Registrierungsänderungen über Scripte finden Sie in der Registry Reference des Windows 2000 Server Resource Kit unter http://www.microsoft.com/reskit (englischsprachig). Script 3.41 verwendet den WMI-Provider Registry, um den Eintrag Remote zur Registrierung des in der Variable strComputer definierten Computers hinzuzufügen und diesen auf den Wert 1 zu setzen. Script 3.41: Hinzufügen eines Registrierungseintrags, um Scripte remote ausführen zu können 1Const HKEY_LOCAL_MACHINE = &H80000002 2strComputer = "RemoteComputerName" 3 4Set objRegProv = GetObject("winmgmts:{impersonationLevel=Impersonate}" & _ 5 "!\\" & strComputer & "\root\default:StdRegProv") 6 7strKeyPath = "SOFTWARE\Microsoft\Windows Script Host\Settings" 8objRegProv.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,"Remote","1"
Der Benutzer, der das Script ausführt, muss ein Mitglied der Gruppe Administratoren des Zielcomputers sein. Funktionalitäten von WshController Das Objekt WshController ermöglicht es Ihnen, Scripte aus Remotecomputern auszuführen, deren Status zu überwachen und die von diesen Scripten generierten Fehler abzufragen. In Tabelle 3.24 sehen Sie die Methoden, Eigenschaften und Ereignisse der Objekte WshController, WshRemote und WshRemoteError, die Sie hierzu verwenden können.
Seite 182 von 394
Tabelle 3.24: Methoden, Eigenschaften und Ereignisse der Objekte WshController, WshRemote und WshRemoteError Aufgabe
Methode, Eigenschaft oder Ereignis
Scripte auf Remotecomputern ausführen
CreateScript, Execute, Terminate
Den Status von Remotescripten überwachenStatus, Start, End, Error (Ereignisse) Fehler eines Remotescripts abfragen
Error (Ereignis), Error (Eigenschaft), Character, Description, Line, Number, Source, SourceText
Scripte auf einem Remotecomputer ausführen Zum Starten eine Scripts auf einem Remotecomputer stellt Ihnen WshController die Methode CreateScript zur Verfügung - diese gibt ein WshRemote-Objekt zurück. Das WshRemote-Objekt kann dann zum Ausführen des Worker Scripts verwendet werden. Das Worker Script wird also nicht über die Methode CreateScript selbst gestartet. Stattdessen wird die Methode Execute des neuen WshRemote-Objekts zum Start des Scripts auf dem Remotecomputer verwendet. Script 3.42 startet das hypothetische Worker Script MapNetworkDrive.vbs auf dem Remotecomputer RASServer01. Die Position des Worker Scripts kann als lokaler Pfad oder als UNC-Pfad angegeben werden. In Script 3.42 wird allerdings gar kein Pfad angegeben - in diesem Fall muss sich das Worker Script im gleichen Ordner befinden wie dass Controller Script. Script 3.42: Ein lokales Script auf einem Remotecomputer ausführen 1 strRemoteComputer = "RASServer01" 2 strWorkerScript = "MapNetworkDrive.vbs" 3 Set objWshController = WScript.CreateObject("WshController") 4 Set objRemoteScript = _ 5 objWshController.CreateScript(strWorkerScript, strRemoteComputer) 6 objRemoteScript.Execute 7 8 Do While Not objRemoteScript.Status = 2 Wscript.Sleep(100) 9 10 Wscript.Echo "Remote script not yet complete." 11Loop
Anmerkung: Ein Remotezugriff ist kein interaktiver Prozess - das bedeutet, dass Remotescripte keine GUI-Elemente auf dem Remotecomputer anzeigen können. Wenn Sie dies versuchen, schlägt das Script fehl oder produziert unerwartete Ergebnisse - ein Nachrichtenfenster ist zum Beispiel nicht zu sehen.
Den Status von Remotescripten überwachen Wenn Sie ein Remotescript starten, dann möchten Sie wahrscheinlich auch wissen, ob das Script erfolgreich ausgeführt wurde. Das Objekt WshRemote verfügt hierzu über eine Eigenschaft mit dem Namen Status. Sie kann drei verschiedene Werte annehmen: •
0 - das Remotescript wurde noch nicht gestartet.
•
1 - das Remotescript wird gerade ausgeführt. Seite 183 von 394
•
2 - die Ausführung des Remotescripts ist beendet.
Zusätzliche zur Eigenschaft Status stehen Ihnen drei Ereignisse zur Überwachung des Remotescripts zu Verfügung: •
Start - dieses Ereignis wird ausgelöst, wenn die Ausführung des Remotescripts beginnt.
•
End - dieses Ereignis wird bei Beendigung des Remotescripts ausgelöst.
•
Error - dieses Ereignis wird ausgelöst, wenn im Remotescript ein Fehler auftritt. In einem solchen Fall können Sie über das Objekt WshRemoteError von WshRemote weitere Informationen über den Fehler abfragen.
Script 3.43 verwendet drei Subroutinen (Remote_Start, Remote_End und Remote_Error), die auf die Ereignisse Start, Error und End reagieren. Diese Subroutinen werden aufgerufen, wenn eins der Ereignisse ausgelöst wird, und sie zeigen eine einfache Nachricht an. Durch den Aufruf von ConnectObject in Zeile 7 zwingt das Script dazu auf die Ereignisse zu reagieren. Beachten Sie, dass der String 'Remote_', der als zweiter Parameter übergeben wird, der erste Teil des Namens der Subroutine ist, die das entsprechende Ereignis verarbeitet. Der zweite Teil des jeweiligen Namens ergibt sich aus dem jeweiligen Ereignis. Script 3.43: Überwachen eines remote ausgeführten Scripts 1 strRemoteComputer = "RASServer01" 2 strWorkerScript = "CreateTextFilMapNetworkDrive.vbs" 3 4 Set objWshController = WScript.CreateObject("WshController") 5 Set objRemoteScript =_ 6 objWshController.CreateScript(strWorkerScript, strRemoteComputer) 7 Wscript.ConnectObject objRemoteScript, "Remote_" 8 objRemoteScript.Execute 9 10Do While Not objRemoteScript.Status = 2 11 Wscript.Sleep(100) 12Loop 13 14Sub Remote_Start 15 Wscript.Echo "Start des Remotescripts." 16End Sub 17 18Sub Remote_Error 19 Wscript.Echo "Fehler im Remotescript." 20 objRemoteScript.Terminate 21 Wscript.Quit 22End Sub 23 24Sub Remote_End 25 Wscript.Echo "Beendigung des Remotescripts." 26End Sub
Wenn alles wie erwartet funktioniert, werden nur die Ereignisse Start und End ausgelöst - im Fall eines Fehlers wird das Ereignis Error ausgelöst. Weitere Informationen zur Abfrage von weiteren Fehlerdetails finden Sie im folgenden Abschnitt.
Seite 184 von 394
Die vom Remotescript ausgelösten Fehler genauer untersuchen Wenn ein Fehler aufgetreten ist, dann müssen Sie feststellen, wie dieser Fehler zustande gekommen ist. Hierzu können Sie bei einem Remotescript das Objekt WshRemoteError verwenden (dieses Objekt ist eine Eigenschaft von WshRemote). Es enthält einige Eigenschaften, die den aufgetretenen Fehler genauer definieren. Diese Eigenschaften sehen Sie in Tabelle 3.25. Tabelle 3.25: Eigenschaften des Objekts WshRemoteError EigenschaftBeschreibung
Character Gibt die Zeichenposition in der aktuellen Scriptzeile wieder, an der der Fehler aufgetreten ist. Beim Semikolon in der folgenden Zeile handelt es sich zum Beispiel um ein falsches Zeichen - es befindet sich an Position 14 der Zeile. Die Eigenschaft Character würde also den Wert 14 zurückgeben: Wscript.Echo ; Description Eine Beschreibung des Fehlers Line
Die Zeilennummer des Scripts, in der der Fehler aufgetreten ist.
Number
Der Fehlercode des Fehlers.
Source
Das COM-Objekt, das den Fehler ausgelöst hat.
SourceText Die Scriptzeile, die den Fehler ausgelöst hat. Diese Eigenschaft enthält jedoch nicht in allen Fällen einen Wert. Script 3.44 reagiert mit einer Subroutine auf das Error-Ereignis. Im Gegensatz zu Script 3.43, dass nur eine einfach Fehlermeldung angezeigt hat, verwendet die Subroutine die Eigenschaft Error von WshRemote, um eine Instanz des Objekts WshRemoteError zu erhalten. Dann zeigt das Script alle verfügbaren Eigenschaften des Objekts an. Script 3.44: Abfragen von Fehlerinformationen bei Remotescripten 1 strRemoteComputer = "RASServer01" 2 strWorkerScript = "CreateTestFile.vbs" 3 4 Set objWshController = WScript.CreateObject("WshController") 5 Set objRemoteScript =_ 6 objWshController.CreateScript(strWorkerScript, strRemoteComputer) 7 Wscript.ConnectObject objRemoteScript, "Remote_" 8 objRemoteScript.Execute 9 10Do While Not objRemoteScript.Status = 2 11 Wscript.Sleep(100) 12Loop 13 14 15Sub Remote_Error Wscript.Echo "Fehler im Remotescript." 16 Set objError = objRemoteScript.Error 17 18 Wscript.Echo "Character :" & objError.Character 19 Wscript.Echo "Description :" & objError.Description Wscript.Echo "Line :" & objError.Line 20 Wscript.Echo "Number :" & objError.Number 21 Wscript.Echo "Source :" & objError.Source 22 23 Wscript.Echo "Source Text :" & objError.SourceText
Seite 185 von 394
24 objRemoteScript.Terminate 25 Wscript.Quit 26End Sub
Beschränkungen im Zusammenhang mit Remotescripten Es gibt zwei wichtige Einschränkungen im Zusammenhang mit Remotescripten. Erstens gibt es keinen einfachen Weg, um an die Ausgaben des Remotescripts zu gelangen, und zweitens können Remotescripte nicht mit den Kontoinformationen des Benutzers arbeiten, der das Controller Script ausgeführt hat. Um das erste Problem zu umgehen, können Sie eine Textdatei erstellen, in der das Worker Script dann seine Ausgaben speichert - diese Textdatei kann dann zum Beispiel in einer Netzwerkfreigabe gespeichert sein. Weitere Informationen zur Erstellung von Textdateien erhalten Sie im Kapitel Script Runtime Primer in diesem Buch. Es gibt leider keinen Weg, um das zweite Problem zu vermeiden - jedenfalls nicht ohne potentielle Sicherheitsprobleme.
Absichern von Scripten Sicherheit ist in der Systemadministration immer ein wichtiges Thema - dies gilt auch für Scripte. Der ILOVEYOU-Virus war nicht so erfolgreich, weil er Sicherheitslücken im Windows Script Host ausgenutzt hat, sondern durch menschliches Fehlverhalten: Menschen sind von natur aus neugierig. Wenn Sie mit der Frage konfrontiert werden 'Wollen Sie dieses Script ausführen', und keine weiteren Informationen zur Verfügung stehen, dann entscheiden sich viele Menschen dafür mit 'Ja' zu antworten. Der WSH 5.6 hilft den Benutzern intelligentere Entscheidungen zu treffen. Sie können den WSH zum Beispiel so konfigurieren, dass er eine Nachricht wie 'Der Autor dieses Scripts ist unbekannt. Sind Sie sicher, dass Sie fortfahren wollen?' anzeigt, wenn ein Benutzer versucht ein Script auszuführen. Alternativ kann der Administrator den Benutzern diese Entscheidung auch abnehmen - der WSH kann so konfiguriert werden, dass er nur Scripte ausführt, die digital signiert und freigegeben wurden. In diesem Abschnitt werden mehrere Techniken untersucht, mit der Sie die Sicherheit Ihrer Scripte verbessern können: • • •
Signieren von Scripten. Den Benutzer in der Scriptausführung einschränken. Den Windows Script Host deaktivieren.
Wichtig: Sicherheit betrifft noch andere Elemente. Es stimmt zwar, dass Hacker oft mit Scripten arbeiten, aber sie stellen nicht die einzige Sicherheitsbedrohung dar. Auch ausführbare Dateien und Batchdateien können missbraucht werden. Die in diesem Kapitel besprochenen Techniken können als Teil eines Sicherheitsplans eingesetzt werden - sie sollten jedoch nicht den ganzen Sicherheitsplan darstellen.
Seite 186 von 394
Signieren von Scripten Digitale Signaturen (eingeführt mit WSH 5.6) bieten Ihnen einen Weg, den Autoren eines Scripts zu überprüfen und zu garantieren, dass das Script seit seiner Erstellung und Signierung nicht verändert wurde. Das bedeutet nicht zwingend, dass das Script 'sicher' ist - auch Virenautoren können Signaturen verwenden. Die Signatur bietet Ihnen jedoch zwei Vorteile: •
•
Sie können definieren, welchen Scriptautoren vertraut wird und welchen nicht. Zum Beispiel könnten Sie festlegen, dass nur die Scripte ausgeführt werden, die mit einem vertrauenswürdigen Zertifikat signiert wurden. Sie können sicherstellen, dass ein Script seit seiner Signierung nicht verändert wurde. Wenn Sie wissen, dass das Script ThirdPartyScript.vbs sicher ist, dann können Sie das Script in dem sicheren Wissen verwenden, dass es von niemandem manipuliert wurde. Wenn ein Script signiert wird, dann wird ein 'Hash' für dieses Script ermittelt. Mit diesem Hash kann die Signatur des Scripts überprüft werden. Wenn das Script verändert wurde, dann ist der Hash nicht mehr gültig.
Wenn ein Script signiert wird, dann wird am Ende des Scripts eine digitale Signatur als Kommentar angehängt. Eine solche Signatur kann zum Beispiel so aussehen: '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''
SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG SIG
'' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''
Begin signature block MIIC8AYJKoZIhvcNAQcCoIIC4TCCAt0CAQExDjAMBggq hkiG9w0CBQUAMGYGCisGAQQBgjcCAQSgWDBWMDIGCisG AQQBgjcCAR4wJAIBAQQQTvApFpkntU2P5azhDxfrqwIB AAIBAAIBAAIBAAIBADAgMAwGCCqGSIb3DQIFBQAEEPC2 QdSn0Xnjl7nT/Xwadl2gggF6MIIBdjCCASCgAwIBAgIQ NeMgQmXo1o1F8M6hs6TX1jANBgkqhkiG9w0BAQQFADAW MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMDEyMjEy MzUxMTJaFw0zOTEyMzEyMzU5NTlaMBUxEzARBgNVBAMT Ck15IENvbXBhbnkwXDANBgkqhkiG9w0BAQEFAANLADBI AkEAx/bBOOqOzdHk2EfxXloUaGo9PtI/HSJ9LQSXkhF7 neEf4Qy+oyA7NImnOacI+1HDCOAPeKgGJIvaFcZs0BuM iQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCN YSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mC EAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQAD QQA6/fIIDKycSp2DdBT/A3iUSxoiu2BqmEEpVoGKE5yY CA3MDWuI29RRvgNJ2oQasb8rZiD5dEexGK3rWEQGV6r+ MYHhMIHeAgEBMCowFjEUMBIGA1UEAxMLUm9vdCBBZ2Vu Y3kCEDXjIEJl6NaNRfDOobOk19YwDAYIKoZIhvcNAgUF AKBOMBAGCisGAQQBgjcCAQwxAjAAMBkGCSqGSIb3DQEJ AzEMBgorBgEEAYI3AgEEMB8GCSqGSIb3DQEJBDESBBCV t6owbn7YLnkAnCqiDdINMA0GCSqGSIb3DQEBAQUABECe xmfNlmrIls2kFkyhXOWKicnpOk5iW4twTRNAc4LAkO8M uk0ZBCBgR5XC8F7slEMfWCG9R7129EUF4vFhZToK End signature block
Die Verwendung von signierten Scripten erzwingen Um die Sicherheitseinstellung für signierte und unsignierte Scripte zu konfigurieren, müssen Sie den Registrierungsschlüssel HKEY_LOCAL_MACHINE\Software\Microsoft\Windows Script Host\Settings\TrustPolicy bearbeiten. Die folgenden Werte können Sie verwenden: •
0 - alle Scripte werden ohne Warnung ausgeführt (die Standardeinstellung).
•
1 - bevor ein Script ausgeführt wird, wird eine Sicherheitswarnung mit dem Sicherheitsstatus des Scripts angezeigt (signiert und überprüft, signiert und nicht überprüft und unsigniert). Der Benutzer hat jedoch unabhängig vom Status des Scripts die Möglichkeit das Script Seite 187 von 394
auszuführen. Außerdem kann er sich weitere Informationen zur Signierung des verwendeten Zertifikats anzeigen lassen. •
•
2 - bevor ein Script ausgeführt wird, wird dessen Signatur überprüft. Es wird geprüft, ob das Script aus einer vertrauenswürdigen Quelle stammt. Wenn dies der Fall ist, dann wird das Script automatisch ausgeführt. Wenn das Script unsigniert ist oder nicht aus einer vertrauenswürdigen Quelle stammt, dann wird das Script nicht ausgeführt. Der Benutzer hat in so einem Fall keine Möglichkeit die Ausführung des Scripts zu erzwingen. Er erhält stattdessen die folgende Nachricht: Ausführung des Windows Script Host fehlgeschlagen. (Keine Signatur vorhanden.)
Scripte über ein mit Hilfe eines Scripts signieren Der WSH 5.6 stellt ein Objekt mit dem Namen Scripting.Signer zur Verfügung, über das Sie ein Script mit Hilfe eines Scripts signieren können. Hierzu gehen Sie folgendermaßen vor: • •
Erstellen Sie eine Instanz des Objekts Scripting.Signer. Verwenden Sie die Methode SignFile. Diese erwartet als Parameter den Dateinamen des zu signierenden Scripts und den Namen des Zertifikats, mit dem das Script signiert werden soll.
Das folgende Script verwendet zum Beispiel das Zertifikat mit dem Namen IT Department und das Script C:\Scripts\CreateUsers.vbs zu signieren: set objSigner = WScript.CreateObject("Scripting.Signer") objSigner.SignFile "C:\Scripts\CreateUsers.vbs", "IT Department"
Sie können auch mehrere Scripte auf einmal signieren. Das folgende Script verwendet eine Schleife, um alle Scripts im Ordner C:\Scripts zu signieren (es geht allerdings davon aus, dass in diesem Ordner nur Scripte gespeichert sind): set Set Set Set For
objSigner = WScript.CreateObject("Scripting.Signer") objFSO = CreateObject("Scripting.FileSystemObject") objFolder = objFSO.GetFolder("c:\scripts") colListOfFiles = objFolder.Files each objFile in colListOfFiles objSigner.SignFile objFile.Name, "IT Department"
Next
Eine Signatur mit Hilfe eines Scripts überprüfen Das Objekt Scripting.Signer kann auch zur Überprüfung einer Signatur verwendet werden. Hierzu verwenden Sie die Methode VerifyFile mit den folgenden Parametern: • •
Dem Dateinamen des Scripts, dessen Signatur Sie überprüfen möchten. Einen Boolean-Wert, der angibt ob, Sie eine Sicherheitsmeldung anzeigen möchten, wenn die Signatur nicht überprüft werden kann. Wenn der Wert auf False gesetzt ist, dann wird keine Meldung angezeigt.
Das folgende Script überprüft die Signatur von Script C:\Scripts\CreateUsers.vbs und unterdrückt die Sicherheitswarnung. Es gibt einen von zwei möglichen Werten zurück: True bedeutet, dass die Signatur überprüft wurde, und False bedeutet, dass das Script nicht signiert ist oder die Signatur nicht überprüft werden konnte: set objSigner = WScript.CreateObject("Scripting.Signer") blnShowGUI = False
Seite 188 von 394
blnIsSigned = objSigner.VerifyFile("C:\Scripts\CreateUsers.vbs", blnShowGUI) If blnIsSigned then WScript.Echo objFile.Name & " wurde signiert." Else WScript.Echo objFile.Name & " wurde nicht signiert." End If End If
Natürlich können Sie auch mit der Methode VerifyFile mehrere Signaturen auf einmal prüfen. Das folgende Script überprüft die Signaturen aller Scripte im Ordner C:\Scripts (es geht auch diesmal wieder davon aus, dass im Ordner nur Scripte gespeichert sind): set objSigner = WScript.CreateObject("Scripting.Signer") blnShowGUI = False Set Set Set For
objFSO = CreateObject("Scripting.FileSystemObject") objFolder = objFSO.GetFolder("C:\Scripts") colListOfFiles = objFolder.Files each objFile in colListOfFiles blnIsSigned = objSigner.VerifyFile(objFile.Name, blnShowGUI) If blnIsSigned then WScript.Echo objFile.Name & " wurde signiert." Else WScript.Echo objFile.Name & " wurde nicht signiert." End If
Next
Die Ausführung von Scripten einschränken Standardmäßig wird ein Script mit einem Doppelklick auf eine .vbs-Datei sofort ausgeführt. Über die Registrierung können Sie dieses Verhalten jedoch ändern. Der Benutzer wird hierdurch jedoch nicht daran gehindert, dass Script auszuführen. Er kann zum Beispiel einfach die Option Öffnen mit im Kontextmenü auswählen und das Script dann über Wscript.exe oder Cscript.exe ausführen. Außerdem kann er über die folgenden Befehle das Script in der Kommandozeile starten: wscript.exe DeleteFiles.vbs cscript.exe DeleteFiles.vbs Trotzdem gibt Ihnen die Methode ein wenig zusätzliche Sicherheit - die Benutzer haben immerhin die Möglichkeit, die Ausführung eines potentiell gefährlichen Scripts abzubrechen. Ohne diese Option würde das Script einfach ohne Warnung gestartet. Die Registrierungsanpassung können Sie mit der folgenden Batchdatei vornehmen: reg copy HKCR\VBSFile\Shell HKCR\VBSFile\bkupShell /s /f reg delete HKCR\VBSFile\Shell /f reg add HKCR\VBSfile\ /v NoOpen /t REG_SZ /d " Führen Sie dieses Script nicht aus, wenn es nicht von der IT-Abteilung freigegeben wurde."
Die Standardfunktionalität können Sie mit der folgenden Batchdatei wiederherstellen: reg copy HKCR\VBSFile\bkupShell HKCR\VBSFile\Shell /s /f reg delete HKCR\VBSFile\bkupShell /f reg delete HKCR\VBSFile /v NoOpen /f
Die Batchdatei führt die folgenden Schritte durch:
Seite 189 von 394
1.Sie verwendet Reg.exe (zum Beispiel aus den Windows 2000 Support Tools), um den Registrierungspfad HKEY_CLASSES_ROOT\VBSFile\Shell nach HKEY_CLASSES_ROOT\VBSFile\bkupShell zu kopieren (der gesamte Pfad wird gesichert, damit die Originaleinstellungen bei Bedarf wiederhergestellt werden können). 2.Sei löscht HKEY_CLASSES_ROOT\VBSFile\Shell. 3.Sie erstellt einen neuen Eintrag (NoOpen) in HKEY_CLASSES_ROOT\VBSFile und konfiguriert die gewünschte Warnmeldung als Wert für diesen Eintrag - in dieser BeispielBatchdatei lautet diese Warnmeldung 'Führen Sie dieses Script nicht aus, wenn es nicht von der IT-Abteilung freigegeben wurde.' (die Länge der Warnmeldung ist auf 140 Zeichen begrenzt). Eine Beschränkung für andere Scriptdateien (zum Beispiel .VBE, .JS, .JSE und .WSF) können Sie auf ähnliche Weise konfigurieren. Wichtig: Wenn Sie die Warnmeldung verwenden, könnte dies zu Problemen bei An- und Abmeldescripten führen - nämlich dann, wenn bei der Scriptausführung die Warnmeldung angezeigt wird. Um dieses Problem zu umgehen, können Sie das Script zum Beispiel aus einer Batchdatei heraus starten.
Deaktivieren des Windows Script Host Wenn erforderlich, können Sie den Windows Script Host auch deaktivieren - so können Sie sicherstellen, dass keine Scripte mehr ausgeführt werden, die vom WSH abhängig sind (inklusive VBScript und JScript). Um den Windows Script Host zu deaktivieren, erstellen Sie einen der beiden folgenden Registrierungseinträge (REG_DWORD) und setzen seinen Wert auf 0. Um den WSH für einen bestimmten Benutzer zu deaktivieren, verwenden Sie den folgenden Eintrag: HKEY_CURRENT_USER\Software\Microsoft\Windows Script Host\Settings\Enabled
Um den WSH für alle Benutzer des Computers zu deaktivieren, verwenden Sie diesen Eintrag: HKEY_CURRENT_USER\Software\Microsoft\Windows Script Host\Settings\Enabled
Wenn der WSH deaktiviert ist, sehen Sie die folgende Fehlermeldung, wenn Sie versuchen ein WSH-Script zu starten: Der Zugriff auf Windows Script Host wurde auf diesem Computer deaktiviert. Für weitere Informationen wenden Sie sich an Ihren Administrator.
Die Meldung wird auch dann angezeigt, wenn der Benutzer versucht, ein Script über die Kommandozeile zu starten (zum Beispiel über den Befehl cscript.exe c:\scripts\myscript.vbs).
Scripting-Konzepte und -Technologien zur Systemadministration - Kapitel 4 - Die Script-Laufzeitbibliothek Veröffentlicht: 26. Apr 2004
Seite 190 von 394
Die Verwaltung des Dateisystems ist ein wichtiger Teil der Systemadministration - und leider bieten in diesem Bereich weder der Windows Script Host (WSH) noch Microsoft Visual Basic Scripting Edition (VBScript) sehr viele Möglichkeiten. Glücklicherweise können Sie zur Verwaltung von Laufwerken, Ordnern und Dateien die Script-Laufzeitbibliothek (Script Runtime Library) verwenden. Sie stellt Methoden zum Lesen und Schreiben in und aus Textdateien, zum Erstellen von "Verzeichnissen" und zum Codieren von Scripten zur Verfügung. (Engl. Originaltitel: Script Runtime Primer)
Script Laufzeitbibliothek-Übersicht Es gibt zwei primäre Microsoft-Scriptsprachen: Microsoft Visual Basic Scripting Edition (VBScript) und Microsoft JScript. Diese wurden ursprünglich als clientseitige Scriptsprachen für den Microsoft Internet Explorer entworfen. Daher gibt es einige Einschränkungen bei den beiden Sprachen. Weder VBScript noch JScript verfügen zum Beispiel über Methoden, um Dateioperationen wie das Kopieren, Verschieben oder Löschen von Dateien durchzuführen. Diese Einschränkungen wurden ursprünglich zum Schutz der Kunden durchgeführt: Die meisten Besucher einer Website fänden es sicher nicht sehr nett, wenn ein Script auf einer Webseite Dateien von ihrer Festplatte löschen würde. Die Scripting-Technologie hat sich jedoch von einer clientseitigen Technologie, die hauptsächlich für Webseitenelemente verwendet wurde, deutlich weiterentwickelt. Mit der Einführung von ASP (Active Server Pages) benötigten Webentwickler, die Möglichkeit Dateioperationen durchzuführen - und mit der Einführung des WSH (Windows Script Host) wurden Dateioperationen außerhalb des Webbrowsers erforderlich. Als Reaktion auf diese Anforderungen veröffentlichte Microsoft die Script Runtime Library. Hierbei handelt es sich um eine einzelne DLL (dynamic-link library) mit dem Namen scrrun.dll, die Scriptautoren in die Lage versetzt, mit dem Dateisystem zu arbeiten. Sie implementiert die folgenden Möglichkeiten: • • •
Abrufen von Informationen über Elemente des Dateisystems (inklusive Laufwerke, Dateien und Ordner) Kopieren, Verschieben und Löschen von Dateien und Ordnern Erstellen, Lesen und Schreiben von Textdateien
Außerdem ermöglicht die Script Runtime Library das Erstellen von Dictionaries (Datenstrukturen, die wie Collections funktionieren) und das Kodieren von Scripten (kodierte Scripte können nicht ohne weiteres gelesen werden und schützen so Ihr geistiges Eigentum). Anmerkung: In diesem Kapitel werden die Objekte FileSystemObject und Dictionary besprochen. Das Script Encoder-Objekt wird jedoch nicht behandelt. Die Script Runtime Library ist Teil von Windows 2000. Sie wird auch zusammen mit bestimmten Microsoft-Anwendungen installiert. Hierzu zählen unter anderem: • •
Windows Script Host VBScript
Seite 191 von 394
• •
Internet Explorer Microsoft Office
Das Objekt FileSystemObject Wie der Name (FileSystemObject - Dateisystemobjekt) schon andeutet, hilft Ihnen das Objekt FileSystemObject (FSO) bei der Arbeit mit dem Dateisystem. Es ermöglicht Ihnen, grundlegende Informationen über Dateisystemelemente wie Laufwerke, Ordner und Dateien abzurufen, und stellt außerdem Methoden zur Durchführung von administrativen Aufgaben zur Verfügung - zum Beispiel dem Kopieren, Löschen und Verschieben von Dateien und Ordnern. Der Name FileSystemObject ist nicht ganz passend - denn das Objekt setzt sich eigentlich aus mehreren Objekten zusammen. Die einzelnen Objekte von FileSystemObject sehen Sie in Tabelle 4.1. Tabelle 4.1: Die einzelnen Objekte des Objekts FileSystemObject Objekt
Beschreibung
Drive
Stellt den Zugriff auf ein Laufwerk oder eine Collection von Laufwerken zur Verfügung.
File
Stellt den Zugriff auf eine Datei oder eine Collection von Dateien zur Verfügung.
Folder
Stellt den Zugriff auf einen Ordner Laufwerk oder eine Collection von Ordnern zur Verfügung.
TextStreamStellt eine Quelle zur Verfügung, aus der Text gelesen werden kann, in die Text geschrieben werden kann, oder an die Text angehängt werden kann. Die einzelnen Objekte werden in diesem Kapitel genauer besprochen.
Verwalten von Laufwerken Als Systemadministrator müssen Sie wissen, welche Laufwerke auf einem Computer installiert sind, und Sie müssen deren Eigenschaften abfragen können - zum Beispiel Laufwerkstyp (Diskettenlaufwerk, Festplatte, CD-ROM), Laufwerksgröße und den freien Speicherplatz des Laufwerks. Als Scriptautor haben Sie zwei primäre Optionen bei der Verwaltung von Laufwerken: das Objekt FileSystemObject und WMI (Windows Management Instrumentation). Im Allgemeinen ist es besser, die Laufwerksverwaltung über WMI durchzuführen - und zwar aus den folgenden Gründen: •
• •
WMI kann einige Eigenschaften zur Verfügung stellen, die Ihnen über das FileSystemObject nicht zur Verfügung stehen - zum Beispiel die physikalischen Eigenschaften eines Laufwerks (Köpfe, Sektoren und Zylinder). WMI kann wahlweise nur bestimmte Laufwerkstypen zurückgeben (zum Beispiel nur Festplatten). Mit dem Objekt FileSystemObject ist dies nicht möglich. Mit WMI können Sie Laufwerksinformationen von Remotecomputern abfragen. Mit Seite 192 von 394
FileSystemObject ist dies nur zusammen mit dem Objekt WshController möglich. Auch wenn WMI also die bessere Technologie für solche Zwecke darstellt, gibt es doch zwei Gründe dafür, dass Sie sich mit dem Objekt FileSystemObject auskennen sollten. Erstens kann es auch auf älteren Computern ohne WMI verwendet werden (zum Beispiel Microsoft® Windows® 98) - auch wenn es für einige ältere Betriebssysteme möglich ist, WMI nachträglich zu installieren. Und zweitens wird das Objekt FileSystemObject seit langer Zeit von vielen Scriptautoren verwendet. Es ist daher sehr wahrscheinlich, dass Sie auf das Objekt stoßen werden, wenn Sie Scripte anderer Autoren lesen und verwenden.
Eine Collection mit Laufwerken abrufen Bevor Sie die Laufwerke eines Computers verwalten können, müssen Sie wissen, welche Laufwerke überhaupt zur Verfügung stehen. Das Objekt FileSystemObject stellt Ihnen hierzu einen Collection mit allen verfügbaren Laufwerken zur Verfügung - uns zwar inklusive aller Laufwerke mit Wechseldatenträgern und zugeordneten Netzlaufwerke (mit anderen Worten: jedes Laufwerk mit einem Laufwerksbuchstaben). Um diese Collection zu erhalten, erstellen Sie erst eine Instanz von FileSystemObject und eine Referenz auf die Eigenschaft Drives. Dann können Sie diese Referenz (Drives ist die Collection) für eine Auflistung aller Elemente der Collection nutzen. Script 4.1 ruft eine Collection mit allen auf dem Computer installierten Laufwerken ab und gibt für jedes Element der Collection den Laufwerksbuchstaben zurück. Script 4.1: Auflisten aller auf einem Computer installierten Laufwerke 14 25 3Set objFSO = CreateObject("Scripting.FileSystemObject") 4Set colDrives = objFSO.Drives 5For Each objDrive in colDrives 6 Wscript.Echo "Laufwerksbuchstabe: " & objDrive.DriveLetter 7Next
Eine komplette Liste aller Eigenschaften des Objekts FileSystemObject finden Sie in Tabelle 4.2 weiter unten in diesem Kapitel.
Binden an ein bestimmtes Laufwerk Wenn Sie bereits wissen, mit welchem Laufwerk Sie arbeiten möchten (zum Beispiel mit Laufwerk C oder dem Netzlaufwerk \\accounting\\receivables, dann können Sie die Methode GetDrive verwenden, um eine Referenz auf dieses Laufwerk zu erhalten. Sie müssen so nicht die gesamte Collection durchgehen. Die Methode GetDrive erwartet einen Parameter - den Laufwerksbuchstaben oder den UNCPfad des Netzlaufwerks. Den Laufwerksbuchstaben können Sie in unterschiedlichen Varianten angeben: • • •
C C: C:\
Seite 193 von 394
Script 4.2 erstellt eine Instanz von FileSystemObject, verwendet die Methode GetDrive, um eine direkte Referenz auf Laufwerk C zu erhalten und gibt dann den freien Speicherplatz dieses Laufwerks aus. Script 4.2: Eine Referenz auf ein einzelnes Laufwerk 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objDrive = objFSO.GetDrive("C:") 3Wscript.Echo "Verfügbarer Speicherplatz: " & objDrive.AvailableSpace
Wie Sie sehen, ist keine For-Each-Schleife erforderlich. Das liegt daran, dass GetDrive ein einzelnes Objekt vom Typ Drive statt ein Objekt (Collection) vom Typ Drives zurückgibt.
Auflisten der Laufwerkseigenschaften Die Collection Drives wird meist zur Inventarisierung oder Überwachung verwendet - als Systemadministrator müssen Sie wissen, welche Laufwerke auf bestimmten Computern zur Verfügung stehen und außerdem über detaillierte Informationen zu diesen Laufwerken verfügen (zum Beispiel die Seriennummern oder den freien Speicherplatz). Mit einer DrivesCollection oder einem einzelnen Drive-Objekt können Sie die in Tabelle 4.2 aufgelisteten Eigenschaften abfragen. Tabelle 4.2: Eigenschaften des Objekts Drive Eigenschaft
Beschreibung
AvailableSpaceDer freie Speicherplatz auf dem Laufwerk in Byte. Um den freien Speicherplatz in KB zu erhalten, teilen Sie diesen Wert durch 1.024 (es handelt sich um den Speicherplatz, der dem aktuellen Benutzer zur Verfügung steht - wenn Kontingente verwendet werden, kann der Wert kleiner sein als der gesamte zur Verfügung stehende freie Speicherplatz). DriveLetter
Der dem Laufwerk zugewiesene Laufwerksbuchstabe ohne den angehängten Doppelpunkt. Für das Diskettenlaufwerk wird also zum Beispiel der Buchstabe A verwendet.
DriveType
Ein Integer-Wert, der den Laufwerkstyp angibt: 1 - Wechsellaufwerk 2 - Festplatte 3 - Netzlaufwerk 4 - CD-ROM 5 - RAM-Laufwerk
FreeSpace
Im Gegensatz zu AvailableSpace gibt diese Eigenschaft den gesamten zur Verfügung stehenden freien Festplattenplatz in Byte zurück.
FileSystem
Der Typ des verwendeten Dateisystems (FAT, FAT 32, NTFS).
IsReady
Gibt an, ob auf das Laufwerk zugegriffen werden kann. Wenn sich zum Beispiel keine CD im CD-Laufwerk oder keine Diskette im Diskettenlaufwerk Seite 194 von 394
Eigenschaft
Beschreibung
befindet, hat diese Eigenschaft den Wert False. Path
Pfad zum Laufwerk. Bei lokalen Laufwerken finden Sie unter dieser Eigenschaft den Laufwerksbuchstaben (zum Beispiel A). Bei Netzlaufwerken gibt die Eigenschaft den UNC-Pfad des Laufwerkes zurück (zum Beispiel \\Server1\SharedFolder).
RootFolder
Der Pfad zum Stammordner des Laufwerks.
SerialNumber Die Seriennummer des Laufwerks. Bei Diskettenlaufwerken oder Netzlaufwerken hat diese Eigenschaft normalerweise den Wert 0. ShareName
Freigabename eines Netzwerklaufwerks.
TotalSize
Gibt die Gesamtgröße des Laufwerks in Byte zurück (um die Gesamtgröße in KB zu erhalten, teilen Sie den Wert durch 1.024. Für die Gesamtgröße in MB teilen Sie den Wert durch 1.048.576 (1.024 x 1.024).
VolumeName Der Volumenname des Laufwerks. Um die Laufwerke eines Computers aufzulisten erstellen Sie eine Instanz von FileSystemObject und eine Referenz auf die Eigenschaft Drives und verwenden dann eine For-Each-Schleife, um alle Elemente der Collection zu durchlaufen. In der Schleife können Sie für die einzelnen Laufwerke jeweils alle Eigenschaften ausgeben. Script 4.3: Auflisten der Laufwerkseigenschaften 1 Set objFSO = CreateObject("Scripting.FileSystemObject") 2 Set colDrives = objFSO.Drives 3 For Each objDrive in colDrives 4 Wscript.Echo "Verfügbarer Speicherplatz: " & objDrive.AvailableSpace 5 Wscript.Echo "Laufwerksbuchstabe: " & objDrive.DriveLetter 6 Wscript.Echo "Laufwerkstyp: " & objDrive.DriveType 7 Wscript.Echo "Dateisystem: " & objDrive.FileSystem 8 Wscript.Echo "Bereit: " & objDrive.IsReady 9 Wscript.Echo "Pfad: " & objDrive.Path 10 Wscript.Echo "Stammordner: " & objDrive.RootFolder 11 Wscript.Echo "Seriennummer: " & objDrive.SerialNumber 12 Wscript.Echo "Freigabename: " & objDrive.ShareName 13 Wscript.Echo "Gesamtgröße: " & objDrive.TotalSize 14 Wscript.Echo "Volumenname: " & objDrive.VolumeName 15Next
Wenn Sie Script 4.3 unter CScript ausführen, erhalten Sie eine ähnliche Ausgabe wie die folgende: Verfügbarer Speicherplatz: 5422272512 Laufwerksbuchstabe: C Laufwerkstyp: 2 Dateisystem: NTFS Bereit: Wahr Pfad: C: Stammordner: C:\ Seriennummer: 2018221812 Freigabename: Gesamtgröße: 15356563456 Volumenname: Festplatte
Seite 195 von 394
Prüfen, ob ein Laufwerk bereit ist Script 4.3 hat eine potentielle Fehlerquelle. Wenn sich keine Diskette im Diskettenlaufwerk oder keine CD im CD-ROM befindet, dann schlägt das Script mit der Fehlermeldung Laufwerk nicht bereit fehl. Wenn ein Laufwerk nicht bereit ist (normalerweise, weil kein Medium eingelegt ist), dann können Sie nur die folgenden Eigenschaften ohne Fehler abfragen: • • • •
DriveLetter DriveType IsReady ShareName
Glücklicherweise haben Sie über die Eigenschaft IsReady die Möglichkeit festzustellen, ob ein Laufwerk tatsächlich nicht bereit ist. Wenn ihr Wert True ist, dann können Sie ohne Gefahr alle anderen Eigenschaften abrufen. Script 4.4 ruft eine Collection mit den verfügbaren Laufwerken ab. Für jedes Laufwerk prüft das Script über die Eigenschaft IsReady, ob es die restlichen Eigenschaften ohne Fehler abfragen kann. Wenn dies möglich ist, gibt das Script den Laufwerksbuchstaben und den freien Speicherplatz des Laufwerks zurück. Wenn das Laufwerk nicht bereit ist, dann gibt das Script nur den Laufwerksbuchstaben zurück (eine der vier Eigenschaften, die auch in diesem Zustand gefahrlos abgefragt werden kann). Script 4.4: Prüfen, ob ein Laufwerk bereit ist 1 Set objFSO = CreateObject("Scripting.FileSystemObject") 2 Set colDrives = objFSO.Drives 3 For Each objDrive in colDrives 4 If objDrive.IsReady = True Then 5 Wscript.Echo "Drive letter: " & objDrive.DriveLetter 6 Wscript.Echo "Free space: " & objDrive.FreeSpace Else 7 8 Wscript.Echo "Drive letter: " & objDrive.DriveLetter 9 End If 10Next
Anmerkung: Mit WMI gibt es solche Probleme nicht. Wenn sich kein Medium im Laufwerk befindet schlägt das Script hier nicht fehl - stattdessen gibt WMI den freien Speicherplatz einfach mit Null an.
Verwalten von Ordnern Wenn Sie wissen in welchem Laufwerk eine Datei gespeichert ist, dann benötigen Sie im nächsten Schritt den Ordner, in der die Datei gespeichert ist. Bei vielen anderen Aufgaben der Systemadministration müssen Sie die Inhalte von Ordnern auflisten, Ordner kopieren, Ordner anlegen und sie auch wieder löschen.
Seite 196 von 394
Das Objekt FileSystemObject stellt Ihnen nicht nur detaillierte Informationen zu einem Ordner zur Verfügung, sondern bietet Ihnen auch Möglichkeiten zum Kopieren, Verschieben und Löschen an. Außerdem können Sie die Dateien und Ordner in einem Ordner auflisten.
Eine Referenz auf einen Ordner erstellen In der Windows-Shell sind Ordner COM-Objekte. Das bedeutet, dass Sie vor einem Zugriff auf die Eigenschaften eines Ordners eine Objektreferenz auf diesen Ordner erstellen müssen. Dies können Sie über die Methode FileSystemObject.GetFolder durchführen. Für die Methode GetFolder benötigen Sie den Pfad des Ordners. Erkann entweder als lokaler Pfad oder als UNC-Pfad angegeben werden (zum Beispiel \\accounting\receivables). Sie dürfen keine Wildcards verwenden. Außerdem können Sie keine Referenz auf mehrere Ordner erstellen. Die folgende Codezeile führt also zu einem Fehler: objFSO.GetFolder("C:\FSO", "C:\Scripts")
Wenn Sie mit mehreren Ordnern arbeiten wollen, dann müssen Sie entweder WMI verwenden oder für jeden Ordner eine separate Referenz erstellen. Script 4.5 erzeugt zum Beispiel eine Referenz auf den Ordner C:\FSO und weist diese der Variable objFolder zu. Script 4.5: Erstellen einer Referenz auf einen Ordner 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFolder = objFSO.GetFolder("C:\FSO")
Mit dem Punkt (.) können Sie eine Referenz auf den aktuellen Ordner erstellen, und mit zwei Punkten (..) erstellen Sie eine Referenz auf den dem aktuellen Ordner übergeordneten Ordner. Mit dem Backslash schließlich erstellen Sie eine Referenz auf den Stammordner. Die folgende Codezeile erstellt zum Beispiel eine Referenz auf den aktuellen Ordner: Set objFolder = objFSO.GetFolder(".")
Prüfen, ob ein Ordner vorhanden ist Bei den meisten Ordneroperationen muss der Ordner bereits vorhanden sein - ein Script kann einen Ordner, der nicht vorhanden ist nicht kopieren, verschieben oder löschen. Ein solcher Versuch wird zur Fehlermeldung Pfad nicht gefunden führen. Um solche Probleme zu vermeiden, können Sie mit der Methode FolderExists überprüfen, ob ein Ordner schon vorhanden ist. Die Methode benötigt einen Parameter: den Pfad des Ordners. Sie gibt einen Boolean-Wert zurück. Wenn der Rückgabewert True ist, dann ist der Ordner vorhanden - bei False nicht. Script 4.6 verwendet die Methode FolderExists, um die Existenz von Ordner C:\FSO zu überprüfen. Wenn die Methode den Wert True zurückgibt, dann erstellt das Script eine Referenz auf den Ordner. Andernfalls gibt das Script die Nachricht "Ordner ist nicht vorhanden zurück". Script 4.6: Überprüfen, ob ein Ordner vorhanden ist 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2If objFSO.FolderExists("C:\FSO") Then 3 Set objFolder = objFSO.GetFolder("C:\FSO") 4 Wscript.Echo "Referenz erstellt."
Seite 197 von 394
5Else 6 Wscript.Echo "Ordner ist nicht vorhanden zurück? 7End If
Einen Ordner erstellen Das Objekt FileSystemObject ermöglicht es Ihnen über Ihr Script neue Ordner anzulegen. Script 4.6 prüft zum Beispiel, ob ein bestimmter Ordner vorhanden ist. Wenn der Ordner existiert, dann verwendet es die Methode GetFolder, um eine Referenz auf den Ordner zu erstellen. Wenn der Ordner nicht vorhanden ist, dann gibt das Script eine Benachrichtigung aus. Auch wenn das Script so daran gehindert wird einen Fehler zu produzieren, ist dies wohl kaum die beste Lösung. Statt den Benutzer einfach darüber zu informieren, dass der Ordner nicht vorhanden ist, wäre es möglicherweise besser, den Ordner anzulegen. Hierzu kann das Script zum Beispiel die Methode FileSystemObject.CreateFolder verwenden. Sie erwartet als Parameter den vollständigen Pfad des neuen Ordners. Script 4.7 erstellt zum Beispiel einen Ordner mit dem Namen C:\FSO. Script 4.7: Einen neuen Ordner erstellen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFolder = objFSO.CreateFolder("C:\FSO")
Wenn der zu erstellende Ordner schon vorhanden ist, dann wird ein Fehler erzeugt ('Datei ist bereits vorhanden'). Daher sollten Sie vorher prüfen, ob der neue Ordner bereits vorhanden ist. Anmerkung: Das Objekt FileSystemObject kann nur Ordner auf dem lokalen Computer erstellen. Wenn Sie Ordner auf einem Remotecomputer erstellen müssen, dann müssen Sie das Objekt WshController verwenden. Alternativ können Sie den Ordner lokal erstellen und ihn dann über WMI auf den Remotecomputer verschieben (den Ordner müssen Sie jedoch über FileSystemObject erstellen, da WMI keine Ordner erstellen kann).
Löschen eines Ordners Manchmal ist es erforderlich, einen Ordner zu löschen. Zum Beispiel, wenn Sie einen temporären Ordner entfernen möchten. Hierzu können Sie die Methode DeleteFolder verwenden. Sie benötigt als Parameter den Pfad des zu löschenden Ordners. Script 4.8 löscht zum Beispiel den Ordner C:\FSO und seine gesamten Inhalte. Script 4.8: Löschen eines Ordners 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.DeleteFolder("C:\FSO")
Die Methode DeleteFolder löscht den Ordner und alle Inhalte sofort. Es wird keine Bestätigung angefordert, und die Elemente werden auch nicht im Papierkorb abgelegt. Ordner über Wildcards löschen
Seite 198 von 394
Ein Hauptvorteil der Verwaltung über Scripte ist, dass sie viele Aufgaben auf einmal erledigen können. Statt also viele Ordner einen nach dem anderen zu löschen, können Sie diese Aufgabe einfach in einem Schritt über ein Script durchführen. Das Objekt FileSystemObject erlaubt es Ihnen, Ordner über Wildcards zu löschen. Stellen Sie sich zum Beispiel vor, Sie haben es mit der Ordnerstruktur aus Abbildung 4.1 zu tun und möchten alle Unterordner löschen, die mit einem U anfangen.
Abbildung 4.1: Beispiel-Ordnerstruktur Eine solche Aufgabe können Sie über die folgende Scriptzeile ausführen: objFSO.DeleteFolder("C:\FSO\U*")
Sie löscht die Ordner Unterordner 1 und Unterordern 2. Wildcards können nur im letzten Teil des Pfades verwendet werden. Die folgende Scriptzeile wird also zum Fehler 'Pfad nicht gefunden' führen: objFSO.DeleteFolder("C:\*\Unterordern 1")
Ordner und deren Inhalte kopieren Über die Methode CopyFolder können Sie Ordner und deren Inhalte kopieren. Wenn Sie keine Wildcards verwenden, funktioniert die Methode wie der Befehl Xcopy /E: sie kopiert alle Dateien und alle Unterordner, inklusive aller leeren Unterordner. Die Methode benötigt zwei Parameter: •
Quellordner - der zu kopierende Ordner. Dieser Ordner kann entweder als lokaler Pfad (C:\Scripts) oder als UNC-Pfad (\\helpdesk\scripts) angegeben werden.
•
Zielordner - der Ordner, in dem die Kopien erstellt werden. Auch er kann entweder als lokaler Pfad oder als UNC-Pfad angegeben werden. Wenn der Zielordner nicht vorhanden ist, dann erstellt das Script diesen automatisch.
Die Methode CopyFolder akzeptiert noch einen dritten optionalen Parameter: Überschreiben. Wenn Sie diesen Parameter auf True setzen (das ist die Standardeinstellung), dann werden alle bestehenden Ordner im Zielordner überschrieben. Wenn Sie den Parameter auf False setzen, werden die vorhanden Ordner nicht überschrieben - stattdessen tritt ein Laufzeitfehler auf. Anmerkung: Die Methode CopyFolder hält in dem Moment an, in dem sie auf einen Fehler stößt - auch wenn der Befehl On Error Resume Next verwendet wird. Wenn also von 100 zu kopierenden Ordnern erst drei kopiert wurden, bevor ein Fehler auftritt, dann werden die restlichen 97 Ordner nicht mehr kopiert. Script 4.9 verwendet die Methode CopyFolder, um die Inhalte von C:\Scripts nach C:\FSO zu kopieren. Hierbei werden alle bestehenden Ordner im Zielordner überschrieben. Es wird kein Ordner mit dem Namen C:\FSO\Scripts erstellt - stattdessen enthält der Ordner C:\FSO alle
Seite 199 von 394
Dateien und Ordner aus C:\Scripts. Um einen Ordner mit dem Namen C:\FSO\Scripts zu erstellen, müssen Sie als Zielordner C:\FSO\Scripts angeben. Script 4.9: Einen Ordner kopieren 1Const OverWriteFiles = True 2Set objFSO = CreateObject("Scripting.FileSystemObject") 3objFSO.CopyFolder "C:\Scripts" , "C:\FSO" , OverWriteFiles
Anmerkung: Da es sich bei CopyFolder um einen einzelnen Vorgang handelt, gibt es keine Möglichkeit, ihren Fortschritt zu überwachen. Sie können nur abwarten, bis die Kopieroperation beendet ist. Wenn Sie den Fortschritt der Kopieroperation überwachen möchten, dann sollten Sie stattdessen das Objekt Shell.Application verwenden. Dieses Objekt wird im Abschnitt Dateien und Ordner besprochen. Ordner mit Hilfe von Wildcards kopieren Die Methode CopyFolder kopiert alle Dateien und alle Unterordner eines Ordners. Dieses Verhalten kann jedoch problematisch sein - vielleicht möchten Sie nur die Dateien im Ordner C:\FSO und nicht die in C:\FSO\Subfolder1, C:\FSO\Subfolder2 und C:\FSO\Subfolder3 gespeicherten Dateien kopieren? Unglücklicherweise gibt es keine einfache Methode, um nur die Dateien im Ordner ohne die Unterordner zu kopieren. Sie können jedoch Wildcards verwenden. Die folgende Scriptzeile kopiert zum Beispiel nur die Ordner, die mit den Buchstaben log beginnen: objFSO.CopyFolder "C:\Scripts\Log*" ,
"C:\Archive",
True
Wenn die Codezeile ausgeführt wird, dann werden die Ordner C:\Scripts\Logs und C:\Scripts\Logfiles kopiert - zusammen mit den in ihnen gespeicherten Dateien und Unterordnern. Die Dateien im Ordner C:\Scripts werden jedoch nicht kopiert (unabhängig von deren Namen). Es ist mit CopyFolder nicht möglich, nur die Dateien ohne Ordner zu kopieren. Hierzu müssen Sie die CopyFile verwenden - sie wird später in diesem Kapitel besprochen.
Verschieben von Ordnern und deren Inhalten Zum Verschieben von Ordnern verwenden Sie die Methode MoveFolder. Sie akzeptiert zwei Parameter: •
Quellordner - der zu verschiebende Ordner. Dieser Ordner kann entweder als lokaler Pfad oder als UNC-Pfad angegeben werden.
•
Zielordner - der Ordner, in den die Quellordner verschoben werden. Auch er kann entweder als lokaler Pfad oder als UNC-Pfad angegeben werden. Wenn der Zielordner nicht vorhanden ist, dann erstellt das Script diesen automatisch.
Wenn der Zielordner noch nicht vorhanden ist, dann wird der Quellordner verschoben. Wenn der Zielordner schon existiert, schlägt die Methode fehl. Sie können mit der Methode keinen bestehenden Ordner überschreiben. Script 4.10 verschiebt den lokalen Ordner C:\Scripts in die Freigabe \\helpdesk\management. Script 4.10: Verschieben eines Ordners Seite 200 von 394
1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.MoveFolder "C:\Scripts" , "\\helpdesk\management"
Bedenken Sie, dass Sie bei einem Fehler keine Möglichkeit haben, die Aktionen von MoveFolder rückgängig zu machen. Wenn zum Beispiel während des Verschiebens in eine Netzwerkfreigabe die Netzwerkverbindung getrennt wird, bleiben einige Dateien auf Computer A, einige Dateien wurden auf Computer B verschoben und einige Dateien sind möglicherweise während des Verschiebens verloren gegangen. Es gibt keine Möglichkeit den ursprünglichen Zustand wiederherzustellen. Daher sollten Sie den Verschiebevorgang besser über zwei einzelne Methoden durchführen: CopyFolder und DeleteFolder. Sie löschen den Quelleordner über die Methode DeleteFolder erst dann, wenn die Methode CopyFolder erfolgreich ausgeführt wurde.
Ordner umbenennen Das Objekt FileSystemObject stellt keine Methode zum Umbenennen von Ordnern zu Verfügung. Sie können eine solche Operation jedoch über die Methode MoveFolder durchführen. Stellen wir uns vor, Sie haben den folgenden Pfad: C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Current Logs
Wenn Sie den Ordner über den Windows Explorer umbenennen, bleibt der Pfad gleich - das Einzige, was sich ändert, ist der letzte Teil: C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Archived Logs
Mit MoveFolder können Sie das gleiche erreichen, indem Sie den Ordner von C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Current Logs nach C:\Scripts\PerformanceMonitoring\Servers\Domain Controllers\Archived Logs verschieben. Das Endergebnis ist exakt das gleiche. Script 4.11 verwendet MoveFolder, um den Ordner C:\FSO\Samples in C:\FSO\Scripts umzubenennen. Script 4.11: Ordner über die Methode MoveFolder umbenennen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.MoveFolder "C:\FSO\Samples" , "C:\FSO\Scripts"
Verwenden von Ordnereigenschaften Da es sich bei Ordnern um COM-Objekte handelt, verfügen diese natürlich auch über Eigenschaften und sie können aufgelistet werden. Um Informationen über einen bestimmten Ordner abzurufen, können Sie das Objekt Folder verwenden - dieses finden Sie unter dem Objekt FileSystemObject. Die Eigenschaften des Folder-Objekts sehen Sie in Tabelle 4.3. Tabelle 4.3: Eigenschaften des Objekts Folder Eigenschaft
Beschreibung
Attributes
Ein Bitfeld mit den Attributen des Ordners. Weitere Informationen finden Sie im Abschnitt Verwalten von Ordnerattributen in diesem Kapitel.
DateCreated
Erstellungsdatum des Ordners. Seite 201 von 394
Eigenschaft
Beschreibung
DateLastAccessedLetztes Zugriffsdatum. DateLastModified Letztes Bearbeitungsdatum. Drive
Laufwerksbuchstabe mit Doppelpunkt (zum Beispiel C:) des Laufwerks, in dem der Ordner gespeichert ist.
Files
Eine Collection mit Objekten vom Typ File zum Zugriff auf die im Ordner gespeicherten Dateien.
IsRootFolder
Ein Boolean-Wert, der anzeigt, ob es sich um einen Stammordner (wie zum Beispiel C:\) handelt.
Name
Name des Ordners ohne Pfadangaben (zum Beispiel System32).
ParentFolder
Name des Ordners, in dem der Ordner gespeichert ist. Für den Ordner C:\Scripts wäre dies zum Beispiel C:\.
Path
Vollständiger Pfad des Ordners (zum Beispiel C:\Windows\System32).
ShortName
Ordnername in der MS-DOS-Syntax mit der 8.3-Namenskonvention. Der Ordner C:\Windows\Programme wird zum Beispiel als Progra~1 angezeigt
ShortPath
Pfadname des Ordners in der MS-DOS-Syntax. Der Ordner C:\Windows\Programme wird zum Beispiel als C:\Windows\Progra~1 angezeigt.
Size
Gesamtgröße in Byte aller Inhalte des Ordners. Dies schließt die in dem Ordner gespeicherten Dateien und alle Dateien in Unterordnern ein.
SubFolders
Eine Collection mit den Unterordnern des Ordners. Die Unterordner in den Unterordnern sind nicht in dieser Collection enthalten.
Type
Ein String mit einer Beschreibung des Ordnertyps - meist File Folder.
Auflisten von Ordnereigenschaften Um die Eigenschaften eines Ordners abzufragen, muss ein Script folgendermaßen vorgehen: 1.Eine Instanz des Objekts FileSystemObject erstellen. 2.Eine Referenz auf den jeweiligen Ordner über die Methode GetFolder erzeugen. 3.Die Eigenschaften aus Tabelle 4.3 ausgeben. Bei der Arbeit mit den Eigenschaften eines Ordners sollten Sie bedenken, dass die Eigenschaften Files und Subfolders beide eine Collection zurückgeben. Außerdem handelt es sich bei der Eigenschaft Attributes um ein Bitfeld. Wie Sie mit diesen Eigenschaften umgehen, erfahren Sie in den folgenden Abschnitten dieses Kapitels. Script 4.12 verwendet die Methode GetFolder, um eine Referenz auf den Ordner C:\FSO zu erstellen und dann die Eigenschaften des Ordners auszugeben. Script 4.12: Eigenschaften eines Ordners abfragen Seite 202 von 394
1 Set objFSO = CreateObject("Scripting.FileSystemObject") 2 Set objFolder = objFSO.GetFolder("C:\FSO") 3 Wscript.Echo "DateCreated: " & objFolder.DateCreated 4 Wscript.Echo "DateLastAccessed: " & objFolder.DateLastAccessed 5 Wscript.Echo "DateLastModified: " & objFolder.DateLastModified 6 Wscript.Echo "Drive: " & objFolder.Drive 7 Wscript.Echo "IsRootFolder: " & objFolder.IsRootFolder 8 Wscript.Echo "Name: " & objFolder.Name 9 Wscript.Echo "ParentFolder: " & objFolder.ParentFolder 10Wscript.Echo "Path: " & objFolder.Path 11Wscript.Echo "ShortName: " & objFolder.ShortName 12Wscript.Echo "ShortPath: " & objFolder.ShortPath 13Wscript.Echo "Size: " & objFolder.Size 14Wscript.Echo "Type: " & objFolder.Type
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: DateCreated: 28.03.2004 15:56:33 DateLastAccessed: 28.03.2004 15:56:3 DateLastModified: 28.03.2004 15:56:3 Drive: C: IsRootFolder: Falsch Name: fso ParentFolder: C:\ Path: C:\fso ShortName: FSO ShortPath: C:\FSO Size: 0 Type: Dateiordner
Verwalten von Ordnerattributen Wenn Sie mit der rechten Maustaste auf einen Ordner klicken und Eigenschaften auswählen, dann können Sie die Attribute des Ordners festlegen. Wie Sie in Abbildung 4.2 sehen, stehen Ihnen die folgenden Attribute zur Verfügung: • • • •
Schreibgeschützt Versteckt Archiviert Komprimiert
Seite 203 von 394
Abbildung 4.2: Eigenschaften eines Ordners Die Attribute, die Sie über das Objekt FileSystemObject abfragen können, sehen Sie in Tabelle 4.4. Tabelle 4.4: Über FileSystemObject abfragbare Ordnerattribute Konstante Wert Beschreibung
Hidden
2
Versteckt - Zeigt an, dass der Ordner versteckt ist.
System
4
System - Zeigt an, dass es sich um einen Systemordner handelt.
Directory
16
Ordner - Der Standardwert für alle Ordner.
Archive
32
Archiv - Das Archiv-Attribut wird zum Beispiel von Sicherungsprogrammen verwendet. Diese stellt über das Attribut fest, welche Dateien und Ordner gesichert werden müssen. Seite 204 von 394
Konstante Wert Beschreibung
Compressed2048Komprimiert - Zeigt an, ob der Ordner komprimiert ist. Es könnte sein, dass Sie bei der Abfrage der Eigenschaft Attributes einige verwirrende Ergebnisse erhalten - zum Beispiel den Wert 20. Dieser Wert steht nicht in Tabelle 4.4. Außerdem erhalten Sie immer nur einen einzelnen Wert zurück - auch wenn der Ordner mehrere der in der Tabelle aufgelisteten Eigenschaften verwendet. Statt zum Beispiel einen Wert der Tabelle zu erhalten (2, 4, 16, 32, 2048), gibt die Abfrage der Eigenschaft den Wert 2102 zurück. Dieses Verhalten liegt daran, dass es sich bei dem zurückgegebenen Wert immer um ein Bitfeld handelt. Ein Bitfeld ist wie eine Reihe Schalter, die entweder an oder aus sein können. Wenn ein bestimmter Schalter aus ist, dann hat dieser Schalter den Wert 0. Wenn ein bestimmter Schalter an ist, dann hat er im Fall eines Ordners den Wert aus Tabelle 4.4. Der Wert des gesamten Bitfeldes setzt sich aus der Gesamtsumme aller Schalter-Werte zusammen. In Abbildung 4.3 sehen Sie zum Beispiel eine stark vereinfachte Darstellung eines Bitfeldes. In diesem Beispiel ist nur ein Schalter (Ordner) an - er halt den Wert 16 (siehe Tabelle 4.4). Da alle anderen Schalter aus sind haben Sie alle den Wert 0. Die Gesamtsumme aller Schalter ist also 16. Die Eigenschaft Attribute des entsprechenden Ordners hätte also den Wert 16.
Abbildung 4.3: Erstes Beispiel für ein Bitfeld Im Gegensatz dazu sind in Abbildung 4.4 drei Schalter aktiviert. Versteckt (mit dem Wert 2), Ordner (mit dem Wert 16) und Komprimiert (mit dem Wert 248). Der Gesamtwert dieses Bitfeldes wäre also 2 + 16 + 2048 = 2066. Für diesen Ordner hätte die Eigenschaft Attributes also den Wert 2066.
Abbildung 4.4: Zweites Beispiel für ein Bitfeld Bitfelder arbeiten so, dass jeder mögliche Wert nur mit einer einzigen Schalterkombination erreicht werden kann - der Wert 2066 ist nur mit den Schaltern Versteckt, Komprimiert und Ordner möglicht. Daher können Sie über den Wert der Eigenschaft Attributes ermitteln, welcher Schalter gesetzt ist. Glücklicherweise müssen Sie hierzu keine mathematischen Berechnungen durchführen oder alle möglichen Werte kennen. Stattdessen können Sie den logischen Operator AND verwenden - mit ihm können Sie feststellen, welche Schalter an bzw. aus sind. Das folgende Codestück prüft zum Beispiel, ob ein Ordner versteckt ist. Wenn dies der Fall ist, dann gibt das Script die Nachricht 'Versteckter Ordner' aus: If objFolder.Attributes AND 2 Then Wscript.Echo "Versteckter Ordner" End If
Der If-Befehl mag ein wenig komisch aussehen. Sie können ihn auch so lesen: 'Wenn in der Eigenschaft Attributes der Schalter mit dem Wert 2 (Hidden - siehe Tabelle 4.4) gesetzt ist, dann .' Der folgende Befehl bedeutet zum Beispiel: 'Wenn in der Eigenschaft Attributes der Schalter mit dem Wert 16 (System) gesetzt ist, dann .': Seite 205 von 394
If objFolder.Attributes AND 16 Then
Script 4.13 erstellt eine Referenz auf den Ordner C:\FSO und zeigt dessen Attribute an. Script 4.13: Auflisten von Ordnerattributen 1 Set objFSO = CreateObject("Scripting.FileSystemObject") 2 Set objFolder = objFSO.GetFolder("C:\FSO") 3 If objFolder.Attributes AND 2 Then Wscript.Echo "Versteckt" 4 5 End If 6 If objFolder.Attributes AND 4 Then 7 Wscript.Echo "System" 8 End If 9 If objFolder.Attributes AND 16 Then 10 Wscript.Echo "Ordner" 11End If 12If objFolder.Attributes AND 32 Then 13 Wscript.Echo "Archiv" 14End If 15If objFolder.Attributes AND 2048 Then 16 Wscript.Echo "Komprimiert" 17End If
Ändern von Ordnerattributen Sie können die im vorherigen Abschnitt kennen gelernten Attribute natürlich auch selbst aktivieren oder deaktivieren - ganz einfach indem Sie die Schalter anschalten oder abschalten. Am einfachsten geht das folgendermaßen: 1.Erstellen Sie eine Referenz auf den Ordner über die Methode GetFolder. 2.Prüfen Sie den Attributwert den Sie ändern möchten. Wenn Sie zum Beispiel das Attribut Versteckt eines Ordners ausschalten möchten, dann prüfen Sie, ob das Attribut im Moment angeschaltet ist. 3.Wenn der Ordner versteckt ist, dann verwenden Sie den logischen Operator XOR, um den Schalter auf aus umzustellen. Wenn der Ordner nicht versteckt ist, dann verwenden Sie XOR nicht - in diesem Fall würden Sie den Schalter sonst anschalten. Script 4.14 verwendet den Operator AND mit dem Wert 2(Hidden), um festzustellen, ob der Ordner C:\FSO versteckt ist. Wenn dies der Fall ist, dann verwendet das Script den logischen Operator XOR, um den entsprechenden Schalter auf aus umzustellen. Script 4.14: Ändern von Ordnerattributen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFolder = objFSO.GetFolder("C:\FSO") 3If objFolder.Attributes AND 2 Then 4 objFolder.Attributes = objFolder.Attributes XOR 2 5End If
Auflisten der Dateien in einem Ordner Nur zu wissen, dass ein Ordner vorhanden ist, ist meist eher nutzlos. Normalerweise wollen Sie wissen, welche Dateien in dem Ordner gespeichert sind. Hierzu stellt Ihnen das Objekt Folder eine Eigenschaft mit dem Namen Files zur Verfügung. Diese gibt einen Collection mit Seite 206 von 394
allen Dateien im entsprechenden Ordner zurück. Um diese Collection zu erhalten, gehen Sie folgendermaßen vor: 1.Erstellen Sie eine Instanz des Objekts FileSystemObject. 2.Verwenden Sie die Methode GetFolder, um eine Referenz auf den entsprechenden Ordner zu erstellen. 3.Erstellen Sie eine Objektreferenz auf die Eigenschaft Files des Ordnerobjekts. 4.Verwenden Sie eine For-Each-Schleife, um alle Dateien und deren Eigenschaften in der Collection Files zu durchlaufen (die Eigenschaften des Objekts Files sehen Sie in Tabelle 4.5.). Script 4.15 ruft die Collection Files des Ordnerobjekts C:\FSO ab und gibt dann die Eigenschaften Name und Größe (in Byte) jedes File-Objekts in der Collection aus. Script 4.15: Abfragen der Eigenschaften aller Dateien in einem Ordner 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFolder = objFSO.GetFolder("C:\FSO") 3Set colFiles = objFolder.Files 4For Each objFile in colFiles 5 Wscript.Echo objFile.Name, objFile.Size 6Next
Wie bei den meisten Collections haben Sie keine Kontrolle darüber, in welcher Reihenfolge die Informationen zurückgegeben werden - Sie können nicht angeben, dass die Dateien nach Name, Größe oder anderen Kriterien sortiert werden sollen. Wenn Sie die Dateien sortieren möchten, dann müssen Sie die Collection in ein Array, ein Dictionary-Objekt oder ein nicht verbundenes Recordset kopieren. Weitere Informationen hierzu finden Sie im Kapitel Creating Enterprise Scripts in diesem Buch.
Auflisten von Unterordnern Zusätzlich zu den in einem Ordner gespeicherten Dateien interessieren Sie sich sicher für die Unterordner eines Ordners. Hierzu stellt Ihnen das Folder-Objekt eine Eigenschaft mit dem Namen Subfolders zur Verfügung. Über diese Eigenschaft erhalten Sie eine Collection mit den direkten Unterordnern des Ordners. Die Unterordner der Unterordner sind nicht in der Collection enthalten. Bei der folgenden Ordnerstruktur enthält die Collection für den Ordner Scripte also nur die Unterordner Unterordner1 und Unterordner2. Unterordner1A, Unterordner1B usw. sind nicht in der Collection enthalten.
Abbildung 4.5: Eine Beispiel-Ordnerstruktur Eine Collection mit den Unterordnern erhalten Sie folgendermaßen: Seite 207 von 394
1.Sie erstellen eine Instanz des Objekts FileSystemObject. 2.Sie verwenden die Methode GetFolder um eine Referenz auf einen Ordner zu erhalten. 3.Sie erstellen eine Referenz auf die Eigenschaft Subfolders des Ordnerobjekts (dies ist erforderlich, da es sich bei Collections ja um Objekte handelt). Nachdem Sie eine Referenz auf die Collection erstellt haben, können Sie die einzelnen Unterordner in der Collection mit einer For-Each-Schleife durchlaufen. Script 4.16 erstellt zum Beispiel eine Referenz auf den Ordner C:\FSO und gibt dann den Namen und die Größe der Unterordner aus. Die restlichen Eigenschaften eines Ordnerobjekts haben Sie in Tabelle 4.3 gesehen. Script 4.16: Auflisten von Unterordnern 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFolder = objFSO.GetFolder("C:\FSO") 3Set colSubfolders = objFolder.Subfolders 4For Each objSubfolder in colSubfolders 5 Wscript.Echo objSubfolder.Name, objSubfolder.Size 6Next
Unterordner der Unterordner auflisten Wahrscheinlich reicht es Ihnen nicht aus, die Unterordner eines Ordners zu kennen stattdessen möchten Sie alle Unterordner auflisten. Die Collection Subfolders gibt Ihnen zwar die beiden Unterordner von c:\Scripte zurück, aber wie kommen Sie an deren Unterordner?
Abbildung 4.6: Eine Beispiel-Ordnerstruktur Um alle Unterordner abzufragen, müssen Sie eine rekursive Funktion verwenden - eine Funktion, die sich selbst aufruft. Weitere Informationen über Rekursion finden Sie im Kapitel VBScript Primer in diesem Buch. Script 4.17 ist ein Beispiel dafür, wie ein Script alle Unterordner eines Ordners (inklusiver der Unterordner der Unterordner usw.) auflisten kann. Hierzu geht das Script folgendermaßen vor: 1.Er erstellt eine Instanz des Objekts FileSystemObject. 2.Es verwendet die Methode GetFolder, um eine Referenz auf C:\Scripte zu erstellen. Diese Referenz wird der Subroutine ShowSubfolders als Parameter übergeben. Die Subroutine listet alle Unterordner von C:\Scripts auf. 3.Das Script ruft eine Collection mit allen Unterordnern von C:\Scripte ab. Diese Collection hat zwei Elemente: Unterordner1 und Unterordner2. Seite 208 von 394
4.Das Script gibt den Ordnerpfad für das erste Element der Collection aus. Dann ruft sich die Subroutine mit dem Namen des Ordners als Parameter selbst auf (die Subroutine ShowSubFolders wird also jetzt mit dem Parameter Unterordner1 aufgerufen). 5.Das Script ruft eine Collection mit allen Unterordnern von Unterordner1 ab. Diese Collection hat zwei Elemente: Unterordner1A und Unterordner1B. 6.Das Script gibt den Ordnerpfad für das erste Element der Collection aus (Unterordner1A). Dann ruft sich die Subroutine mit dem Namen des Ordners als Parameter selbst auf (die Subroutine ShowSubFolders wird als mit dem Parameter Unterordner1A aufgerufen). 7.Da Unterordner1A keine Unterordner mehr hat, wird das nächste Elemente der Collection abgearbeitet. Die Subroutine ruft sich selbst mit dem Parameter Subfolder1B auf. 8.Damit ist die Rekursion durch Unterordner1 abgeschlossen. Das Script macht mit dem zweiten Element der ersten Collection (aus Schritt 3) weiter und wiederholt den gesamten Vorgang. Script 4.17: Rekursive Auflistung von Unterordnern 1Set FSO = CreateObject("Scripting.FileSystemObject") 2ShowSubfolders FSO.GetFolder("C:\Scripts") 3Sub ShowSubFolders(Folder) 4 For Each Subfolder in Folder.SubFolders 5 Wscript.Echo Subfolder.Path 6 ShowSubFolders Subfolder 7 Next 8End Sub
Wenn Sie das Script unter CScript ausführen, sollte die Ausgabe so aussehen: C:\scripte\Unterodner1 C:\scripte\Unterodner1\Unterodner1A C:\scripte\Unterodner1\Unterodner1B C:\scripte\Unterodner2 C:\scripte\Unterodner2\Unterodner2A C:\scripte\Unterodner2\Unterodner2A\Unterodner 2A-1 C:\scripte\Unterodner2\Unterodner2B C:\scripte\Unterodner2\Unterodner2C
Um zum Beispiel eine komplette Liste aller Ordner eines Laufwerkes abzufragen, beginnen Sie einfach mit dem Stammordner des entsprechenden Laufwerks (zum Beispiel C:\).
Verwalten von Dateien Zur Verwaltung des Dateisystems gehört auch die Verwaltung der einzelnen Dateien - diese müssen kopiert, verschoben, umbenannt und gelöscht werden. Das Objekt FileSystemObject bietet auch für diese Aufgaben die richtigen Methoden an.
Eine Referenz auf eine Datei erstellen Das Objekt FileSystemObject stellt Ihnen einige Methoden zur Verfügung, über die ein Script mit Dateien arbeiten kann, ohne eine neue Instanz eines File-Objekts zu erstellen - unter anderem CopyFile und DeleteFile. Für andere Aufgaben ist jedoch ein FileObjekterforderlich. Um zum Beispiel eine Liste der Dateieigenschaften abzurufen, muss ein Script erst eine Referenz auf ein File-Objekt erstellen. Seite 209 von 394
Um eine solche Referenz zu erstellen, verwenden Sie die Methode GetFile. Das von Getfile zurückgegebene File-Objekt weisen Sie dann mit dem Schlüsselwort Set als Referenz zu einer Variablen zu. Als Parameter erwartet die Methode GetFile den Dateinamen (entweder ein lokaler Pfad oder ein UNC-Pfad). Script 4.18 erstellt zu Beispiel eine Referenz auf die Datei C:\FSO\ScriptLog.txt. Script 4.18: Eine Referenz auf eine Datei erstellen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.GetFile("C:\FSO\ScriptLog.txt")
Im Allgemeinen ist es eine gute Idee, den absoluten Pfad einer Datei als Parameter zu verwenden. Dies stellt sicher, dass das Script immer auf die Datei zugreifen kann. Wenn sich die Datei im gleichen Ordner wie das Script befindet, dann reicht es auch, nur den Dateinamen anzugeben. Die folgende Scriptzeile erzeugt zum Beispiel eine Referenz auf die Datei ScriptLog.txt, die sich im gleichen Ordner wie das Script befindet: objFSO.GetFile("ScriptLog.txt")
Wenn sich die Datei im dem Scriptordner übergeordneten Ordner befindet, dann können Sie die folgende Syntax verwenden: objFSO.GetFile(".\ScriptLog.txt")
Bitte denken Sie daran, dass das Objekt FileSystemObject nicht in den Pfaden der Umgebungsvariable Path nach den Dateien sucht. So können Sie beispielsweise den Windows-Rechner starten, indem Sie in der Eingabeaufforderung einfach calc.exeeingeben egal wo Sie sich befinden. Mit GetFile-Methode funktioniert dies nicht. Hier müssen Sie immer den korrekten Pfad angeben (für den Windows-Taschenrechner wäre das zum Beispiel C:\Windows\System32)
Prüfen, ob eine Datei vorhanden ist Manchmal ist es sehr wichtig zu prüfen, ob eine Datei vorhanden ist oder nicht. Zum Beispiel wenn Sie die Datei verschieben, kopieren oder löschen möchten. Wenn Sie versuchen eine solche Aktion mit einer nicht vorhandenen Datei durchzuführen, dann kommt es zu einem Laufzeitfehler. Um solche Fehler zu vermeiden, können Sie vorher mit der Methode FileExists prüfen, ob die Datei vorhanden ist. Die Methode benötigt nur einen einzelnen Parameter (die Datei mit Pfad) und gibt einen Boolean-Wert zurück. Wenn die Datei vorhanden ist, dann ist der Rückgabewert True ansonsten False: Script 4.19 prüft mit der Methode FileExists, ob die Datei C:\FSO\ScriptLog.txt vorhanden ist. Wenn dies der Fall ist, dann verwendet das Script die Methode GetFile, um eine Referenz auf die Datei zu erstellen. Wenn die Datei nicht vorhanden ist, dann gibt das Script die Fehlermeldung 'Datei ist nicht vorhanden' aus. Script 4.19: Existenz einer Datei überprüfen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2If objFSO.FileExists("C:\FSO\ScriptLog.txt") Then 3 Set objFile = objFSO.GetFile("C:\FSO\ScriptLog.txt") 4Else
Seite 210 von 394
5 Wscript.Echo "Datei ist nicht vorhanden." 6End If
Es ist nicht möglich Wildcards zu verwenden - zum Beispiel um zu prüfen, ob eine bestimmte Gruppe von Dateien vorhanden ist (beispielsweise *.txt). Die folgende Codezeile führt zwar nicht zu einem Fehler, gibt jedoch immer den Wert False zurück: WScript.Echo objFSO.FileExists("C:\FSO\*.*")
Wenn Sie die Existenz einer Datei auf Basis bestimmter Kriterien prüfen müssen, dann haben Sie zwei Möglichkeiten: •
•
Verwenden Sie die Methode GetFolder, um eine Referenz auf den Ordner zu erstellen, rufen Sie die Eigenschaft Files ab und gehen Sie die gesamte Collection durch. So können Sie alle Dateien im Ordner überprüfen und feststellen, ob die gesucht Datei vorhanden ist. Verwenden Sie WMI. Mit WMI haben Sie die Möglichkeit speziellere Abfragen zu erstellen - zum Beispiel 'alle Dateien mit der Dateierweiterung .doc'. Danach können Sie mit der Methode Count ermitteln, wie viele Dateien die Abfrage zurückgegeben hat.
Löschen einer Datei Zum Löschen von Dateien erstellen Sie als erstes eine Instanz des Objektes FileSystemObject. Dann rufen Sie die Methode DeleteFile mit dem Dateinamen und deren Pfad als Parameter auf. Script 4.20 löscht als Beispiel die Datei C:\FSO\ScriptLog.txt. Script 4.20: Löschen einer Datei 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.DeleteFile("C:\FSO\ScriptLog.txt")
Standardmäßig löscht die Methode DeleteFile keine schreibgeschützten Dateien - wenn Sie dies versuchen, kommt es zu einem Laufzeitfehler. Um solche Fehler zu vermeiden und um schreibgeschützte Dateien löschen zu können, können Sie den optionalen Parameter Force verwenden. Wenn Sie den Parameter auf True setzen, werden alle Dateien gelöscht: objFSO.DeleteFile("C:\FSO\ScriptLog.txt", True)
Eine Gruppe von Dateien löschen Innerhalb eines Ordners können Sie mehrere Dateien über Wildcards löschen. Wenn die Dateien über mehrere Ordner verteilt sind, ist dies mit einem einzigen Aufruf von DeleteFile jedoch nicht möglich. Um mehrere Dateien in unterschiedlichen Ordner mit einem Aufruf zu löschen (zum Beispiel alle .TMP-Dateien auf einem Computer), müssen Sie WMI verwenden. Um mehrere Dateien in einem Ordner zu löschen, verwenden Sie die Methode DeleteFile und geben den Pfad und den Dateinamen einfach mit Wildcards an. Die folgende Codezeile löscht zum Beispiel alle .doc-Dateien im Ordner C:\FSO: objFSO.DeleteFile("C:\FSO\*.doc")
Die folgende Codezeile löscht alle Dateien mit den Buchstaben log irgendwo im Dateinamen: objFSO.DeleteFile("C:\FSO\*log.* ")
Wie bereits erwähnt, löscht DeleteFile standardmäßig keine Dateien, die schreibgeschützt sind. Bei einem solchen Versuch kommt es zu einem Laufzeitfehler und die Methode bricht ab - und zwar unabhängig davon, ob im Script der Befehl On Error Resume Next verwendet wurde oder nicht. Seite 211 von 394
Script 4.21 löscht alle .txt-Dateien im Ordner C:\FSO. Um sicherzustellen, dass alle Dateien gelöscht werden (auch die schreibgeschützten), wird der Parameter Force mit dem Wert DeleteReadOnly verwendet. Script 4.21:Löschen einer Gruppe von Dateien 12 23 3Const DeleteReadOnly = True 4Set objFSO = CreateObject("Scripting.FileSystemObject") 5objFSO.DeleteFile("C:\FSO\*.txt"), DeleteReadOnly
Tipp: Was wenn Sie alle Dateien außer den schreibgeschützten löschen möchten? In diesem Fall rufen Sie über die Eigenschaft Files des Folder-Objekts eine Liste aller Dateien ab und gehen diese durch. Hierbei können Sie für jede Datei abfragen, ob sie schreibgeschützt ist und sie gegebenenfalls löschen.
Kopieren einer Datei Die Methode CopyFile dient zum Kopieren von Dateien. Sie müssen zwei Parameter angeben - ein dritter Parameter ist optional: •
Quelle (erforderlich) - die zu kopierende Datei. Entweder ein lokaler Pfad oder ein UNCPfad.
•
Ziel (erforderlich) - Pfad und Dateiname. Entweder ein lokaler Pfad oder ein UNC-Pfad. Um die Datei unter dem gleichen Namen in den Zielordner zu kopieren, lassen Sie einfach den Dateinamen weg: objFSO.CopyFile "C:\FSO\ScriptLog.txt" , "D:\Archive\" Um der Datei am Zielort einen neuen Namen zu gegen verwenden Sie folgende Syntax: objFSO.CopyFile "C:\FSO\ScriptLog.txt" , "D:\Archive\NewFileName.txt" Wenn der Zielordner nicht vorhanden ist wird er automatisch erstellt.
•
Überschreiben (optional) - Standardmäßig kopiert die Methode die Datei nicht, wenn im Zielordner schon eine Datei mit dem gleichen Namen vorhanden ist. Sie können das Überschreiben erzwingen, indem Sie den Parameter auf den Wert True setzen.
Script 4.22 kopiert die Datei C:\FSO\ScriptLog.txt in den Ordner D:\Archive. Um sicherzustellen, dass der Vorgang nicht fehlschlägt, wird der dritte Parameter auf True gesetzt. Script 4.22: Eine Datei kopieren 1Const OverwriteExisting = True 2Set objFSO = CreateObject("Scripting.FileSystemObject") 3objFSO.CopyFile "C:\FSO\ScriptLog.txt" , "D:\Archive\", OverwriteExisting
Wenn Sie den Zielordner angeben, dürfen Sie das letzte Slash-Zeichen nicht vergessen. Wenn Sie das Zeichen weglassen, dann wird die Datei nicht in den Ordner kopiert. Stattdessen versucht die Methode eine neue Datei mit dem Ordnernamen anzulegen. Wenn Sie versuchen eine schreibgeschützte Datei zu überschreiben, wird die Methode fehlschlagen - auch dann, wenn Sie den Parameter OverWrite auf True gesetzt haben. In einem solchen Fall müssen Sie die Datei im Zielordner vorher löschen. Eine Gruppe von Dateien kopieren Seite 212 von 394
Solange sich die Dateien im gleichen Ordner befinden, können Sie mit Wildcards ganze Dateigruppen kopieren. Script 4.23 kopiert zum Beispiel alle .txt-Dateien im Ordner C:\FSO nach D:\Archive. Script 4.23: Kopieren von mehreren Dateien 1Const OverwriteExisting = True 2Set objFSO = CreateObject("Scripting.FileSystemObject") 3objFSO.CopyFile "C:\FSO\*.txt" , "D:\Archive\" , OverwriteExisting
Mit Wildcards und der Methode CopyFile haben Sie die Möglichkeit alle Dateien in einem Ordner zu kopieren - ohne die Unterordner. Mit CopyFolder hingegen werden sowohl Dateien als auch Unterordner kopiert. Die folgende Scriptzeile kopiert alle Dateien im Ordner C:\FSO: objFSO.CopyFile "C:\FSO\*.*" , "D:\Archive\"
Verschieben einer Datei Statt eine Datei zu kopieren, möchten Sie diese möglicherweise verschieben. Hierzu können Sie die Methode MoveFile nutzen. Sie arbeitet genauso wie die Methode CopyFile: Sie erstellen eine Instanz des Objekts FileSystemObject und rufen die Methode MoveFile mit zwei Parametern auf: • •
Dem kompletten Pfad der zu verschiebenden Datei. Dem kompletten Pfad des neuen Speicherortes - inklusive des abschließenden BackslashZeichens.
Script 4.24 verschiebt die Datei C:\FSO\ScriptLog.log in den Ordner Archive auf Laufwerk D. Script 4.24: Verschieben einer Datei 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.MoveFile "C:\FSO\ScriptLog.log" , "D:\Archive\"
Mehrere Dateien verschieben Über Wildcards können Sie mehrere Dateien in einem Vorgang verschieben. Mit dem folgenden Parameter verschieben Sie zum Beispiel alle Dateien im Ordner FSO, die mit data anfangen: C:\FSO\Data*.*
Script 4.25 verschiebt alle Protokolldateien (.log) aus dem Ordner FSO auf Laufwerk C in den Ordner Archive auf Laufwerk D. Script 4.25: Verschieben von mehreren Dateien 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.MoveFile "C:\FSO\*.log" , "D:\Archive\"
Eine Datei umbenennen Das Objekt FileSystemObject stellt keine Methode zum Umbenennen von Dateien bereit. Sie können eine Datei jedoch über den gleichen Vorgang umbenennen, den wir bereits beim Umbenennen von Ordnern kennen gelernt haben. Verwenden Sie die Methode MoveFile und belassen Sie die Datei in ihrem aktuellen Ordner. Seite 213 von 394
Script 4.26 benennt die Datei ScriptLog.txt in BackupLog.txt um. Technisch verschiebt das Script die Datei C:\FSO\ScriptLog.txt nach C:\FSO\BackupLog.txt. Das Endergebnis ist jedoch eine umbenannte Datei. Script 4.26: Umbenennen von Dateien über die Methode MoveFile 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2objFSO.MoveFile "C:\FSO\ScriptLog.txt" , "C:\FSO\BackupLog.txt"
Abfragen von Dateieigenschaften Auch Dateien haben einige Eigenschaften, die Sie abfragen können. Auch hierzu verwenden Sie das Objekt FileSystemObject - neben vielen anderen Dingen können Sie mit ihm die Eigenschaften einer oder mehrere Dateien abfragen. Eine vollständige Liste der Eigenschaften des Objekts File finden Sie in Tabelle 4.5. Tabelle 4.5: Eigenschaften des Objekts File Eigenschaft
Beschreibung
Attributes
Ein Bitfeld mit den Attributen der Datei.
DateCreated
Das Erstellungsdatum der Datei.
DateLastAccessedDas Datum des letzten Dateizugriffs. DateLastModified Das Datum der letzten Änderung an der Datei. Drive
Laufwerksbuchstabe mit Doppelpunkt des Laufwerks, auf dem die Datei gespeichert ist.
Name
Dateiname ohne Pfadinformationen. Für die Datei C:\Windows\System32\Scrrun.dll wäre das zum Beispiel Scrrun.dll.
ParentFolder
Name des Ordners, in dem die Datei gespeichert ist. Für C:\Windows\System32\Scrrun.dll wäre das zum Beispiel System32.
Path
Der vollständige Pfad der Datei (zum Beispiel C:\Windows\System32\Scrrun.dll).
ShortName
Der MS-DOS-Name der Datei (in 8.3-Schreibweise). Für die Datei C:\MySpreadsheet.xls ist das zum Beispiel MySpre~1.xls.
ShortPath
Der Pfad in MS-DOS-Schreibweise (in 8.3-Schreibweise). Für die Datei C:\Windows\Program Files\MyScript.vbs wäre das zum Beispiel C:\Windows\Progra~1\MyScript.vbs.
Size
Gesamtgröße der Datei in Byte.
Type
Ein String mit der Dateiart (zum Beispiel Microsoft Word Document).
Ein Script kann folgendermaßen auf die Dateieigenschaften zugreifen: • •
Eine Instanz des Objekts FileSystemObject erstellen. Mit der Methode GetFile eine Objektreferenz auf eine bestimmte Datei erstellen (die Seite 214 von 394
Methode GetFile benötigt als Parameter den Pfad der Datei). •
Die Dateieigenschaften ausgeben oder ändern.
Script 4.27 verwendet die GetFile-Methode, um eine Referenz auf die Datei C:\Windows\System32\Scrrun.dll zu erstellen und deren Eigenschaften auszugeben. Script 4.27: Auflisten von Dateieigenschaften 1 Set objFSO = CreateObject("Scripting.FileSystemObject") 2 Set objFile = objFSO.GetFile("c:\windows\system32\scrrun.dll") 3 Wscript.Echo "DateCreated: " & objFile.DateCreated 4 Wscript.Echo "DateLastAccessed: " & objFile.DateLastAccessed 5 Wscript.Echo "DateLastModified: " & objFile.DateLastModified 6 Wscript.Echo "Drive: " & objFile.Drive 7 Wscript.Echo "Name: " & objFile.Name 8 Wscript.Echo "ParentFolder: " & objFile.ParentFolder 9 Wscript.Echo "Path: " & objFile.Path 10Wscript.Echo "ShortName: " & objFile.ShortName 11Wscript.Echo "ShortPath: " & objFile.ShortPath 12Wscript.Echo "Size: " & objFile.Size 13Wscript.Echo "Type: " & objFile.Type
Wenn Sie das Script unter CScript ausführen erhalten Sie die folgende Ausgabe: Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.GetFile("c:\windows\system32\scrrun.dll") Wscript.Echo "DateCreated: " & objFile.DateCreated Wscript.Echo "DateLastAccessed: " & objFile.DateLastAccessed Wscript.Echo "DateLastModified: " & objFile.DateLastModified Wscript.Echo "Drive: " & objFile.Drive Wscript.Echo "Name: " & objFile.Name Wscript.Echo "ParentFolder: " & objFile.ParentFolder Wscript.Echo "Path: " & objFile.Path Wscript.Echo "ShortName: " & objFile.ShortName Wscript.Echo "ShortPath: " & objFile.ShortPath Wscript.Echo "Size: " & objFile.Size Wscript.Echo "Type: " & objFile.Type
Auflisten der Dateiattribute Auch Dateien haben Attribute. Wie bei Ordnern werden diese als Bitfeld zurückgegeben (weitere Informationen zu Bitfeldern finden Sie im Abschnitt Verwalten von Ordnerattributen dieses Kapitels). In Tabelle 4.6 sehen Sie die verfügbaren Dateiattribute. Tabelle 4.6: Dateiattribute Konstante Wert Beschreibung
Normal
0
Datei ohne Attribute.
Read-only 1
Datei kann nur gelesen werden.
Hidden
2
Versteckte Datei.
System
4
Datei ist Teil des Betriebssystems.
Archive
32
Datei ist zur Sicherung markiert.
Alias
64
Datei ist eine Verknüpfung, die auf eine andere Datei verweist.
Seite 215 von 394
Konstante Wert Beschreibung
Compressed2048Datei ist komprimiert. Um die Dateiattribute abzufragen, verwenden Sie als erstes die Methode GetFile, um eine Referenz auf die Datei zu erstellen. Danach verwenden Sie den logischen Operator AND, um die Dateiattribute zu überprüfen. Script 4.28 erstellt eine Referenz auf die Datei C:\FSO\ScriptLog.txt und prüft dann alle Dateiattribute. Script 4.28: Auflisten von Dateiattributen 1 Set objFSO = CreateObject("Scripting.FileSystemObject") 2 Set objFile = objFSO.GetFile("C:\FSO\ScriptLog.txt") 3 If objFile.Attributes AND 0 Then 4 Wscript.Echo "Keine Attribute vorhanden." 5 End If 6 If objFile.Attributes AND 1 Then 7 Wscript.Echo "Read-only." 8 End If 9 If objFile.Attributes AND 2 Then 10 Wscript.Echo "Hidden." 11End If 12If objFile.Attributes AND 4 Then 13 Wscript.Echo "System." 14End If 15If objFile.Attributes AND 32 Then 16 Wscript.Echo "Archive." 17End If 18If objFile.Attributes AND 64 Then 19 Wscript.Echo "Alias." 20End If 21If objFile.Attributes AND 2048 Then 22 Wscript.Echo "Compressed." 23End If
Konfigurieren von Dateiattributen Natürlich haben Sie auch die Möglichkeit die Dateiattribute zu verändern. Dies ist jedoch nur für die folgenden Attribute möglich: 1.ReadOnly 2.Hidden 3.System 4.Archive Um ein Dateiattribut zu ändern, muss ein Script folgendermaßen vorgehen: 1.Eine Referenz auf die Datei über GetFile erstellen. 2.Die Attribute prüfen, die geändert werden sollen. Wenn Sie zum Beispiel das Attribut readonly setzen möchten, dann sollten Sie prüfen, ob das Attribut nicht eventuell bereits gesetzt ist. 3.Wenn das Attribut nicht gesetzt ist, dann verwendet das Script den logischen Operator XOR, Seite 216 von 394
um das Attribut 'umzuschalten'. Wenn das Attribut bereits gesetzt ist, dann dürfen Sie XOR nicht verwenden - Sie würden das Attribut so 'ausschalten'. Script 4.29 verwendet den Operator AND um zu prüfen, ob der Schalter mit dem Wert 1 (read-only) gesetzt ist. Wenn dies nicht der Fall ist, dann wird der Schalter mit dem Operator XOR gesetzt. Script 4.29: Konfigurieren von Dateiattributen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFile = objFSO.GetFile("C:\FSO\TestScript.vbs") 3If objFile.Attributes AND 1 Then objFile.Attributes = objFile.Attributes XOR 1 4 5End If
Über die folgende Codezeile schalten Sie alle Attribute gleichzeitig aus: objFile.Attributes = objFile.Attributes AND 0
Den Pfad einer Datei verarbeiten Manchmal benötigen Sie den Pfad einer Datei oder nur einen Teil eines Pfades - in anderen Fällen vielleicht nur die Dateierweiterung. In all diesen Fällen müssen Sie in der Lage sein, die gewünschten Informationen abzufragen. Das Objekt FileSystemObject stellt Ihnen hierzu die in Tabelle 4.7 aufgelisteten Methoden zur Verfügung. Tabelle 4.7: Methoden zur Abfrage von Dateipfaden Methode
Beschreibung
GetAbsolutePathNameGibt den vollständigen Pfad einer Datei zurück (zum Beispiel C:\FSO\Scripts\Scriptlog.txt). GetParentFolderName Gibt den Pfad des Ordners zurück, in dem die Datei gespeichert ist (zum Beispiel C:\FSO\Scripts). GetFileName
Gibt den Dateinamen ohne Pfadinformationen zurück (zum Beispiel ScriptLog.txt).
GetBaseName
Gibt den Basisnamen der Datei zurück (der Dateiname ohne Erweiterung - zum Beispiel ScriptLog).
GetExtensionName
Gibt die Erweiterung der Datei zurück (zum Beispiel txt).
Script 4.30 fragt die Pfadinformationen der Datei ScriptLog.txt ab. Das Script funktioniert nur dann, wenn sich die Datei ScriptLog.txt im gleichen Ordner wie das Script befindet. Andernfalls müssten Sie der Methode GetFile den vollständigen Pfad zur Zieldatei angeben (zum Beispiel C:\FSO\Scripts\ScriptLog.txt). Script 4.30: Abfragen von Pfadinformationen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFile = objFSO.GetFile("ScriptLog.txt") 3Wscript.Echo "Absoluter Pfad: " & objFSO.GetAbsolutePathName(objFile) 4Wscript.Echo "Überg. Ordner: " & objFSO.GetParentFolderName(objFile) 5Wscript.Echo "Dateiname: " & objFSO.GetFileName(objFile) 6Wscript.Echo "Basisname: " & objFSO.GetBaseName(objFile) 7Wscript.Echo "Erweiterung: " & objFSO.GetExtensionName(objFile)
Seite 217 von 394
Wenn Sie das Script unter CScript ausführen, erhalten Sie eine Ausgabe wie die folgende: Absoluter Pfad: C:\scriptlog.txt Überg. Ordner: C:\ Dateiname: scriptlog.txt Basisname: scriptlog Erweiterung: txt
Abfragen der Dateiversion Manchmal müssen Sie als Administrator feststellen, ob eine Datei veraltet ist. Hierzu benötigen Sie die Dateiversion, die Sie über die Methode GetFileVersion erhalten. Das Script muss folgendermaßen vorgehen: 1.Eine Instanz des Objekts FileSystemObject erstellen. 2.Die Methode GetFileVersion aufrufen und ihr als Parameter den Pfad zur Datei übergeben. Script 4.31 demonstriert diesen Vorgang und fragt die Dateiversion von Scrrun.dll ab. Script 4.31: Abfragen der Dateiversion 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Wscript.Echo objFSO.GetFileVersion("c:\windows\system32\scrrun.dll")
Wenn Sie das Script auf einem Computer unter Windows 2000 mit WSH 5.6 ausführen, erhalten Sie die in Abbildung 4.7 zu sehende Ausgabe. Abbildung 4.7: Versionsnummer der Datei Scrrun.dll Versionsnummern setzen sich normalerweise aus vier Teilen zusammen. Die Versionsnummer 5.6.0.6626 enthält zum Beispiel die folgenden Informationen: 1.5 - Die Hauptversionsnummer. 2.6 - Die Unterversionsnummer. Hauptversion und Unterversion zusammen sind die Information, die normalerweise mit 'Versionsnummer' gemeint ist. In unserem Beispiel würden Sie zum Beispiel wahrscheinlich eher von Version 5.6 statt von Version 5.6.0.6626 sprechen. 3.0 - DieBuildnummer (normalerweise 0). 4.6626 - Die Dateinummer. Nicht alle Dateien stellen Versionsnummern zur Verfügung. Bei den meisten ausführbaren Dateien und DLLs sind jedoch Versionsnummern vorhanden.
Textdateien lesen und schreiben Eins der praktischsten Werkzeuge eines Systemadministrators ist die Textdatei. Das scheint im Zeitalter von 3D-Grafik und High-End-Datenbanken erst einmal schwer vorstellbar trotzdem hat eine einfache Textdatei viele Vorteile. Sie ist klein und leicht zu pflegen, es wird keine zusätzliche Software benötigt (Notepad.exe reicht aus) und sie ist extrem portabel. Mit Textdateien steht Ihnen ein einfacher Weg zur Verfügung, um Informationen in ein Script und aus einem Script heraus zu übertragen. Textdateien können Argumente enthalten, die Sie Seite 218 von 394
ansonsten direkt in das Script schreiben müssten. Sie können Scriptausgaben sehr einfach in einer Textdatei speichern (natürlich könnten Sie die Daten auch direkt in einer Datenbank schreiben - dies wäre jedoch viel komplizierter). Das Objekt FileSystemObject bietet Ihnen daher sowohl Methoden zum Lesen als auch zum Schreiben von Textdateien an.
Textdateien erstellen Sie haben die Möglichkeit mit vorhandenen Dateien zu arbeiten oder neue Textdateien anzulegen. Um eine neue Textdatei anzulegen benötigen Sie eine Instanz des Objekts FileSystemObject. Mit diesem rufen Sie dann die Methode CreateTexFile auf, und übergeben ihr den Dateinamen der neuen Datei als Parameter. Script 4.32 erstellt eine neue Textdatei mit dem Namen ScriptLog.txt im Ordner C:\FSO. Script 4.32: Erstellen einer Textdatei 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFile = objFSO.CreateTextFile("C:\FSO\ScriptLog.txt")
Wenn die Datei noch nicht vorhanden ist, dann erstellt die Methode CreateTextFile diese. Wenn die Datei bereits existiert, wird sie mit einer neuen, leeren Textdatei überschrieben. Wenn Sie nicht möchten, dass vorhandene Dateien überschrieben werden, dann können Sie einen zweiten, optionalen Parameter verwenden: Overwrite. Wenn Sie diesen Parameter auf True setzen, dann werden vorhandene Dateien überschreiben (True ist auch der Standardwert). Mit False werden die vorhandenen Dateien nicht überschrieben. Die folgende Scriptzeile überschreibt die vorhandene Datei nicht: Set objFile = objFSO.CreateTextFile("C:\FSO\ScriptLog.txt", False)
Wenn Sie den Parameter auf False setzen und die Datei bereits vorhanden ist, dann kommt es zu einem Laufzeitfehler. Daher sollten Sie vor dem Anlegen der Datei prüfen, ob diese schon vorhanden ist und in diesem Fall einen anderen Dateinamen wählen. Dateinamen in einem Script erstellen Ein Weg, um das oben beschriebene Problem mit vorhandenen Dateien zu umgehen, besteht darin, einen eindeutigen Dateinamen zu generieren. Da diese Dateinamen dann jedoch nicht sehr aussagekräftig sind, handelt es sich hierbei allerdings nicht um den besten Ansatz. Trotzdem kann ein eindeutiger Dateiname in einigen Situationen sehr nützlich sein - zum Beispiel bei temporären Dateien. Einen solchen Dateinamen erzeugen Sie mit der Methode GetTempFileName. Das Script muss zuerst eine Instanz von FileSystemObject erstellen und ruft dann die Methode GetTempName auf (sie benötigt keine Parameter). Script 4.33 erstellt zum Beispiel in einer Schleife 10 zufällige Dateinamen. Script 4.33: Generieren von Dateinamen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2For i = 1 to 10 3 strTempFile = objFSO.GetTempName 4 Wscript.Echo strTempFile 5Next
Seite 219 von 394
Wenn Sie das Script unter Cscript ausführen, dann erhalten Sie eine ähnliche Ausgabe wie die folgende: rad646E9.tmp radEC50C.tmp rad0C40A.tmp radE866E.tmp rad77F3D.tmp rad19970.tmp rad7A21A.tmp radB9DDC.tmp rad84930.tmp rad92199.tmp
Anmerkung: Die von GetTempName generierten Dateien sind nicht auf jeden Fall eindeutig. Dies liegt teilweise an dem zur Generierung der Dateinamen verwendeten Algorithmus und teilweise an der endlichen Zahl der möglichen Dateinamen. Die generierten Dateinamen haben immer acht Zeichen - die ersten drei Zeichen sind immer rad. Es gibt also nur 9.894 eindeutige Dateinamen. Wenn Sie mehr Dateinamen erzeugen, kommt es zwangsläufig zu Dubletten. Script 4.34 demonstriert die Verwendung von GetTempName bei der Erstellung einer Datei. Das Script geht folgendermaßen vor: 1.Es erstellt eine Instanz von FileSystemObject. 2.Es erstellt in der Variablen strPath eine Referenz auf den Ordner, in dem die Datei erstellt werden soll (C:\FSO). 3.Es verwendet die Methode GetTempName, um einen eindeutigen Dateinamen zu erstellen. 4.Es verwendet die Methode BuildPath, um den Ordnernamen mit dem Dateinamen zu kombinieren und so einen vollständigen Pfad für die neue Datei zu erstellen. Dieser Pfad wird in der Variable strFullName gespeichert. 5.Es ruft die Methode CreateTextFile auf und verwendet die Variable strFullName aus dem Parameter für die Methode. Nachdem die Datei erstellt wurde, schließt das Script diese sofort wieder. In einem Produktionsscript würde das Script nun in die Datei schreiben. Script 4.34: Erstellen und benennen einer Textdatei 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2strPath = "C:\FSO" 3strFileName = objFSO.GetTempName 4strFullName = objFSO.BuildPath(strPath, strFileName) 5Set objFile = objFSO.CreateTextFile(strFullName) 6objFile.Close
Öffnen von Textdateien Die Arbeit mit Textdateien ist ein Prozess mit drei Schritten. Bevor Sie etwas anderes machen, müssen Sie die Datei öffnen (alternativ können Sie auch eine neue Textdatei erstellen - diese ist dann automatisch geöffnet). Durch das Öffnen erhalten Sei ein Objekt vom Typ TextStream.
Seite 220 von 394
Nachdem Sie über eine Referenz auf das TextStream-Objekt verfügen, können Sie aus der Datei lesen oder in die Datei schreiben. Es ist nicht möglich, beide Vorgänge gleichzeitig durchzuführen. Sie können zum Beispiel eine Datei nicht öffnen, Daten lesen und dann sofort Daten in die geöffnete Datei schreiben. Stattdessen müssen Sie die Datei nach dem Lesevorgang schließen, sie neu öffnen und dann in die Datei schreiben. Dies liegt daran, dass eine Textdatei entweder zum Lesen oder zum Schreiben geöffnet wird. Wenn Sie eine neue Textdatei erstellen, dann ist diese automatisch zum Schreiben geöffnet (zu lesen gibt es ja normalerweise auch nichts). Im letzten Schritt müssen Sie die Datei dann noch schließen. Die ist zwar nicht unbedingt erforderlich (die Datei wird auch geschlossen, wenn das Script beendet wird), um ein korrektes Script zu schreiben sollten Sie diesen Schritt aber trotzdem durchführen. Um eine Textdatei zu öffnen, gehen Sie folgendermaßen vor: 1.Erstellen Sie eine Instanz von FileSystemObject. 2.Verwenden Sie die Methode OpenTextFile, um eine Textdatei zu öffnen. Sie benötigt zwei Parameter - den Pfadnamen der Datei und einen der folgenden Modi: •
ForReading (1) - Die Datei wird im Lesemodus geöffnet. Schreiben in die Datei ist nicht möglich (hierzu müssten Sie die Datei schließen und denn im Modus ForWriting oder ForAppending neu öffnen).
•
ForWriting (2) - In diesem Modus werden die vorhanden Dateien der Textdatei mit den neuen Daten überschrieben.
•
ForAppending (8) - In diesem Modus werden die neuen Dateien an die bestehenden Daten
Wenn Sie versuchen im Modus ForReading in eine Datei zu schreiben, erhalten Sie den Fehler 'Falscher Dateimodus'. Auch wenn Sie versuchen andere Dateien als Textdateien zu öffnen, kommt es zu diesem Fehler (auch bei HTML- und XML-Dateien handelt es sich um Textdateien). Als zweiten Parameter können Sie entweder den Wert (zum Beispiel 1) oder eine Konstante (zum Beispiel ForReading) angeben. Das folgende Script demonstriert beide Methoden: Const ForReading = 1 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading) Set objFile2 = objFSO.OpenTextFile("C:\FSO\ScriptLog2.txt", 1)
Allerdings müssen Sie die Konstante erst definieren - das liegt daran, dass VBScript leider keinen Zugriff auf die Konstanten von COM-Objekten hat (und ForReading ist eine Konstante des COM-Objekts FileSystemObject - im Gegensatz zum Beispiel zur Konstante True, bei der es sich um eine Konstante von VBScript handelt). Das folgende Script bricht mit der Fehlermeldung 'Ungültiger Prozeduraufruf oder Argument' ab, da die Konstante ForReading nicht definiert wurde: Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading)
Script 4.35 öffnet die Datei C:\FSO\ScriptLog.txt zum Lesen und definiert hierzu die Konstante ForReading mit dem Wert 1. Script 4.35: Eine Textdatei zum Lesen öffnen Seite 221 von 394
1Const ForReading = 1 2Set objFSO = CreateObject("Scripting.FileSystemObject") 3Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading)
Schließen von Textdateien Alle vom Script geöffneten Textdateien werden beim Beenden des Scripts automatisch geschlossen. Daher müssen Sie dies nicht unbedingt selbst durchführen. Sie sollten sich jedoch trotzdem selbst darum kümmern. Nicht nur, weil es eine saubere Programmierung so erfordert, sondern auch um zu vermeiden, dass folgende Probleme mit der geöffneten Datei auftreten: •
Löschen der Datei - Wenn Sie versuchen eine geöffnete Datei zu löschen, tritt der Laufzeitfehler 'Zugriff verweigert' auf.
•
Neu-Einlesen der Datei - Es könnte sein, dass Sie im selben Script ein zweites Mal auf die Textdatei zugreifen möchten. Wenn Sie versuchen eine bereits geöffnete Datei ein zweites Mal zu öffnen, erhalten Sie keinen Laufzeitfehler. Das Ergebnis wird allerdings auch nicht Ihren Erwartungen entsprechen. Das folgende Script liest zum Beispiel aus einer Textdatei und gibt die gelesenen Informationen aus. Dann versucht es den Vorgang zu wiederholen, ohne die Datei vorher zu schließen:
Set objFSO = CreateObject("Scripting.FileSystemObject") Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1) Wscript.Echo "Erstes Lesen der Datei:" strContents = objFile.ReadAll Wscript.Echo strContents Wscript.Echo "Zweites Lesen der Datei:" Do While objFile.AtEndOfStream = False strLine = objFile.ReadLine Wscript.Echo strLine Loop
Wenn Sie das Script unter Cscript ausführen, erhalten Sie die folgende Ausgabe: Erstes Lesen der Datei: Zeile 1. Zeile 2. Zeile 3. Zweites Lesen der Datei:
Beim ersten Lesen werden die erwarteten Dateiinhalte ausgegeben. Beim zweiten Lesen kommt es jedoch zu keinerlei Ausgabe. Dies liegt daran, dass das Ende der Datei bereits beim ersten Lesen erreicht wurde. Um die Datei ein zweites Mal zu lesen, müssen Sie sie erst schließen und dann neu öffnen. Sie können nicht einfach zum Anfang der Datei springen und dann von vorne beginnen. Um eine Textdatei zu schließen, wird die Methode Close des Objekts TextStreamObject verwendet. Script 4.36 demonstriert dies, indem es erst eine Instanz von FileSystemObject erstellt, dann eine Textdatei öffnet (C:\FSO\ScriptLog.txt) und diese dann wieder schließt. Script 4.36: Öffnen und Schließen einer Textdatei 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1) 3objFile.Close
Seite 222 von 394
Lesen von Textdateien Lesen aus einer Textdatei ist ein Standardverfahren bei vielen Administrationsaufgaben. Zum Beispiel: •
Lesen von Kommandozeilenargumenten - In der Textdatei könnte zum Beispiel eine Liste der Computer gespeichert sein, auf die das Script zugreifen soll.
•
Eine Protokolldatei automatisch nach bestimmten Einträgen durchsuchen - Sie können zum Beispiel nach Fehlereinträgen suchen.
Mit dem Objekt FileSystemObject können Sie aus Textdateien lesen. Hierbei sollten Sie jedoch die folgenden Einschränkungen bedenken: •
Sie können nur ASCII-Textdateien lesen - Sie können keine Unicode-Dateien oder Binärdateien (zum Beispiel Microsoft Word oder Microsoft Excel lesen).
•
Sie können nur vom Anfang zum Ende der Textdatei lesen - Sie können nicht rückwärts lesen oder zu Zeilen oder Zeichen in der Textdatei zurückspringen.
•
Sie können eine Datei nicht für gleichzeitiges Lesen und Schreiben öffnen
Die zum Lesen verfügbaren Dateien von FileSystemObject sehen Sie in Tabelle 4.8. Die Beispiele gehen von einer Textdatei aus, die folgendermaßen aussieht: Alerter,Share Process,Running,Auto,LocalSystem, AppMgmt,Share Process,Running,Manual,LocalSystem, Ati HotKey Poller,Own Process,Stopped,Auto,LocalSystem,
Tabelle 4.8: FileSystemObject-Methoden zum Lesen aus Textdateien Methode Beschreibung
Read
Liest die angegeben Anzahl an Zeichen. Der folgende Befehl liest zum Beispiel die ersten 12 Zeichen der ersten Zeile ("Alerter'Shar'") und speichert diese in der Variable strText: strText = objTextFile.Read(12)
ReadLineLiest eine Zeile auf der Textdatei. Der folgende Befehl liest zum Beispiel die erste Zeile ("Alerter'Share Process' Running,Auto,LocalSystem") und speichert sie in der Variable strText: strText = objTextFile.ReadLine ReadAll Liest die gesamte Textdatei. Skip
Überspringt die angegebene Anzahl an Zeichen. Der folgende Befehl überspringt zum Beispiel die ersten 12 Zeichen. Alle weiteren Leseoperationen beginnen also bei Zeichen 13 ('e Process,Running,Auto,LocalSystem'): objTextFile.Skip(12)
SkipLine Überspringt eine Zeile. Der folgende Code liest zum Beispiel die erste und die dritte Zeile: strText = objTextFile.Readline objTextFile.SkipLine strText = objTextFile.Readline Die Größe einer Datei prüfen
Seite 223 von 394
Manchmal sind die von Windows erstellten Textdateien leer - das bedeutet, dass in der Datei keine Zeichen enthalten sind und sie eine Größe von 0 Byte hat. Dies kommt zum Beispiel bei Protokollen vor, die so lange leer sind bis ein Ereignis aufgetreten ist. Leere Dateien sind ein Problem für Scriptautoren, da VBScript einen Laufzeitfehler erzeugt, wenn Sie versuchen aus einer leeren Datei zu lesen. Wenn die Möglichkeit besteht, dass die Datei leer sein könnte, dann vermeiden Sie den Fehler, indem Sie vor einem Leseversuch die Dateigröße testen. Script 4.37 erstellt eine Referenz auf die Datei C:\Windows\Netlogon.log. Dann prüft das Script die Größe der Datei - wenn diese größer 0 ist, wird die Datei geöffnet und gelesen. Andernfalls wird eine Fehlermeldung ausgegeben. Script 4.37: Prüfen der Dateigröße 1 Set objFSO = CreateObject("Scripting.FileSystemObject") 2 Set objFile = objFSO.GetFile("C:\Windows\Netlogon.log") 3 If objFile.Size > 0 Then 4 Set objReadFile = objFSO.OpenTextFile("C:\Windows\Netlogon.log", 1) 5 strContents = objReadFile.ReadAll 6 Wscript.Echo strContents 7 objReadFile.Close 8 Else 9 Wscript.Echo "Die Datei ist leer." 10End If
Lesen einer gesamten Textdatei Das einfachste Verfahren, um eine gesamte Datei einzulesen, ist die Methode ReadAll. Sie rufen die Methode einfach auf, und speichern ihren Rückgabewert in einer Variablen. Auch wenn Sie mehrere Textdateien zusammenfassen möchten, ist die Methode ReadAll die beste Wahl. Stellen Sie sich zum Beispiel vor, Sie haben eine Gruppe von täglichen Protokolldateien, die Sie in einer wöchentlichen Protokolldatei zusammenfassen möchten. Ein solches Script würde folgendermaßen vorgehen: 1.Es öffnet die Textdatei für Montag, liest die gesamten Dateien über ReadAll ein und speichert diese in einer Variablen. 2.Es öffnet die wöchentliche Protokolldatei im Modus Append und schreibt die Daten aus der Variablen in diese Datei. 3.Es wiederholt die Schritte 1 und 2 für alle verbleibenden täglichen Protokolldateien. Die Methode ReadAll benötigt keine Parameter. Script 4.38 öffnet die Textdatei C:\FSO\ScriptLog und liest den gesamten Inhalt der Datei über ReadAll aus. Script 4.38: Lesen einer Textdatei über die Methode ReadAll 1Const ForReading = 1 2Set objFSO = CreateObject("Scripting.FileSystemObject") 3Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForReading) 4strContents = objFile.ReadAll 5Wscript.Echo strContents 6objFile.Close
Zeilenweises Lesen einer Textdatei Seite 224 von 394
Bei der Systemadministration haben Sie es meist mit Textdateien zu tun, die als 'Datenbankersatz' verwendet werden. Jede Textzeile ist hier ein Datensatz. Ein Script könnte zum Beispiel die Servernamen, auf die es zugreifen soll, aus der Textdatei lesen. Diese Textdatei wird dann so oder ähnlich aussehen: atl-dc-01 atl-dc-02 atl-dc-03 atl-dc-04
In einem solchen Fall möchten Sie die Textdatei wahrscheinlich zeilenweise auslesen. Hierzu steht Ihnen die Methode ReadLine zur Verfügung. Sie verwenden die Methode, indem Sie eine Do-Loop-Schleife so lange ausführen, bis die Eigenschaft AtEndOfStream den Wert True hat (das bedeutet, dass das Ende der Datei erreicht wurde). In der Schleife lesen Sie jeweils eine einzelne Textzeile über die Methode ReadLine ein und führen die gewünschte Aktion aus. Script 4.39 demonstriert dieses Verfahren, indem es die Datei C:\FSO\ServerList.txt öffnet und diese dann zeilenweise einliest. Script 4.39: Zeilenweises Einlesen einer Datei über die Methode ReadLine 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFile = objFSO.OpenTextFile("C:\FSO\ServerList.txt", 1) 3Do Until objFile.AtEndOfStream 4 strLine = objFile.ReadLine 5 Wscript.Echo strLine 6Loop 7objFile.Close
Eine Textdatei von unten nach oben 'lesen' Wie schon erwähnt, können Sie mit dem Objekt FileSystemObject eine Datei nur von vorne nach hinten lesen. Sie können nicht hinten beginnen und rückwärts lesen. Manchmal ist das ein Problem - zum Beispiel bei der Arbeit mit Protokolldateien. Stellen Sie sich vor, Sie möchten die neusten Einträge einer Protokolldatei (also die letzten) zuerst lesen und sich dann zu den ältesten vorarbeiten. Ein Script muss in einem solchen Fall folgendermaßen vorgehen: 1.Es erstellt einen Array, der die einzelnen Zeilen der Textdatei speichert. 2.Es liest die einzelnen Zeilen über die Methode ReadLine ein und speichert diese jeweils im Array. 3.Es ruft den Inhalt des Array ab, beginnt jedoch mit dem letzten Element des Arrays (also mit der Zeile, die als letztes gelesen wurde bzw. die als letzte Zeile in der Datei steht). Script 4.40 demonstriert dieses Verfahren mit der Datei C:\FSO\ScriptLog.txt. Es speichert die einzelnen Zeilen im Array arrFileLines. Nachdem alle Zeilen der Datei gelesen wurden, werden die einzelnen Elemente des Arrays in umgekehrter Reihenfolge ausgegeben. Hierzu wird eine For-Schleife verwendet. Sie beginnt mit dem letzten Element (die Obergrenze des Arrays - Upper Bound - UBOUND) und arbeitet sich zum ersten Elemente vor (die Untergrenze des Arrays - Lower Bound - LBOUND). Script 4.40: Umgekehrtes 'Lesen' aus einer Textdatei
Seite 225 von 394
1 Dim arrFileLines() 2 i = 0 3 Set objFSO = CreateObject("Scripting.FileSystemObject") 4 Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1) 5 Do Until objFile.AtEndOfStream 6 Redim Preserve arrFileLines(i) 7 arrFileLines(i) = objFile.ReadLine 8 i = i + 1 9 Loop 10objFile.Close 11For l = Ubound(arrFileLines) to LBound(arrFileLines) Step -1 Wscript.Echo arrFileLines(l) 12 13Next
Nehmen wir an, die Textdatei hat den folgenden Inhalt: 6/19/2002 6/20/2002 6/21/2002 6/22/2002 6/23/2002
Success Failure Failure Failure Success
Dann würde die Ausgabe des Scripts folgendermaßen aussehen: 6/23/2002 6/22/2002 6/21/2002 6/20/2002 6/19/2002
Success Failure Failure Failure Success
Eine Textdatei Zeichen für Zeichen einlesen Eine Textdatei kann auch einzelne Werte enthalten. In einem solchen Fall hat zum Beispiel jeder Werte eine bestimmte Länge. Wert 1 ist 15 Zeichen lang, Wert 2 ist 10 Zeichen lang, usw. Eine solche Textdatei könnte zum Beispiel folgendermaßen aussehen: Server atl-dc-01 atl-printserver-02 atl-win2kpro-05
Value 19345 00042 00000
Status OK OK Failed
Wenn Sie nun einzelne Werte aus dieser Datei benötigen, dann müssen Sie die Datei zeichenweise einlesen (eine Zeile enthält ja schließlich mehrere Werte). Nehmen wir an, Sie benötigen jeweils den dritten Wert aus der oben gezeigten Textdatei. Dieser Wert beginnt in jeder Zeile beim 26. Zeichen und ist nicht länger als 5 Zeichen. Sie müssen alle die Zeichen 26, 27, 28, 29 und 30 jeder Zeile lesen. Mit der Methode Read ist genau dies möglicht. Sie liest die als Parameter übergebene Anzahl von Zeichen ein. Die folgende Zeile liest zum Beispiel die nächsten 7 Zeichen aus der Textdatei und speichert diese in der Variable strCharacters: strCharacters = objFile.Read(7)
Indem Sie die Methoden Skip und SkipLine verwenden, haben Sie die Möglichkeit Zeichen zu überspringen und so nur bestimmte Zeichen einzulesen. Script 4.41 liest zum Beispiel nur das sechste Zeichen jeder Zeile ein. Hierzu geht es folgendermaßen vor: 1.Es überspringt die ersten fünf Zeichen mit dem Befehl Skip(5). 2.Es liest sechs Zeichen über den Befehl Read(1) ein. 3.Es springt zur nächsten Zeile. Seite 226 von 394
Script 4.41: Einlesen von bestimmten Zeichen 1Set objFSO = CreateObject("Scripting.FileSystemObject") 2Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", 1) 3Do Until objFile.AtEndOfStream 4 objFile.Skip(5) 5 strCharacters = objFile.Read(1) 6 Wscript.Echo strCharacters 7 objFile.SkipLine 8Loop
Um zu veranschaulichen wie das Script arbeitet, nehmen wir an, die Textdatei C:\FSO\ScriptLog.txt hätte den folgenden Inhalt: XXXXX1XXXXXXXXXXXXXX XXXXX2XXXXXXXXXXXXXXXXXXX XXXXX3XXXXXXXXXXXX XXXXX4XXXXXXXXXXXXXXXXXXXXXXXXX
Die ersten fünf Zeichen jeder Zeile sind ein X, das sechste Zeichen ist eine Zahl und die restlichen Zeichen der Zeile sind wieder X. Wenn das Script ausgeführt wird, dann geht es folgendermaßen vor: 1.Es öffnet die Textdatei und beginnt in der ersten Zeile mit dem Lesen. 2.Es überspringt die ersten fünf Zeichen. 3.Es verwendet die Methode Read, um das sechste Zeichen zu lesen. 4.Es gibt das Zeichen aus. 5.Es springt zur nächsten Zeile und wiederholt den Prozess so lange bis alle Zeilen gelesen wurden. Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: 1 2 3 4
In Textdateien schreiben Textdateien sind eine sehr gute Möglichkeit Daten dauerhaft zu speichern. Außerdem können Sie die von einem Script ausgeführten Aktionen in einer Textdatei protokollieren - dies ist gerade beim Erstellen von Scripten und bei der Fehlersuche sehr praktisch. Um in eine Datei zu schreiben, muss ein Script folgendermaßen vorgehen: 1.Es erstellt eine Instanz von FileSystemObject. 2.Es verwendet die Methode OpenTextFile, um eine Datei zu öffnen. Hierbei gibt es zwei Möglichkeiten: •
ForWriting (2) - In diesem Modus werden die vorhanden Dateien der Textdatei mit den neuen Daten überschrieben.
•
ForAppending (8) - In diesem Modus werden die neuen Dateien an die bestehenden Daten
Seite 227 von 394
3.Es schreibt über die Methode Write, WriteLine oder WriteBlankLines in die Datei. 4.Es schließt die Datei. Die drei Methoden zum Schreiben in Textdateien sehen Sie in Tabelle 4.9. Tabelle 4.9: Methoden zum Schreiben in Textdateien Methode
Beschreibung
Write
Schreibt Daten in eine Textdatei, ohne einen Zeilenumbruch anzuhängen. Der folgende Code schreibt zum Beispiel zwei Strings in eine Textdatei: objFile.Write ("Die ist Zeile 1.") objFile.Write ("Die ist Zeile 2.") Die Textdatei sieht dann so aus: Die ist Zeile 1.Die ist Zeile 2.
WriteLine
Schreibt Daten in eine Textdatei und hängt einen Zeilenumbruch an. Der folgende Code schreibt zum Beispiel zwei Strings in eine Textdatei: objFile.WriteLine ("Die ist Zeile 1.") objFile.WriteLine ("Die ist Zeile 2.") Das Ergebnis sieht so aus: Die ist Zeile 1. Die ist Zeile 2.
WriteBlankLinesSchreibt eine leere Zeile in die Textdatei. Der folgende Code schreibt zwei Strings in die Textdatei und trennt diese durch einen Leerzeile: objFile.Writeline ("Dies ist Zeile 1.") objFile.WriteBlankLines(1) objFile.Writeline ("Dies ist Zeile 2.") Das Ergebnis sieht so aus: Dies ist Zeile 1. Dies ist Zeile 2. Außerdem können Sie beim Schreiben in eine Textdatei die VBScript-Konstante VbTab verwenden. Mit dieser Konstante fügen Sie an der entsprechenden Position ein Tabulatorzeichen ein. Die folgende Codezeile demonstriert dies: objTextFile.WriteLine(objService.DisplayName & vbTab & objService.State)
Eine Schwachstelle von FileSystemObject ist, dass es nicht möglich ist, bestimmte Zeilen in einer Textdatei zu verändern. Sie können zum Beispiel kein Script schreiben, das einige Zeilen überspringt und dann eine Zeile mit einem bestimmten Text überschreibt. Stattdessen müssen Sie folgendermaßen vorgehen: 1.Sie lesen alle Zeilen der Datei ein (zum Beispiel in ein Array). Seite 228 von 394
2.Sie ändern die entsprechenden Zeilen im Array. 3.Sie schreiben das Array wieder zurück in die Datei Überschreiben vorhandener Daten Stellen Sie sich vor, Sie möchten dass ein Script eine bestimmte nächtliche Aufgabe protokolliert, damit Sie das Protokoll jeweils am nächsten Tag überprüfen können. In diesem Fall reicht es Ihnen, wenn Ihnen das Protokoll vom Vortag zur Verfügung steht. Es ist nicht nötig, dass das Script die aktuellen Daten an die alten Dateien anhängt. Das Script kann die alten Daten vom Vortag einfach überschreiben. Wenn Sie eine Datei im Modus ForWriting öffnen, dann werden die in der Datei vorhanden Daten mit den neu geschriebenen Daten überschrieben. Nehmen wird zum Beispiel an, in einer einzelnen Textdatei haben Sie die letzte Kopie der gesammelten Werke von Shakespeare gespeichert. Nun öffnet Ihr Script diese Datei im Modus ForWriting und schreibt ein einzelnes Zeichen in die Datei. In diesem Fall sind alle Werke von Shakespeares für immer verloren, und die Datei enthält nur noch das eine neue Zeichen. Script 4.42 öffnet die Datei C:\FSO\ScriptLog.txt im Modus ForWriting und schreibt das aktuelle Datum und die Uhrzeit in die Datei. Jedes Mal wenn Sie das Script ausführen, wird der gesamte Inhalt der Datei überschrieben. Die Datei enthält niemals mehr als ein Datum und eine Uhrzeit - egal wie oft es auch ausgeführt wird. Script 4.42: Überschreiben von vorhandenen Daten 1Const ForWriting = 2 2Set objFSO = CreateObject("Scripting.FileSystemObject") 3Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForWriting) 4objFile.Write Now 5objFile.Close
Neue Daten an bestehende Daten anhängen Stellen Sie sich vor, Sie können die Protokolldateien aus dem Beispiel oben nur einmal in der Woche überprüfen. In diesem Fall möchten Sie sicher nicht, dass das Protokoll vom Vortag mit dem aktuellen Protokoll überschrieben wird. Stattdessen soll das Script die aktuellen Daten an die alten Daten anhängen. Dies können Sie durchführen, indem Sie die Textdatei im Modus ForAppending öffnen. Statt die Daten zu überschreiben, werden die neuen Daten am Ende der Datei angehängt. Script 4.43 schreibt zum Beispiel das aktuelle Datum und die Uhrzeit in eine Textdatei. Da die Datei aber im Modus ForAppending geöffnet wird, werden diese neuen Daten an die alten angehängt. Bei jeder Ausführung kommt so eine Zeile zur Textdatei hinzu. Die Textdatei sieht nach einigen Ausführungen des Scripts zum Beispiel so aus: 6/25/2002 6/25/2002 6/25/2002 6/25/2002
8:49:47 8:49:48 8:50:33 8:50:35
AM AM AM AM
Script 4.43: Daten an eine Textdatei anhängen 1Const ForAppending = 8 2Set objFSO = CreateObject("Scripting.FileSystemObject") 3Set objFile = objFSO.OpenTextFile("C:\FSO\ScriptLog.txt", ForAppending) 4objFile.WriteLine Now
Seite 229 von 394
5objFile.Close
Da das Script die Methode WriteLine verwendet, werden Datum und Uhrzeit immer in eine neue Zeile geschrieben. Wäre die Methode Write verwendet worden, dann sähe die Textdatei so aus: 6/25/2002 8:49:47 AM6/25/2002 8:49:48 AM6/25/2002 8:50:33 AM6/25/2002 8:50:35 AM
Das Dictionary-Objekt Oftmals erhält ein Script Informationen von einer externen Quelle - zum Beispiel aus einer Textdatei oder einer Datenbank. Diese Informationen müssen irgendwo gespeichert werden. Natürlich haben Sie die Möglichkeit solche Informationen in einzelnen Variablen oder in einem Array zu speichern - sehr effektiv ist dies jedoch nicht. Die Alternative wäre ein Dictionary-Objekt (ein Verzeichnis-Objekt). Ein Dictionary-Objekt funktioniert wie ein assoziativer Array. Das heißt, es speichert Wert und Schlüssel in Paaren. Ein Beispiel soll dies verdeutlichen. Ein Array speichert Werte so (die Zahl stellt den Arrayindex dar): 0 - München 1 - Hannover 2 - Wiesbaden Ein Dictionary-Objekt könnte die Werte zum Beispiel so speichern: Bayern - München Niedersachsen - Hannover Hessen - Wiesbaden Alternativ können Sie sich ein Dictionary-Objekt auch wie ein Telefonbuch vorstellen. Es speichert Namen und die dazugehörigen Telefonnummern. Über die Namen können Sie dann die Telefonnummern abrufen. Stellen Sie sich vor, das Script verwendet mehrere Argumente. Sie können diese Argumente zwar in einem Array speichern, das Dictionary-Objekt bietet jedoch mehrere Vorteile gegenüber einem Array: • •
Sie müssen nicht festlegen, wie viele Objekte gespeichert werden sollen. Bei einem Array müssen Sie das Array im Voraus erstellen (oder später vergrößern). Sie müssen keine Indexnummer zum Zugriff auf ein Element verwenden. Stattdessen können Sie über einen Schlüssel(begriff) auf das Element zugreifen (im Beispiel oben wären das die Bundesländer - sprich: 'Gib mir die Landeshauptstadt von Hessen zurück' statt "Gib mir das Element mit der Nummer 2').
Damit ist das Dictionary-Objekt ideal um Informationen zu speichern und diese später wieder abzufragen.
Seite 230 von 394
Ein Dictionary-Objekt erstellen Da ein Dictionary-Objekt ein COM-Objekt ist, müssen Sie wie bei allen anderen COMObjekten auch erst eine Instanz des Objekts erstellen. Die folgende Codezeile führt dies durch: Set objDictionary = CreateObject("Scripting.Dictionary")
Nachdem Sie das Dictionary-Objekt erstellt haben, können Sie dessen Eigenschaften konfigurieren und Elemente zum Dictionary-Objekt hinzufügen. Das Dictionary-Objekt hat nur eine konfigurierbare Eigenschaft: CompareMode. Diese Eigenschaft beeinflusst, wie Sie die Einträge im Dictionary-Objekt finden können. Standardmäßig arbeitet das Dictionary-Objekt im Binärmodus. Das bedeutet, dass die einzelnen Schlüssel als ASCII-Werte gespeichert werden. Bei ASCII-Werten wird zwischen Großbuchstaben und Kleinbuchstaben unterschieden. Im Binärmodus könnten Sie zum Beispiel die beiden folgenden Werte als Schlüssel anlegen: alerter ALERTER Mit anderen Worten: Im Binärmodus ist es durchaus möglich versehentlich mehrere Einträge für dasselbe Element anzulegen. Außerdem ist die Suche in diesem Modus schwieriger. Wenn Sie zum Beispiel nach dem Schlüssel Alerter suchen, dann erhalten Sie kein Ergebnis. Das liegt daran, weil keiner der beiden Einträge dieser Buchstabenkombination entspricht (großes A, Rest klein). Im zweiten Modus, dem Textmodus, werden Groß- und Kleinbuchstaben gleich behandelt. In diesem Modus können Sie keinen Schlüssel mit dem Namen ALERTER anlegen, wenn bereits ein Schlüssel mit dem Namen alerter vorhanden ist. Auch das Suchen ist viel einfacher. Wenn Sie nach dem Schlüssel alerter such, erhalten Sie die Einträge für Alerter und ALERTER zurück. Um den Modus eines Dictionary-Objekts zu konfigurieren, erstellen Sie zuerst eine Instanz des Objekts. Dann setzen Sie die Eigenschaft CompareMode auf einen der beiden folgenden Werte: 0 - Setzt den Modus auf Binär. Dies ist auch der Standardwert. 1 - Setzt den Modus auf Text. Script 4.44 setzt das Dictionary-Objekt beispielsweise auf den Modus Text. Script 4.44: Dictionary-Objekt konfigurieren 1Const TextMode = 1 2Set objDictionary = CreateObject("Scripting.Dictionary") 3objDictionary.CompareMode = TextMode
Wenn das Dictionary-Objekt bereits Elemente enthält, können Sie die Eigenschaft CompareMode nicht mehr verändern. Das liegt daran, weil es sonst zu Inkonsistenzen im Dictionary-Objekt kommen könnte (zum Beispiel wenn Einträge vorhanden sind, die mit dem neuen Modus nicht kompatibel sind). Die folgenden Schlüssel könnten im Modus Binär zum Beispiel im gleichen Dictionary-Objekt gespeichert werden: apple Seite 231 von 394
Apple APPLE Im Textmodus wären die drei Schlüssel jedoch identisch, da in diesem Modus ja nicht zwischen Groß- und Kleinschreibung unterschieden wird. Sie müssen also erst alle Elemente aus dem Objekt löschen, bevor Sie dessen Modus ändern können.
Einträge zu einem Dictionary-Objekt hinzufügen Mit der Methode Add können Sie neue Einträge zu einem Dictionary-Objekt hinzufügen. Die Methode benötigt zwei Parameter: den Schlüsselnamen, über den später auf den Eintrag zugegriffen werden soll und den Wert für diesen Eintrag. Script 4.45 erstellt ein Dictionary-Objekt und fügt diesem dann die Einträge aus Tabelle 4.10 hinzu. Tabelle 4.10: Beispieleinträge SchlüsselWert
Drucker1 Druckt Drucker2 Offline Drucker3 Druckt Script 4.45: Einen Eintrag zum Dictionary-Objekt hinzufügen 1Set objDictionary 2objDictionary.Add 3objDictionary.Add 4objDictionary.Add
= CreateObject("Scripting.Dictionary") "Drucker 1", "Druckt" "Drucker 2", "Offline" "Drucker 3", "Druckt"
Die Schlüssel der einzelnen Einträge müssen jeweils eindeutig sein - sie dürfen nicht mehrmals vorkommen. Der folgenden Scriptcode würde daher zum Beispiel zu einem Fehler führen: objDictionary.Add "Drucker 1", "Druckt" objDictionary.Add "Drucker 1", "Offline"
Versehentlich einen Schlüssel zu einem Dictionary-Objekt hinzufügen Ein potentielles Problem bei Dictionary-Objekten ist es, wenn Sie versuchen auf ein Element zuzugreifen, das noch nicht vorhanden ist. Statt den Wert für dieses Objekt zurückzugeben, wird in diesem Fall nämlich ein neuer Eintrag mit diesem Schlüssel angelegt. Das folgende Script versucht zum Beispiel auf das nicht vorhandene Element mit dem Schlüssel Printer 4 zuzugreifen: Set objDictionary = CreateObject("Scripting.Dictionary") objDictionary.Add "Drucker 1", "Druckt" objDictionary.Add "Drucker 2", "Offline" objDictionary.Add "Drucker 3", "Druckt" Wscript.Echo objDictionary.Item("Drucker 4")
Wenn das Script versucht auf das nicht vorhandene Element (Drucker 4) zuzugreifen, wird leider kein Laufzeitfehler ausgelöst. Stattdessen wird ein neues Element mit dem Schlüssel Drucker 4 zum Dictionary-Objekt hinzugefügt. Der Wert dieses Elements ist Null. Da der Wert des neuen Eintrags auch gleich angezeigt wird, sieht die Ausgabe des Scripts wie in Abbildung 4.9 aus. Seite 232 von 394
Abbildung 4.9: Ausgabe eines versehentlich hinzugefügten Elements zu einem Dictionary-Objekt Um dieses Problem zu umgehen, müssen Sie vor einem Zugriff auf ein Element prüfen, ob es das Element gibt.
Bearbeiten von Schlüsseln und Werten in einem Dictionary-Objekt Zu den Aufgaben, die Sie mit einem Dictionary-Objekt durchführen können, gehören unter anderem die folgenden: • • • • • •
Prüfen, wie viele Einträge im Dictionary-Objekt vorhanden sind. Die Einträge im Dictionary-Objekt auflisten. Prüfen, ob ein bestimmter Eintrag vorhanden ist oder nicht. Einen Wert oder einen Schlüssel ändern. Einträge aus dem Dictionary-Objekt entfernen. Diese Aktionen werden in den folgenden Abschnitt besprochen.
Die Zahl der Einträge in einem Dictionary-Objekt abfragen Wie die meisten anderen Collections hat auch das Dictionary-Objekt eine Eigenschaft mit dem Namen Count. Diese gibt die Zahl der Elemente der Collection zurück. Script 4.46 erstellt eine Instanz des Dictionary-Objekts, fügt diesem drei Einträge hinzu und gibt dann die Anzahl der Einträge über die Eigenschaft Count zurück. Script 4.46: Prüfen, wie viele Einträge in einem Dictionary-Objekt vorhanden sind 1Set objDictionary = CreateObject("Scripting.Dictionary") 2objDictionary.Add "Drucker 1", "Printing" 3objDictionary.Add "Drucker 2", "Offline" 4objDictionary.Add "Drucker 3", "Printing" 5Wscript.Echo objDictionary.Count
Wenn Sie das Script ausführen, erhalten Sie den Wert 3 zurück (die Anzahl der Einträge).
Die Elemente eines Dictionary-Objekts aufzählen Über die Methoden Keys und Items können Sie die Schlüssel oder Werte aller Einträge eines Dictionary-Objekts in einem Array zurückgeben. Nachdem Sie eine der Methoden aufgerufen haben, können Sie das Array dann mit einer For-Each-Schleife durchgehen. Script 4.47 erstellt ein einfaches Dictionary-Objekt mit drei Einträgen. Dann verwendet es die Methode Keys, um alle Schlüssel des Dictionary-Objekts im Array colKeys zu speichern und die Elemente des Arrays in einer For-Each-Schleife auszugeben. Danach wird für die Werte des Dictionary-Objekts derselbe Vorgang über die Methode Items ausgeführt. Seite 233 von 394
Script 4.47: Auflisten der Schlüssel und Werte eine Dictionary-Objekts 1 Set objDictionary = CreateObject("Scripting.Dictionary") 2 objDictionary.Add "Drucker 1", "Druckt" 3 objDictionary.Add "Drucker 2", "Offline" 4 objDictionary.Add "Drucker 3", "Druckt" 5 6 colKeys = objDictionary.Keys 7 For Each strKey in colKeys Wscript.Echo strKey 8 9 Next 10 11colItems = objDictionary.Items 12For Each strItem in colItems 13 Wscript.Echo strItem 14Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: Drucker 1 Drucker 2 Drucker 3 Druckt Offline Druckt
Um den Wert eines bestimmten Eintrags anzuzeigen, verwenden Sie die Methode Item. Die folgende Zeile zeigt zum Beispiel den Wert des Eintrags mit dem Schlüssel Drucker 3 an: Wscript.Echo objDictionary.Item("Printer 3")
Die Existenz eines Schlüssels prüfen Das Dictionary-Objekt bietet Ihnen gegenüber einem Array oder einer Collection einen großen Vorteil: Sie können sehr einfach feststellen, ob ein bestimmter Schlüssel vorhanden ist. Nehmen wir zum Beispiel einmal an, dass Sie in einer Liste mit Dateien nach bestimmten DLLs suchen möchten. Mit einer Collection oder einem Array müssen Sie die gesamte Liste in einer Schleife durchlaufen und jedes Element einzeln prüfen. Im Gegensatz dazu können Sie bei einem Dictionary-Objekt die Methode Exists verwenden mit dieser stellen Sie fest, ob ein bestimmter Schlüssel vorhanden ist. Die Methode erwartet einen Parameter (den Namen des Schlüssels). Wenn der Schlüssel existiert, dann gibt sie den Boolean-Wert True zurück - andernfalls ist der Rückgabewert False. Script 4.48 erstellt zum Beispiel ein Dictionary-Objekt und fügt diesem drei Elemente hinzu (Drucker 1, Drucker 2 und Drucker 3). Dann prüft das Script, ob ein Schlüssel mit dem Namen Drucker 4 vorhanden ist und gibt das Ergebnis dieser Prüfung aus. Script 4.48: Ein Dictionary-Objekt auf einen bestimmten Schlüssel prüfen 1Set objDictionary = CreateObject("Scripting.Dictionary") 2objDictionary.Add "Drucker 1", "Druckt" 3objDictionary.Add "Drucker 2", "Offline" 4objDictionary.Add "Drucker 3", "Druckt" 5If objDictionary.Exists("Drucker 4") Then 6 Wscript.Echo "Drucker 4 ist vorhanden." 7Else Wscript.Echo "Drucker 4 ist nicht vorhanden." 8 9End If
Seite 234 von 394
Ein Element in einem Dictionary-Objekt ändern Die Elemente eines Dictionary-Objekts sind nicht in Stein gemeißelt. Sie haben die Möglichkeit die Elemente zu ändern. Script 4.49 erstellt ein Dictionary-Objekt mit drei Schlüsseln: atl-dc-01, atl-dc-02 und atl-dc-03. Der Wert jedes Elements wird auf den Text 'No status.' Gesetzt. Danach werden die Werte der Elemente über die Methode Item geändert. Die Methode erwartet als Parameter den Schlüssel des zu ändernden Elements. Script 4.49: Ändern des Werts eines Elements in einem Dictionary-Objekt 1 Set objDictionary = CreateObject("Scripting.Dictionary") 2 objDictionary.Add "atl-dc-01", "Kein Status" 3 objDictionary.Add "atl-dc-02", "Kein Status" 4 objDictionary.Add "atl-dc-03", "Kein Status" 5 6 colKeys = objDictionary.Keys 7 For Each strKey in colKeys 8 Wscript.Echo strKey, objDictionary.Item(strKey) 9 Next 10 11objDictionary.Item("atl-dc-01") = "Verfügbar" 12objDictionary.Item("atl-dc-02") = "Verfügbar" 13objDictionary.Item("atl-dc-03") = "Nicht verfügbar" 14 15colKeys = objDictionary.Keys 16For Each strKey in colKeys 17 Wscript.Echo strKey, objDictionary.Item(strKey) 18Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: atl-dc-01 atl-dc-02 atl-dc-03 atl-dc-01 atl-dc-02 atl-dc-03
Kein Status Kein Status Kein Status Verfügbar Verfügbar Nicht verfügbar
Elemente aus einem Dictionary-Objekt entfernen Es gibt zwei Methoden, um Elemente aus einem Dictionary-Objekt zu entfernen: •
RemoveAll - entfernt alle Elemente.
•
Remove - entfernt ein bestimmtes Element.
Alle Elemente aus einem Dictionary-Objekt entfernen Um alle Elemente aus einem Dictionary-Objekt zu entfernen, können Sie die Methode RemoveAll verwenden. Script 4.50 demonstriert dies bei einem Dictionary-Objekt mit drei Elementen. Nachdem es alle Elemente angezeigt hat, entfernt es die Elemente über den folgenden Befehl: objDictionary.RemoveAll
Um zu überprüfen ob alle Elemente entfernt wurden, gibt es noch einmal alle Elemente aus. Script 4.50: Alle Elemente aus einem Dictionary-Objekt entfernen Seite 235 von 394
1 Set objDictionary = CreateObject("Scripting.Dictionary") 2 objDictionary.Add "Drucker 1", "Druckt" 3 objDictionary.Add "Drucker 2", "Offline" 4 objDictionary.Add "Drucker 3", "Druckt" 5 colKeys = objDictionary.Keys 6 Wscript.Echo "Erster Durchlauf: " 7 For Each strKey in colKeys Wscript.Echo strKey 8 9 Next 10objDictionary.RemoveAll 11colKeys = objDictionary.Keys 12Wscript.Echo VbCrLf & "Zweiter Durchlauf: " 13For Each strKey in colKeys 14 Wscript.Echo strKey 15Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: Erster Durchlauf: Drucker 1 Drucker 2 Drucker 3 Zweiter Durchlauf:
Bestimmte Einträge aus einem Dictionary-Objekt entfernen Stellen Sie sich vor, Sie haben ein Dictionary-Objekt mit den folgenden Schlüsseln: atl-dc-01 atl-dc-02 atl-dc-03 atl-dc-04 atl-dc-05
Sie verwenden das Dictionary-Objekt, um auf die Computer zuzugreifen. Beim Zugriff auf zwei Computer (atl-dc-03 und atl-dc-04) stellt das Script fest, dass diese nicht erreichbar sind. Das Script muss also später erneut versuchen diese Computer zu erreichen. Wie können Sie diese Aufgabe bewältigen? Ein Weg wäre alle erreichten Computer aus dem DictionaryObjekt zu entfernen. Nach dem ersten Durchlauf wären also noch folgende Elemente vorhanden: atl-dc-03 atl-dc-04
Bei nächsten Durchlauf werden wieder alle erfolgreich kontaktierten Computer entfernt, usw. Wenn das Dictionary-Objekt keine Einträge mehr enthält, dann wurden alle Computer erfolgreich kontaktiert. Um ein Element zu entfernen, verwenden Sie die Methode Remove. Ihr übergeben Sie als einzigen Parameter den Schlüssel des zu entfernenden Eintrags: objDictionary.Remove("atl-dc-02")
Script 4.51 erstellt ein Dictionary-Objekt mit drei Elementen und gibt dann alle Schlüssel aus. Danach entfernt es den Eintrag mit dem Schlüssel Drucker 2 und gibt die Elemente erneut aus. Script 4.51: Einen bestimmten Eintrag entfernen 1 Set objDictionary = CreateObject("Scripting.Dictionary") 2 objDictionary.Add "Drucker 1", "Druckt" 3 objDictionary.Add "Drucker 2", "Offline"
Seite 236 von 394
4 objDictionary.Add "Drucker 3", "Druckt" 5 colKeys = objDictionary.Keys 6 Wscript.Echo "Erster Durchlauf: " 7 For Each strKey in colKeys 8 Wscript.Echo strKey 9 Next 10objDictionary.Remove("Drucker 2") 11colKeys = objDictionary.Keys 12Wscript.Echo VbCrLf & "Zweiter Durchlauf: " 13For Each strKey in colKeys 14 Wscript.Echo strKey 15Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: Erster Durchlauf: Drucker 1 Drucker 2 Drucker 3 Zweiter Durchlauf: Drucker 1 Drucker 2
Scripting-Konzepte und -Technologien zur Systemadministration: Kapitel 5 - ADSIScripting (Engl. Originaltitel: ADSI Scripting Primer) Die Administration eines Verzeichnisdienstes besteht oft aus vielen sich wiederholenden Aufgaben, zum Beispiel dem Erstellen, Löschen und Ändern von Benutzern, Gruppen, OUs, Computern und anderen Verzeichniselementen. Scripte sind geradezu prädestiniert, um solche Aufgaben durchzuführen. Bei Active Directory Service Interfaces (ADSI) handelt es sich um eine Technologie, die die Script-basierte Verwaltung des Verzeichnisdiensts Active Directory ermöglicht. ADSI-fähige Scripte können eine große Menge an administrativen Aufgaben durchführen. Die Administration eines Verzeichnisdienstes besteht oft aus vielen sich wiederholenden Aufgaben, zum Beispiel dem Erstellen, Löschen und Ändern von Benutzern, Gruppen, OUs, Computern und anderen Verzeichniselementen. Scripte sind geradezu prädestiniert, um solche Aufgaben durchzuführen. Bei Active Directory Service Interfaces (ADSI) handelt es sich um eine Technologie, die die Script-basierte Verwaltung des Verzeichnisdiensts Active Directory ermöglicht. ADSI-fähige Scripte können eine große Menge an administrativen Aufgaben durchführen.
ADSI-Überblick Die Administration von Verzeichnisdiensten über Scripte ist nicht wirklich schwer. Von den ganzen hierfür erforderlichen Scripting-Fähigkeiten ist ADSI sogar die am einfachsten zu erlernende.
Seite 237 von 394
Ein einführendes Beispiel Stellen Sie sich das folgende Szenario vor: Es ist Freitagmorgen und Sie planen gerade Ihr Wochenende - außerdem haben Sie in der nächsten Woche Urlaub. Sie erhalten eine dringende E-Mail von Ihrem Chef, der Ihnen mitteilt, dass eine Gruppe Consultants Montagmorgen einen Anwendungstest durchführen will. Bei diesem Test soll an der Anwendung jeweils eine Testanmeldung mit 1.000 verschiedenen AD-Benutzerkonten durchgeführt werden. Ihre Aufgabe soll es nun sein, eine Active Directory-Domäne mit 1.000 Benutzerkonten einzurichten. Die Einrichtung der Domäne ist einfach - aber was ist mit den 1.000 Benutzerkonten? Wenn Sie die alle per Hand anlegen, dann haben Sie bis Montagmorgen zu tun. Eine solche Aufgabe können Sie jedoch hervorragend über ein Script erledigen. Script 5.1 legt 1.000 Benutzerkonten an (mit den Namen UserNo1 - UserNo1000). Script 5.1: Erstellen von 1.000 Active Directory-Benutzerkonten 1 Set objRootDSE = GetObject("LDAP://rootDSE") 2 Set objContainer = GetObject("LDAP://cn=Users," & _ 3 objRootDSE.Get("defaultNamingContext")) 4 5 For i = 1 To 1000 6 Set objLeaf = objContainer.Create("User", "cn=UserNo" & i) 7 objLeaf.Put "sAMAccountName", "UserNo" & i 8 objLeaf.SetInfo 9 Next 10Wscript.Echo "1000 Users created."
Warnung: Führen Sie Script 5.1 nicht in einer Produktionsumgebung aus. Sie legen sonst 1.000 Benutzerkonten in dieser Domäne an. Auf langsamen Domänencontrollern kann das Script bis zu fünf Minuten für seine Ausführung benötigen.
Verwaltung des Verzeichnisdienstes Script 5.1 ist zwar sehr nützlich, erledigt jedoch nur eine einzelne Aufgabe - es erstellt Benutzerkonten. Die normale Verwaltung von Active Directory umfasste jedoch sehr viel mehr. Diese Aufgaben lassen sich in vier grundlegende Kategorien unterteilen: 1.Erstellen - Zum Beispiel das Erstellen von Benutzerkonten, Gruppen, OUs, Computerkonten, Standorten, Subnetzen, veröffentlichten Druckern und Freigaben. 2.Ändern - Zum Beispiel das Konfigurieren einer Telefonnummer für ein Benutzerkonto, die Delegierung der Kontrolle über eine OU, das Löschen von Gruppenmitgliedern oder die Deaktivierung von Computerkonten. 3.Lesen - Zum Beispiel das Auslesen von Benutzernamen, Gruppenmitgliedern. 4.Löschen - Zum Beispiel das Löschen von Benutzerkonten, Gruppen und OUs. Was ADSI wirklich praktisch macht, ist die effiziente Verwaltung von Active Directory über den konsistenten Ansatz von ADSI - mit diesem Ansatz werden die unterschiedlichsten Objekte auf die gleiche Art und Weise verwaltet. So funktioniert zum Beispiel die Erstellung von Benutzern, OUs und Gruppen fast gleich. Seite 238 von 394
Das gleiche gilt für die Bearbeitung und für das Auslesen von Active Directory-Objekten. Sie können alle Objekte über die gleichen grundlegenden Schritte auslesen.
ADSI-Scripting-Grundlagen Wir werden uns Scriptbeispiele für die oben definierten grundlegenden Kategorien anschauen - Erstellen, Ändern, Lesen und Löschen. So werden Sie ein besseres Verständnis für ADSIScripting entwickeln.
Primäre ADSI-Scripting-Kategorien Erstellen von Active Directory-Objekten Script 5.1 hat Ihnen gezeigt, wie Sie 1.000 Benutzerkonten erstellen können. Im folgenden Abschnitt wird - etwas bescheidener - jeweils eine OU, ein Benutzerkonto und eine Gruppe erstellt. Die Erstellung von Active Directory-Objekten umfasste vier grundlegende Schritte: 1.Eine Verbindung mit dem Active Directory-Container aufbauen, der das neue Objekt aufnehmen soll. 2.Das Objekt erstellen. 3.Die verpflichtenden Attribute des Objektes konfigurieren (wenn erforderlich). 4.Das neue Objekt an Active Directory übermitteln. Das Ziel der drei Scripte dieses Abschnitts ist es, eine OU mit dem Namen HR und in der OU ein Benutzerkonto mit dem Namen MyerKen und eine Gruppe mit dem Namen Atl-Users zu erstellen. Erstellen einer OU Script 5.2 erstellt eine OU mit dem Namen HR in der Domäne na.fabrikam.com. Allen verpflichtenden Attributen einer OU werden von Active Directory automatisch Werte zugewiesen. Daher ist dieser Schritt in Script 5.2 nicht erforderlich. Um die OU zu erstellen, führt das Script die folgenden Schritte aus: 1.Es baut eine Verbindung mit dem Domänen-Container na.fabrikam.com auf. 2.Es erstellt eine OU mit dem Namen HR. 3.Es übermittelt die neue OU an Active Directory. Script 5.2: Erstellen einer OU 1Set objDomain = GetObject("LDAP://dc=NA,dc=fabrikam,dc=com") 2Set objOU = objDomain.Create("organizationalUnit", "ou=HR") 3objOU.SetInfo
Erstellen eines Benutzerkontos
Seite 239 von 394
Script 5.3 erstellt ein neues Benutzerkonto mit dem Namen MyerKen in der OU HR. Die OU HR befindet sich in der Domäne na.fabrkam.com. Das Script führt die folgenden Schritte durch: 1.Es baut eine Verbindung mit dem Container der OU HR in der Domäne na.fabrikam.com auf. 2.Es erstellt ein Benutzerkonto mit den Namen MyerKen. Es ist nicht notwendig den ersten Buchstaben von Vor- und Nachnamen groß zu schreiben da die Groß- und Kleinschreibung jedoch in Active Directory erhalten bleibt, fällt es den Benutzern so möglicherweise einfacher, den Vor- und Nachnamen zu unterscheiden. 3.Es setzt das verpflichtende Attribut sAMAccountName auf den Wert myerken. 4.Es übermittelt das neue Benutzerkonto an Active Directory. Script 5.3: Erstellen eines Benutzerkontos 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2Set objUser = objOU.Create("user", "cn=MyerKen") 3objUser.Put "sAMAccountName", "myerken" 4objUser.SetInfo
Erstellen einer Gruppe Script 5.4 erstellt eine globale Gruppe mit den Namen Atl-Users in der OU HR in der Domäne na.fabrikam.com. Hierzu geht das Script folgendermaßen vor: 1.Es baut eine Verbindung mit dem Container der OU HR in der Domäne na.fabrikam.com auf. 2.Es erstellt eine Gruppe mit den Namen Atl-Users (standardmäßig wird eine globale Gruppe erstellt). 3.Es setzt das einzige verpflichtende Attribut sAMAccountName auf den Wert Atl-Users. 4.Es übermittelt die neue Gruppe an Active Directory. Script 5.4: Erstellen einer Gruppe 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2Set objGroup = objOU.Create("group", "cn=Atl-Users") 3objGroup.Put "sAMAccountName", "Atl-Users" 4objGroup.SetInfo
Aus den Scripten dieses Abschnitts können wir folgende Erkenntnisse gewinnen: •
• •
Sie führen alle die gleichen grundlegenden Schritte durch (verbinden mit einem Active Directory-Container, Objekt erstellen, verpflichtende Attribute setzen und Objekt an Active Directory übermitteln). Sie verwenden alle dieselbe Methode (Create), egal was für ein Objekt erstellt wird. Die einzigen Teile, die sich bei den Scripten unterscheiden, sind die von den Methoden verwendeten Parameter. Hierzu gehört zum Beispiel der Klassenname (organizationalUnit, user und group), der angibt was für ein Objekt erstellt werden soll. Auch die zu setzenden Attribute unterscheiden sich. Seite 240 von 394
Die Schritte beim Erstellen einer OU (Script 5.2), eines Benutzerkontos (Script 5.3) und einer Gruppe (Script 5.4) sind exakt gleich. Dies gilt auch für alle übrigen Objekttypen. Um ein Objekt zu erstellen, verbindet sich das Script als erstes mit einem Container - dieser Prozess wird auch Bindung genannt. Script 5.2 verbindet sich mit der Domäne, um eine OU zu erstellen. Die Domäne können Sie sich hier als normalen Container vorstellen. Script 5.3 und Script 5.4 verbinden sich beide mit der OU. Nach der Bindung an einen Container erstellt das jeweilige Script ein Objekt. Hierzu müssen zwei Parameter angegeben werden: der Klassenname des Objekts und der Name des eigentlichen Objekts. Script 5.2 erstellt eine OU. Es gibt hierzu die Klasse organizationalUnit und den Objektnamen ou=HR an. Script 5.3 erstellt ein Benutzerkonto: Die Klasse heißt user, und der Name des neuen Objekts ist cn=MyerKen. Script 5.4 erstellt eine Gruppe. Die Klasse heißt hier group, und der Objektname lautet cn=Atl-Users. Der Vorgang findet jeweils in Zeile 3 der Scripte statt. Weitere Informationen dazu wie Sie die Objektklasse eines Objekts abfragen, finden Sie im Abschnitt ADSI Schnittstellen dieses Kapitels. Bevor ein Objekt an Active Directory übermittelt wird, müssen Sie die verpflichtenden Attribute für die Objekte setzen. Für eine OU gibt es keine verpflichtenden Attribute. Daher wird dieser Schritt in Script 5.2 nicht durchgeführt. In Zeile 4 von Script 5.3 und Script 5.4 werden jedoch verpflichtende Attribute gesetzt (sAMAccountName wird sowohl bei einem Benutzerkonto als auch bei einer Gruppe verwendet). Weitere Informationen dazu, wie Sie feststellen, welche verpflichtenden Attribute ein bestimmtes Objekt benötigt, erhalten Sie im Abschnitt Die Active Directory-Architektur in diesem Kapitel. Der letzte Schritt in der Erstellung eines Objekts ist die Übertragung (Speicherung) in Active Directory. Dieser Schritt wird jeweils in der letzten Zeile der Scripte durchgeführt.
Bearbeiten von Active Directory-Objekten Das Bearbeiten von Attributen von Active Directory-Objekten ist gleichbedeutend mit dem Ersetzen von Attributen. Beim Bearbeiten wird der vorhandene Attributwert gelöscht und der neue Attributwert geschrieben. Die Art einer Änderung hängt normalerweise vom Objekttyp und von dessen Attributen ab zum Beispiel, ob ein Attribut einen einzelnen Wert oder mehrere Werte speichert. Die folgenden Beschreibungen ändern jedoch zur Vereinfachung nur Attribute mit einem einzelnen Wert. Die Änderung eines Attributs eines Active Directory-Objekts besteht aus drei grundlegenden Schritten: 1.Eine Verbindung zu dem Active Directory-Objekt aufbauen, das geändert werden soll. 2.Ein oder mehrere Attribute des Objektes ändern. 3.Die Änderungen an Active Directory übertragen. Die drei Scripte in diesem Kapitel ändern die Attribute der drei Objekte, die weiter oben in diesem Kapitel erstellt wurden (die OU HR, der Benutzer MyerKen und die globale Gruppe Atl-Users). Da alle drei Objekte über ein Attribut description (Beschreibung) verfügen, wird dieses Attribut verändert. Ein Attribut einer OU ändern Seite 241 von 394
Script 5.5 ändert das Attribut description der OU HR in der Domäne na.fabrikam.com. Dem Attribut wird der Wert Human Resources zugewiesen. Hierbei geht das Script folgendermaßen vor: 1.Es verbindet sich mit der Objekt der OU HR in der Domäne na.fabrikam.com. Im Gegensatz zum Erstellen wird die OU hier als Objekt statt als Container bezeichnet - die liegt daran, dass bei diesem Vorgang ein Attribut eines Objekts geändert wird. 2.Es ändert das Attribut des Objekts, indem es ihm den Wert Human Resources zuweist. 3.Es überträgt die Änderung an der OU an Active Directory. Script 5.5: Das Attribut description einer OU ändern 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2objOU.Put "description", "Human Resources" 3objOU.SetInfo
Ein Attribut eines Benutzerkontos ändern Script 5.6 ändert das Attribut description des Benutzerkontos mit dem Namen MyerKen in der OU HR der Domäne na.fabrikam.com. Dem Attribut wird der Wert HR employee zugewiesen. Hierzu geht das Script folgendermaßen vor: 1.Es verbindet sich mit dem Benutzerobjekt MyerKen in der OU HR der Domäne na.fabrikam.com. 2.Es ändert das Attribut des Objekts, indem es ihm den Wert HR employee zuweist. 3.Es überträgt die Änderungen am Benutzerkonto an Active Directory. Script 5.6: Das Attribut description eines Benutzerkontos ändern 1Set objUser = _ 2 GetObject("LDAP://cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com") 3objUser.Put "description", "HR employee" 4objUser.SetInfo
Ein Attribut eines Benutzerkontos ändern Script 5.7 ändert das Attribut description der Gruppe mit dem Namen Atl-Users in der OU HR der Domäne na.fabrikam.com. Dem Attribut wird der Wert Atlanta users zugewiesen. Hierzu führt das Script die folgenden Aufgaben durch: 1.Es verbindet sich mit dem Gruppenobjekt Atl-Users in der OU HR der Domäne na.fabrikam.com. 2.Es ändert das Attribut des Objekts, indem es ihm den Wert Atlanta users zuweist. 3.Es überträgt die Änderungen an der Gruppe an Active Directory. Script 5.7: Das Attribut description einer Gruppe ändern 1Set objOU = GetObject _ 2 ("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") 3objOU.Put "description", "Atlanta users" 4objOU.SetInfo
Seite 242 von 394
Zusammenfassend kann man über die Scripte in diesem Abschnitt Folgendes sagen: • •
Sie führen alle die gleichen grundlegenden Schritte aus: Verbinden mit dem Active Directory-Objekt, Ändern des Attributes und die Änderung an Active Directory übertragen. Sie verwenden alle die gleiche Methode (Put), egal, welche Objektklasse geändert wird.
Attribute eines Active Directory-Objekts lesen In den vorhergehenden Abschnitt wird die Erstellung einer OU, eines Benutzerkontos und einer Gruppe beschrieben. Außerdem wurde das Attribut description der Objekte konfiguriert. Als Nächstes lernen Sie, wie die Attribute der Objekte ausgelesen werden. Das Lesen von Attributen von Active Directory-Objekten wird über zwei einfache Schritte durchgeführt: 1.Eine Verbindung mit dem Active Directory-Objekt aufbauen, das gelesen werden soll. 2.Ein oder mehrere Attribute des Objekts lesen. Das Ziel der drei Scripte in diesem Abschnitt ist es, das Attribut description der OU HR, des Benutzerkontos MyerKen und der Gruppe Atl-Users zu lesen und deren Werte anzuzeigen. Wichtig: Die nach den Scripten gezeigten Ausgaben werden von CScript erstellt. Wenn Sie die Scripte ausführen, sollten Sie daher sicherstellen, dass diese unter CScript ausgeführt werden. Einige der folgenden Scripte erzeugen eine größere Menge an Ausgaben. Wenn Sie diese Scripte unter WScript ausführen, werden sie eine große Menge an Nachrichtenfenstern erzeugen. Ein Attribut einer OU auslesen Script 5.8 liest das Attribut description der OU HR in der Domäne na.fabrikam.com und zeigt dieses an. Hierzu geht das Script folgendermaßen vor: 1.Es verbindet sich mit dem OU-Objekt HR in der Domäne na.fabrikam.com. 2.Es liest das Attribut description des Objekts. Script 5.8: Lesen des Attributs description einer OU 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2Wscript.Echo objOU.Get("description")
Ein Attribut eines Benutzerkontos auslesen Script 5.9 liest das Attribut description des Benutzerobjekts MyerKen in der OU HR in der Domäne na.fabrikam.com und zeigt dieses an. Hierzu geht das Script folgendermaßen vor: 1.Es verbindet sich mit dem Benutzerobjekt MeyerKen in der OU HR in der Domäne na.fabrikam.com. 2.Es liest das Attribut description des Objekts. Script 5.9: Lesen des Attributs description eines Benutzerkontos
Seite 243 von 394
1Set objUser = _ 2 GetObject("LDAP://cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com") 3Wscript.Echo objUser.Get("description")
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, erhalten Sie die folgende Ausgabe: HR employee
Reading an Attribute of a Group Script 5.10 liest das Attribut description des Gruppenobjekts Atl-Users in der OU HR in der Domäne na.fabrikam.com und zeigt dieses an. Hierzu geht das Script folgendermaßen vor: 1.Es verbindet sich mit dem Gruppenobjekt Atl-Users in der OU HR in der Domäne na.fabrikam.com. 2.Es liest das Attribut description des Objekts. Script 5.10: Lesen des Attributs description einer Gruppe 1Set objGroup = _ 2 GetObject("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") 3Wscript.Echo objGroup.Get("description")
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, erhalten Sie die folgende Ausgabe: Atlanta users
Aus den Scripten in diesem Abschnitt lassen sich die folgenden wichtigen Erkenntnisse gewinnen: • •
Sie führen alle die gleichen grundlegenden Schritte aus: eine Verbindung mit dem Active Directory-Objekt aufbauen und ein Attribut des Objekts lesen. Sie verwenden alle die gleiche Methode (Get) - egal welche Klasse das zu lesende Objekt hat.
Wie Sie in diesem Abschnitt gesehen haben, funktioniert das Lesen von Attributen bei allen Objekten gleich.
Löschen von Active Directory-Objekten Die letzte Aktion im Leben eines Objekts ist normalerweise das Löschen. Alle bisherigen Scriptbeispiele haben erst die OU erstellt, dann den Benutzer und dann die Gruppe. Die folgenden Scripte gehen nun anders herum vor. Erst wird die Gruppe gelöscht, dann der Benutzer und erst dann die OU. Das liegt daran, dass Sie mit der Methode Delete keine OUs löschen können, ohne erst die Objekte in der OU zu löschen (dies Objekte werden auch als Endknotenobjekt bezeichnet). Wenn Sie versuchen eine OU zu löschen, in der sich noch Endknotenobjekte (untergeordnete Objekte) befinden, erhalten Sie die folgende Fehlermeldung: C:\DeleteOu.vbs(2, 1) (null): Der Verzeichnisdienst kann den angeforderten Vorgang nur an einem Endknotenobjekt durchführen.
Seite 244 von 394
Die Fehlermeldung ist etwas verwirrend. Warum kann der Vorgang nur für ein Endknotenobjekt durchgeführt werden? Wenn Objekte in er OU vorhanden sind, dann ist die OU ein Containerobjekt. Sind jedoch keine Objekte mehr in der OU, dann ist die OU aus der Perspektive des Scripts kein Container mehr, sondern ein Endknotenobjekt (ein Objekt, das keine weiteren Objekte enthält). Nun ist die Fehlermeldung klarer. Da in den folgenden Beispielen erst Benutzer und Gruppen entfernt werden, tritt der Fehler hier nicht auf. Das Löschen von Active Directory-Objekten wird über zwei Schritte durchgeführt: 1.Eine Verbindung mit dem Active Directory-Container aufbauen, in dem das Objekt gespeichert ist. 2.Das Objekt löschen. Eine Gruppe löschen Script 5.11 löscht die Gruppe Atl-Users aus der OU HR in der Domäne na.fabrikam.com. 1.Es baut eine Verbindung mit dem OU-Container HR in der Domäne na.fabrikam.com auf. In diesem Fall wird die OU als Container statt als Objekt behandelt - es soll ja ein Objekt in einem Container gelöscht werden. 2.Es löscht die Gruppe Atl-Users aus der OU. Script 5.11: Löschen einer Gruppe 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2objOU.Delete "group", "cn=Atl-Users"
Ein Benutzerkonto löschen Script 5.12 löscht den Benutzer MyerKen aus der OU HR in der Domäne na.fabrikam.com. 1.Es baut eine Verbindung mit dem OU-Container HR in der Domäne na.fabrikam.com auf. 2.Es löscht den Benutzer MyerKen aus der OU. Script 5.12: Löschen eines Benutzers 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2objOU.Delete "user", "cn=MyerKen"
Löschen einer OU Script 5.12 löscht die OU HR in der Domäne na.fabrikam.com. 1.Es baut eine Verbindung mit dem OU-Objekt HR in der Domäne na.fabrikam.com auf. Es löscht die OU. Script 5.13: Löschen einer OU 1Set objDomain = GetObject("LDAP://dc=NA,dc=fabrikam,dc=com") 2objDomain.Delete "organizationalUnit", "ou=HR"
Die folgenden wichtigen Erkenntnisse können wir aus den Scripten dieses Abschnitts gewinnen: Seite 245 von 394
• •
Sie führen alle die gleichen grundlegenden Schritte aus: eine Verbindung mit dem Active Directory-Container aufbauen und ein Objekt löschen. Sie verwenden alle die gleiche Methode (Delete) - egal, welche Klasse das zu löschende Objekt hat.
Vergleich der primären Aufgaben eines ADSI-Scripts Sie haben inzwischen die vier primären Aufgabenkategorien der Active DirectoryAdministration kennen gelernt (Erstellen, Lesen, Ändern und Löschen). Die Beispielscripte haben Ihnen gezeigt, wie konsistent die Durchführung dieser Aufgaben in ADSI-Scripten ist. In Abbildung 5.1 sehen Sie noch einmal eine Zusammenfassung der Kategorien und der erforderlichen Schritte.
Abbildung 5.1: Schritte zur Durchführung von grundlegenden Aufgaben über ADSI Mit Abbildung 5.1 und den zwölf gezeigten Scripten können wir nun die folgenden Regeln im Bezug auf ADSI-Scripte definieren: •
Unabhängig von der auszuführenden Aufgabe ist Schritt Eins immer die Verbindung (das Binden) mit einem Objekt oder Container.
Seite 246 von 394
• • • • • •
Mit der Methode Create wird ein Active Directory-Objekt erstellt. Mit der Methode Delete wird ein Active Directory-Objekt gelöscht. Mit der Methode Put wird ein Attribut eines Active Directory-Objekts geschrieben. Mit der Methode Get wird ein Attribut eines Active Directory-Objekts gelesen. Mit der Methode SetInfo wird ein neues oder geändertes Attribut eines Objekts an Active Directory übertragen. Die Parameter von Create, Delete, Put und Get sind immer die gleichen.
Das Erstellen von ADSI-Scripten In den folgenden Abschnitten werden die Grundschritte der vorhergehenden Scriptbeispiele genauer untersucht. Sie erhalten so ein besseres Verständnis darüber, wie Sie Ihre eigenen ADSI-Scripte erstellen können.
Schritt 1: Eine Verbindung aufbauen Der erste Schritt in den vorherigen Scripten war immer das Aufbauen einer Verbindung zu einem Objekt. Wenn Sie ein Objekt erstellen oder löschen, müssen Sie eine Verbindung zu dem Container aufbauen in dem sich das Objekt befindet. Wenn Sie ein Objekt (beziehungsweise seine Attribute) lesen oder ändern möchten, müssen Sie eine Verbindung direkt zu dem entsprechenden Objekt aufbauen. Das Active Directory-Objekt für die Bindung auswählen Wenn Sie eine Bindung aufbauen wollen, dann müssen Sie sich überlegen, welche Aufgabe Sie durchführen wollen (Erstellen, Löschen, Lesen oder Ändern). Außerdem müssen Sie den Typ kennen (Objekt oder Container), und wenn das Objekt ein Container ist, dann müssen Sie gegebenenfalls prüfen, ob der Container leer ist. In Abbildung 5.2 sehen Sie ein Flussdiagramm, das Ihnen bei diesem Vorgang hilft.
Seite 247 von 394
Abbildung 5.2: Überlegungen beim Aufbau einer Bindung Die beiden folgenden Aufgaben, die ebenfalls über ein Script durchgeführt werden können, sind im Flussdiagramm nicht enthalten: •
•
Das Löschen eines Containers, der nicht leer ist. Mit der Methode DeleteObject können Sie einen Container löschen, der nicht leer ist. Dies ist jedoch möglicherweise gefährlich - daher sollten Sie hierbei sehr vorsichtig sein. Verschieben von Objekten aus einem Container, bevor dieser gelöscht wird. Weitere Informationen zu diesem Vorgang finden Sie im Abschnitt Verschieben und Umbenennen von Objekten weiter unten in diesem Kapitel.
Eine Bindung durchführen Um eine Bindung an ein Active Directory-Objekt zu erstellen, können Sie die VBScriptFunktion GetObject verwenden. Als Parameter geben Sie den Prefix LDAP: gefolgt von zwei Slash-Zeichen und dem Pfad des Active Directory-Objekts an. Die entsprechenden Zeilen in unseren vier vorhergehenden Beispielscripten sahen so aus: • • •
Set objDomain = GetObject("LDAP://dc=NA,dc=fabrikam,dc=com") Baut eine Bindung zur Domäne na.fabrikam.com auf. Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") Baut eine Bindung zur OU HR in der Domäne na.fabrikam.com auf. Set objUser = _ Seite 248 von 394
GetObject("LDAP://cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com") Baut eine Bindung zum Benutzerobjekt MyerKen in der OU HR in der Domäne na.fabrikam.com auf. •
Set objGroup = _ GetObject("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") Baut eine Bindung zum Gruppenobjekt Atl-Users in der OU HR der Domäne na.fabrikam.com auf.
Der Parameter ADsPath In der Terminologie von ADSI nennt sich der Prefix LDAP:, zusammen mit einem Pfad zu einem Active Directory-Objekt ADsPath (Active Directory Service Path - Active DirectoryDienst-Pfad - der Pfad zu einem Active Directory-Objekt). Bei diesem ADsPath - einem Parameter der Funktion GetObject - handelt es sich um einen String. Er muss daher immer in Anführungszeichen eingeschlossen werden. Angeben des Providers Der erste Teil von ADsPath gibt einen ADSI-Provider an. Über diesen Provider weiß das ADSI-Script aus welcher Quelle die entsprechenden Daten stammen. Um mit Active Directory oder einem anderen LDAP-Verzeichnis zu kommunizieren, verwenden Sie zum Beispiel den Provider LDAP (Lightweight Directory Access Protocol) von ADSI (Adsldp.dll). Um mit der lokalen SAM-Datenbank (Security Accounts Manager) zu kommunizieren (also mit lokalen Konten, Gruppen, usw. zu arbeiten) verwenden Sie den Provider WinNT (Adsnt.dll). Achtung: Bei den ADSI-Providern wird zwischen Groß- und Kleinschreibung unterschieden. WinNT und winnt ist also in diesem Fall nicht gleichwertig. Die Funktion GetObject von VBScript verwendet den Provider, um die richtige ADSI-DLL zu laden - dies ist notwendig, damit das Script auf die entsprechenden Informationen (zum Beispiel in Active Directory oder in der lokalen Sicherheitsdatenbank) zugreifen kann. Wenn die DLL geladen ist, dann wird mit dem zweiten Teil des Parameters ADsPath (dem Pfad zum entsprechenden Objekt) eine Bindung mit dem Objekt aufgebaut. Angeben des Verzeichnispfades des Objekts Der zweite Teil von ADsPath gibt wie gesagt den Pfad zum Objekt wieder. ADSI unterstützt mehrere Formate für diesen Objektpfad. In den vorhergehenden Beispielen wurde immer der eindeutige Name (distinguished name - DN) des Objekts verwendet. Dies ist jedoch nur eine Möglichkeit einen Objektpfad anzugeben. Weitere Möglichkeiten finden Sie im Abschnitt Root Directory Service Entry weiter unten in diesem Kapitel Jedes in Active Directory gespeicherte Objekt hat einen eindeutigen Namen (distinguished name - DN). Stellen Sie sich den DN als LDAP-Version eines vollständigen Dateisystempfades vor. Jede Datei hat einen vollständigen Pfad - dieser setzt sich aus dem Gerät, einem oder mehreren Ordnernamen und dem Namen der Datei selbst zusammen. Genauso ist es bei Active Directory -dieser vollständige Pfad eines Active Directory-Objekts nennt sich DN. So wie ein Dateipfad mehrere Ordner enthält, die jeweils durch ein Backslash getrennt sind, so setzt sich auch der DN eines Active Directory-Objekts aus mehreren Teilen zusammen Seite 249 von 394
diese werden jeweils in der Form attribut=wert angegeben. Die einzelnen Teile werden durch Kommata getrennt. Jeder Teil (attribut=wert) im DN eines Objekts steht für einen Container in Active Directory. Beim attribut handelt es sich um den Typ des DN-Teils (diese Typen sind in RFC 2247 und RFC 2253 definiert). Der Type eines Benutzerkontos ist zum Beispiel CN - der Typ eine Organisationseinheit ist OU. Anmerkung: Mit Ausnahme des Containers Domain Controllers ist der Typ aller Standardcontainer einer Active Directory-Domäne (zum Bespiel Computer, Benutzer und Built-in) CN und nicht OU. Den wert der einzelnen Pfadteile (attribut=wert)legen Sie beim Erstellen eines Objekts fest. Nehmen wir zum Beispiel einmal an, wir arbeiten mit dem Benutzer MyerKen in der OU HR in der Domäne na.fabrikam.com. In Tabelle 5.1 sehen Sie die einzelnen Objektklassen, Attributtypen und Werte für den DN dieses Benutzers ("cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com"). Tabelle 5.1: Objektklassen, Attribute und Werte Objektklasse
AttributtypWert
User
CN
MyerKen
organizationalUnitOU
HR
domain
DC
NA
domain
DC
fabrikam
domain
DC
com
Die gesamte Pfadangabe eines DNs gibt die Position des Objekts im Bezug auf den Stamm des Active Directory-Verzeichnisbaums an. Wenn Sie die einzelnen Teile des Pfades von links nach rechts durchgehen, dann gelangen Sie vom Objekt selbst bis zum Stamm der Active Directory-Hierarchie (Abbildung 5.3 verdeutlicht dies).
Abbildung 5.3: Eine Beispielhierarchie für einen DN Das Benutzerobjekt MyerKen befindet sich in der OU HR in der Unterdomäne NA der Domäne fabricam.com. Der gesamte Parameter ADsPath sieht daher für dieses Benutzerobjekt so aus: "LDAP://cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com"
Seite 250 von 394
Anmerkung: Der in Script 5.1 verwendete ADsPath-Parameter unterscheidet sich von den restlichen Beispielen dieses Kapitels - er verwendet ein bestimmtes ADSI-Feature mit dem Namen rootDSE. Weitere Informationen zu diesem Feature finden Sie im Abschnitt Root Directory Service Entry weiter unten in diesem Kapitel. Eine Objektreferenz auf ein Active Directory-Objekt erstellen Befassen wir uns nun mit dem Teil, der in einer Bindungsanweisung links vom Gleichheitszeichen steht - dem VBScript-Schlüsselwort Setgefolgt von einem Variablennamen. In den Beispielscripten wurden zum Beispiel die Variablennamen objDomain, objOU, objUser und objGroup verwendet. Im Gegensatz zu einer Zuweisung eines Strings oder einer Zahl zu einer Variable müssen Sie in diesem Fall das Schlüsselwort Set verwenden. Mit diesem Schlüsselwort weisen Sie der Variablen eine Referenz auf ein Objekt zu (die Funktion GetObject liefert ein Objekt zurück). Bei ADSI handelt es sich bei dem von GetObject zurückgegebenen Objekt um das in ADsPath angegebene Objekt. Es wird jedoch nicht das Objekt selbst zurückgegeben, sondern nur ein 'virtueller Verweis' auf das Objekt.
Schritt 2: Eine Aufgabe ausführen Das Erstellen und Löschen von Objekten ist wirklich simpel. Nachdem das Script eine Bindung zum entsprechenden Container aufgebaut hat, können Sie das Objekt in diesem Container erstellen. Auch das Lesen und Schreiben von Objektattributen ist sehr einfach. Sie bauen eine Bindung zum entsprechenden Objekt auf und lesen oder schreiben das Attribut. Wenn Sie die Methoden Create, Put (Ändern) oder Get (Lesen) verwenden, dann bearbeiten Sie jedoch nicht das Active Directory-Objekt selbst. Stattdessen werden die entsprechenden Vorgänge lokal in einem bestimmten Speicherbereich (dem so genannten local property cache - lokaler Eigenschafts-Zwischenspeicher) durchgeführt. Das oder die Attribute eines Active Directory-Objekts werden in diesem lokalen Zwischenspeicher gespeichert. Alle Änderungen am Objekt, die Sie vornehmen, werden in diesem lokalen Zwischenspeicher vorgenommen. Um die Änderungen für das eigentliche Active Directory-Objekt zu übernehmen, müssen Sie erst den lokalen Zwischenspeicher an Active Directory übermitteln die machen Sie mit der Methode SetInfo. Eine Methode, die nicht auf diese Art arbeitet, ist Delete. Sie führt ihre Aktion (das Löschen) direkt in Active Directory aus (es gibt noch einige andere Methoden, die ebenfalls direkt in Active Directory arbeiten). Scriptcode, um die eigentliche Aufgabe durchzuführen Wenn Sie sich die Beispielscripte anschauen, dann bemerken Sie, dass für die durchzuführenden Aufgaben jeweils der bei der Bindung erstelle Variablennamen mit einem Punkt und dem Namen der Methode verwendet wird: • • •
objVariableName.Create objVariableName.Get objVariableName.Put Seite 251 von 394
•
objVariableName.Delete
Die Methoden Create und Delete sind leicht zu merken, da ihr Name ihrer Aktion entspricht. Mit Get können Sie Attribute lesen und mit Put können Sie Attribute schreiben (beziehungsweise ändern). Die Parameter der einzelnen Methoden Parameter von Create Die Methode Create benötigt zwei Parameter: den Klassennamen und den RDN(relative distinguished name - relativer eindeutiger Name) des zu erstellenden Objekts. Ein RDN ist Teil der DN eines Objekts. Der RDN muss im Container des Objekts eindeutig sein - er setzt sich aus einem Attribut und dem zugewiesenen Wert zusammen. Sehen wir uns noch einmal die vorherigen Beispielscripte an: •
•
•
Set objOU = objDomain.Create("organizationalUnit", "ou=HR") Erstellt eine OU Der Klassenname ist organizationalUnit,das Attribut ist ou und der Wert ist HR. Daher lautet der RDN ou=HR. Set objUser = objOU.Create("user", "cn=MyerKen") Erstellt ein Benutzerkonto Der Klassenname ist user, das Attribut ist cn und der Wert ist MyerKen. Daher lautet der RDN cn=MyerKen. Set objGroup = objOU.Create("group", "cn=Atl-Users") Erstellt eine Gruppe Der Klassenname lautet group, das Attribut ist cn und der Wert ist Atl-Users. Daher lautet der RDN cn=Atl-Users.
Auch für die Methode Create benötigen Sie das Schlüsselwort Set. Mit diesem weisen Sie die von Create zurückgegebene Objektreferenz zu einer Variablen zu. Denken Sie daran, dass es sich nicht um eine Referenz auf das tatsächliche Objekt, sondern um eine Referenz auf das Objekt im Zwischenspeicher handelt. Nachdem Sie die erforderlichen Aufgaben am Objekt durchgeführt haben, müssen Sie das Objekt aus dem Zwischenspeicher an Active Directory übermitteln. Abbildung 5.4 zeigt noch einmal die einzelnen Teile der Aufrufe von Create in den vorherigen Scripten:
Seite 252 von 394
Abbildung 5.4: Vergleich der unterschiedlichen Aufrufe der Methode Create Parametern von Put Die Methode Put benötigt ebenfalls zwei Parameter: den Namen des Attributes und den Wert, den Sie diesem Attribut zuweisen möchten. ADSI erwartet einen bestimmten Typ von Attributnamen - den lDAPDisplayName. Der Zugriff auf Attribute ist über einige unterschiedliche Namen möglich. In ADSI-Scripten sollten Sie jedoch den lDAPDisplayName verwenden. Sehen wir uns erneut die vorherigen Beispielscripte an: •
•
•
objOU.Put "description", "Human Resources" Schreibt ein Attribut einer OU Der lDAPDisplayName des zu bearbeitenden Attributs ist description. Dem Attribut wird der Wert Human Resources zugewiesen. objUser.Put "description", "HR employee" Schreibt ein Attribut eines Benutzerkontos Der lDAPDisplayName des zu bearbeitenden Attributs ist description. Dem Attribut wird der Wert HR employee zugewiesen. objOU.Put "description", "Atlanta users" Schreibt ein Attribut einer Gruppe Der lDAPDisplayName des zu bearbeitenden Attributs ist description. Dem Attribut wird der Wert Atlanta users zugewiesen.
Auch beim Erstellen von Objekten werden möglicherweise Attribute geschrieben. Nämlich dann, wenn das Objekt verpflichtende Attribute verwendet - diesen müssen Sie vor dem Übertragen des Objekts an Active Directory einen Wert zuweisen. Seite 253 von 394
Die folgenden Zeilen aus den vorherigen Beispielscripten schreiben verpflichtende Attribute: • •
objUser.Put "sAMAccountName", "myerken" Schreibt das verpflichtende Attribut (sAMAccountName) eines Benutzerkontos. objUser.Put "sAMAccountName", "atl-users" Schreibt das verpflichtende Attribut (sAMAccountName) einer Gruppe.
Verpflichtende Attribute werden durch Active Directory definiert. Weitere Informationen zu diesem Thema erhalten Sie im Abschnitt Die Active Directory-Architektur weiter unten in diesem Kapitel. Parameter von Get Die Methode Get benötigt nur einen Parameter: den lDAPDisplayName des Attributes, das Sie lesen möchten. In Zeile 2 von Script 5.8 und Zeile 3 von Script 5.9 und Script 5.10 wird das Attribut mit dem Namen description gelesen: • • •
Wscript.Echo objOU.Get("description") Liest das Attribut description einer OU und zeigt es an. Wscript.Echo objUser.Get("description") Liest das Attribut description eines Benutzerkontos und zeigt es an. Wscript.Echo objGroup.Get("description") Liest das Attribut description einer Gruppe und zeigt es an.
Parameter von Delete Wie die Methode Create benötigt auch Delete zwei Parameter: Den Typ des zu löschenden Objekts und den RDN des Objekts. •
•
•
objOU.Delete "group", "cn=Atl-Users" Löscht eine Gruppe Der Klassenname ist group. Der RDN des Objekts ist cn=Atl-Users. objOU.Delete "user", "cn=MyerKen" Löscht ein Benutzerkonto Der Klassenname ist user. Der RDN des Objekts ist cn=MyerKen. objDomain.Delete "organizationalUnit", "ou=HR" Löscht eine OU Der Klassenname ist organizationalUnit. Der RDN des Objekts ist cn=HR.
Schritt 3: Übermitteln an Active Directory Nach dem Erstellen oder Ändern eines Objekts müssen Sie die vorgenommenen Änderungen an Active Directory übermitteln - dies machen Sie über die Methode SetInfo. Wenn Sie das Übermitteln vergessen, dann wird Ihr ADSI-Script trotzdem korrekt ausgeführt- nur die Änderungen ihres Scripts werden nie in Active Directory auftauchen. Das Übermitteln an Active Directory können Sie sich so vorstellen, wie wenn Sie ein Word-Dokument nach dem Erstellen speichern. Weitere Informationen zum lokalen Zwischenspeicher finden Sie im Abschnitt Schritt 2: Durchführen von Aufgaben weiter oben in diesem Kapitel.
Seite 254 von 394
Vielleicht ist Ihnen aufgefallen, dass die Methode SetInfo im Abschnitt Lesen der Attribute von Active Directory-Objekten nicht verwendet wurde. Das liegt daran, dass in diesen Scripten ja nur Informationen aus Active Directory ausgelesen werden - das Objekt wird nicht verändert. Daher ist es auch nicht nötig Informationen aus dem lokalen Zwischenspeicher an Active Directory zu übermitteln. Auch bei der Verwendung der Methode Delete benötigen Sie SetInfo nicht. Das Löschen von Objekten wird nicht im Zwischenspeicher, sondern direkt in Active Directory durchgeführt.
Mehrere Aufgaben über ein Script durchführen In vielen produktiven Scripten gibt es eine Menge Code, der nicht direkt etwas mit ADSI zu tun hat - dies ist in den Scripten dieses Buches nicht so. So wird es für Sie einfacher die Scripte nachzuvollziehen. Das Script ListDCs.vbs aus dem Resource Kit ist zum Beispiel 454 Zeilen lang. Von diesen 454 Zeilen haben 400 nichts mit ADSI zu zun. Warum sind sie also da? Sie sind da, weil ListDCs.vbs als Mehrzweckwerkzeug entwickelt wurde - so ähnlich wie ein Kommandozeilenbefehl. Das Script enthält zum Beispiel Code zur Verarbeitung von Argumenten, zur Formatierung von Ausgaben und zur Handhabung von Fehlern. Auch wenn dieser Code wichtig ist, so hat er doch nichts mit ADSI zu tun. Wir vermeiden es in diesem Kapitel solchen Code zu verwenden - so können Sie sich auf den Scriptcode konzentrieren, der für die Arbeit mit ADSI notwendig ist. Um ein Script zu erstellen, das mehrere Attribute eines Active Directory-Objekts liest und schreibt, können Sie die vorherigen Beispielscripte kombinieren: 1.Erstellen Sie eine Bindung an ein Objekt - Sie müssen den Pfad des Objekts kennen. 2.Attribute schreiben oder ändern - hierzu müssen Sie die Eigenschaften und Namen der entsprechenden Attribute kennen. Außerdem müssen Sie die geänderten Attribute danach an Active Directory übermitteln. 3.Attribute lesen - hierzu benötigen Sie die gleichen Informationen wie zum Schreiben von Attributen. Vorbereitende Schritte 1.Starten Sie Notepad.exe und speichern Sie eine Datei mit dem Namen ModifyUser.vbs im Ordner C:\scripts. Sie können die Scripte dieses Abschnitts dann jeweils in die Datei einfügen und diese so ausführen. 2.Prüfen Sie, ob Sie das Benutzerkonto MyerKen schon in Active Directory erstellt haben. Wenn nicht, dann erstellen Sie es jetzt. Binden an ein Benutzerobjekt Über einen der folgenden Schritte können Sie in der ersten Zeile Ihres Testscripts ModifyUser.vbs eine Bindung an das Benutzerobjekt aufbauen: 1.Wenn sich das Kontoobjekt in der OU HR derDomäne na.fabrikam.com befindet, verwenden Sie die folgenden Codezeile: Set objUser = _ GetObject("LDAP://cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com")
Seite 255 von 394
2.Wenn Sie eine OU mit einem anderen Namen verwenden und keine Domäne mit dem Namen na.fabrikam.com haben, dann ändern Sie den DN auf den Active DirectoryContainer, in dem sich das Benutzerkonto befindet. Wenn Ihre Domäne zum Beispiel contoso.com heißt und der Name der OU Test lautet, dann sollten Sie die Zeile folgendermaßen umändern: Set objUser = _ GetObject("LDAP://cn=MyerKen,ou=Test,dc=contoso,dc=com") 3.Wenn Sie das Benutzerkonto im Container User der Domäne erstellt haben, mit der Sie momentan verbunden sind, dann verwenden Sie den folgenden Code: Set objRootDSE = GetObject("LDAP://rootDSE") Set objUser = GetObject("LDAP://cn=MyerKen,cn=Users," & _ objRootDSE.Get("defaultNamingContext")) Attribute schreiben oder ändern Als Nächstes benötigen Sie die lDAPDisplayNames der Attribute, die Sie schreiben oder ändern möchten. In Abbildung 5.5 sehen Sie die allgemeinen Attribute eines Benutzerkontos und den entsprechenden lDAPDisplayName jedes Attributs.
Abbildung 5.5: Registerkarte Allgemein des Benutzerkontos MyerKen Wie Sie sehen, stimmt der jeweilige lDAPDisplayName nicht mit den Feldbezeichnungen im Eigenschaftsfenster überein. Sie können dieses also nicht verwenden, um die Attributnamen in Erfahrung zu bringen.
Seite 256 von 394
Es gibt noch zwei andere Methoden, über die Sie auf Attribute von Active Directory-Objekten zugreifen können - der Weg über den lDAPDisplayName ist jedoch der konsistenteste Ansatz. Er ist auch der einzige Weg, über den Sie Attribute mit der Methode Put schreiben können. Um den gesamten gezeigten Vorgang so einfach wie möglich zu halten, verändert das Script keine Attribute, die mehrere Werte enthalten (Multiwert-Attribute). Dies wären zum Beispiel die Attribute otherTelephone und url. Weitere Informationen zu Multiwert-Attributen erhalten Sie im Abschnitt Administration von Multiwert-Attributen weiter unten in diesem Kapitel. Der Scriptcode zum Schreiben oder Ändern der Attribute eines Benutzerobjekts sieht folgendermaßen aus: objUser.Put objUser.Put objUser.Put objUser.Put objUser.Put objUser.Put objUser.Put objUser.Put objUser.Put
"givenName", "Ken" "initials", "E." "sn", "Myer" "displayName", "Myer, Ken" "description", "HR employee" "physicalDeliveryOfficeName", "Room 4358" "telephoneNumber", "(425) 555-0100" "mail", "
[email protected]" "wWWHomePage", "http://www.fabrikam.com"
Fügen Sie Ihrem Script diese Zeilen direkt unter der Zeile zum Binden des Objekts hinzu. Als letztes müssen Sie die Änderungen noch an Active Directory übermitteln. Hierzu fügen Sie die folgende Zeile hinzu: objUser.SetInfo
Lesen und Anzeigen der geänderten Attribute Nachdem Ihr Script das Objekt verändert hat, können Sie die entsprechenden Attribute anzeigen lassen. Den entsprechenden Scriptcode sehen Sie unten. Er liest die einzelnen Attribute in Variablen ein. strGivenName = objUser.Get("givenName") strInitials = objUser.Get("initials") strSn = objUser.Get("sn") strDisplayName = objUser.Get("displayName") strPhysicalDeliveryOfficeName = _ objUser.Get("physicalDeliveryOfficeName") strTelephoneNumber = objUser.Get("telephoneNumber") strMail = objUser.Get("mail") strWwwHomePage = objUser.Get("wWWHomePage")
Nun müssen Sie die Werte in den Variablen nur noch anzeigen lassen: Wscript.Echo "givenName: " & strGivenName Wscript.Echo "initials: " & strInitials Wscript.Echo "sn: " & strSn Wscript.Echo "displayName: " & strDisplayName Wscript.Echo "physicalDeliveryOfficeName: " & _ strPhysicalDeliveryOfficeName Wscript.Echo "telephoneNumber: " & strTelephoneNumber Wscript.Echo "mail: " & strMail
Fortgeschrittene ADSI-Scripting-Techniken
Seite 257 von 394
Neben den grundlegenden Aufgaben (Erstellen, Ändern, Lesen, Löschen) gibt es noch einige andere Administrationsaufgaben. Einige dieser Aufgaben sind zum Beispiel: • • • • • •
Administration von Multiwert-Attributen Kopieren, Verschieben und Umbenennen von Objekten Cachen von Daten Suchen Auflisten der Active Directory-Objekte in Containern Verwenden des Root Directory Service Entry.
Administration von Multiwert-Attributen Die bisherigen Scripte dieses Kapitels haben alle nur Attribute mit Einzelwerten bearbeitet oder gelesen. Multiwert-Attribute enthalten mehrere Werte (wer hätte das gedacht!) - zum Beispiel die Bridgehead-Server der Domäne, die Mitglieder einer Gruppe oder die alternativen Telefonnummern eines Benutzerkontos. Anmerkung: Weitere Informationen dazu, wie Sie feststellen können, ob ein Attribut ein Einzel- oder Mutiwert-Attribut ist, finden Sie im Abschnitt Die Active Directory-Architektur weiter unten in diesem Kapitel. Das Lesen und Ändern eines Multiwert-Attributs funktioniert genau wie das Lesen und Ändern eines Einzelwert-Attributs: das Script erstellt eine Bindung an das Objekt, es verwendet eine Methode zum Ändern oder Lesen des Attributs und übermittelt dann die Änderungen an Active Directory.
Ändern von Multiwert-Attributen Wenn ein Attribut mehrere Werte enthalten kann, dann gibt es mehrere Möglichkeiten Änderungen vorzunehmen: •
Alle Einträge löschen - wennSie alle Einträge aus einem Attribut entfernen, entfernen Sie damit das ganze Attribut. Ein Attribut ohne Eintrag ist für ein ADSI-Script nicht länger Teil des Objekts.
•
Alle Einträge aktualisieren - alle vorhandenen Einträge werden durch neue ersetzt.
•
Einen Eintrag hinzufügen - die vorhandenen Einträge bleiben bestehen, ein neuer Eintrag kommt hinzu.
•
Einen Eintrag löschen
Egal welche dieser Aktionen Sie ausführen möchten - Sie verwenden immer die Methode PutEx. Der erste Parameter der Methode ist die Art der Aktualisierung, die Sie vornehmen möchten. Als zweites Argument geben Sie das zu ändernde Attribut an und als drittes Argument die Werte für das Attribut. In Tabelle 5.2 sehen Sie alle Argumente der Methode PutEx. Tabelle 5.2: Argumente der Methode PutEx Seite 258 von 394
Argument Typ
Control Code
Integer Ja (long)
Keiner
Mit dem Wert 1 werden alle Einträge gelöscht. Mit 2 werden die Einträge aktualisiert, mit 3 werden ein oder mehrere Einträge angehängt und mit 4 werden ein oder mehrere Einträge gelöscht.
Ja
Keiner
Der lDAPDisplayName des zu ändernden Attributs.
Variant Ja (array)
Keiner
Jeder Wert muss in Anführungszeichen eingeschlossen sein. Mehrere Werte müssen durch Kommata getrennt werden. Um das Attribut zu löschen, setzen Sie den Wert auf 0.
Attribute String Value
ErforderlichStandardwertBeschreibung
Tipp: • • • • •
Um die Arbeit mit ADSI-Scripten einfacher zu gestalten, sollten Sie die Parameterwerte von PutEx als Konstanten anlegen: ADS_PROPERTY_CLEAR = 1 ADS_PROPERTY_UPDATE = 2 ADS_PROPERTY_APPEND = 3 ADS_PROPERTY_DELETE = 4
Im gesamten Rest dieses Kapitels werden diese Konstanten für die Methode PutEx verwendet. Das Ändern von Multiwert-Attributen wird über drei grundlegende Schritte durchgeführt (das Definieren der Konstanten ist ein optionaler Schritt). 1.Eine Bindung an das Active Directory-Objekt erstellen, das Sie ändern möchten. 2.Die entsprechende Aufgabe durchführen. 3.Die Änderungen an Active Directory übertragen. Das erste Script im nächsten Abschnitt ändert die Mitglieder einer Gruppe über das Mulitwert-Attribut member der Gruppe. Das Script aktualisiert alle Einträge, fügt einen Eintrag hinzu, löscht einen Eintrag und löscht alle Einträge - und zwar in genau dieser Reihenfolge. Einschränkungen bei Änderungen über die Methode PutEx Mit PutEx können Sie keine doppelten Einträge zu einem Multiwert-Attribut hinzufügen. Wenn Sie einen solchen Vorgang versuchen, erhalten Sie keine Fehlermeldung - wenn Sie jedoch später die Methode SetInfo aufrufen, schlägt das Script fehl und teilt Ihnen mit, dass das Objekt bereits vorhanden ist. Wenn Sie versuchen für ein objektabhängiges Attribut ein nicht vorhandenes Objekt angeben, gibt PutEx eine Fehlermeldung zurück (zum Beispiel, wenn Sie versuchen den DN eines Benutzerkontos zu einer Gruppe hinzuzufügen, das es nicht gibt).
Seite 259 von 394
Wenn Sie versuchen einen nicht vorhandenen Eintrag über PutEx zu löschen, kommt es zu keiner Fehlermeldung. Der folgende Aufruf von SetInfo wird dann jedoch einen Fehler generieren. Sie müssen eine Änderung an einem Attribut erst mit der Methode SetInfo übertragen, bevor Sie eine weitere Änderung an demselben Attribut vornehmen. Sehen Sie sich die folgenden drei Scriptzeilen an: objUser.PutEx ADS_PROPERTY_DELETE, "otherTelephone", Array("555-1080") objUser.PutEx ADS_PROPERTY_APPEND, "otherTelephone", Array("555-1010") objUser.SetInfo
Die Telefonnummer 555-1010 wird zwar zum Attribut otherTelephone hinzugefügt, wenn Sie SetInfo ausführen, aber die Nummer 555-1080 wird nicht gelöscht. Um beide Aktionen durchzuführen, müssen Sie den folgenden Scriptcode verwenden: objUser.PutEx ADS_PROPERTY_DELETE, "otherTelephone", Array("555-1080") objUser.SetInfo objUser.PutEx ADS_PROPERTY_APPEND, "otherTelephone", Array("555-1010") objUser.SetInfo
Ein weiterer wichtiger Punkt bei der Verwendung von PutEx ist, dass die Einträge in einem Multiwert-Attribut nicht in einer bestimmten Reihenfolge gespeichert werden können. Ihr Script sollte sich also nicht auf eine bestimmte Reihenfolge der Speicherung verlassen. Löschen von Multiwert- und Einzelwert-Attributen über die Methode PutEx Mit PutEx können Sie Multiwert- und Einzelwert-Attribute löschen. Dies ist besonders im Zusammenhang mit Einzelwert-Attributen wichtig, da die Methode Put ein Attribut nicht vollständig löschen kann - Sie können es mit ihr nur auf den Wert NULL oder "" setzen. Aktualisieren eines Multiwert-Attributs einer Gruppe Script 5.14 ändert das Attribut member der Gruppe Atl-Users so, dass es zwei Mitglieder enthält. Hierzu führt das Script die folgenden Schritte durch: 1.Es definiert die Konstante ADS_PROPERTY_UPDATE mit dem Wert 2. Diese Konstante wird in den Zeilen 5-9 für die Methode PutEx verwendet. Der Wert 2 bedeutet, dass die Einträge eines Multiwert-Attributs aktualisiert werden. 2.Es baut eine Bindung zur Gruppe Atl-Users in der OU HR der Domäne na.fabrikam.com auf. 3.Es aktualisiert das Attribut member des Gruppeobjekts mit den DNs von MyerKen und LewJudy. Da PutEx mit Multiwert-Attributen arbeitet, müssen diese beiden DNs als Array übergeben werden. Beachten Sie, dass sich das Benutzerkonto MyerKen in der OU HR und das Benutzerkonto LewJudy in der OU Sales befindet - beide OUs befinden sich in der Domäne na.fabrikam.com. 4.Es überträgt die Änderungen an der Gruppe an Active Directory. Da PutEx mit dem Parameter 2 (aktualisieren) aufgerufen wurde, werden die vorhandenen Gruppenmitglieder der Gruppe Atl-Users durch die neuen Mitglieder ersetzt. Script 5.14: Aktualisieren des Attributs member einer Gruppe 1Const ADS_PROPERTY_UPDATE = 2 2Set objGroup = GetObject _
Seite 260 von 394
3 ("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") 4 5objGroup.PutEx ADS_PROPERTY_UPDATE, _ 6 "member", Array("cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com", _ 7 "cn=LewJudy,ou=Sales,dc=NA,dc=fabrikam,dc=com") 8 9objGroup.SetInfo
Einen Einträg zu einem Multiwert-Attribut einer Gruppe hinzufügen Vorausgesetzt, das Script 5.14 ausgeführt wurde, hängt Script 5.15 einen Eintrag an das Attribut member an, so das die Gruppe nun drei Mitglieder hat. Hierzu führt das Script die folgenden Schritte aus: 1.Es definiert die Konstante ADS_PROPERTY_APPEND auf den Wert 3 (anhängen). 2.Es baut eine Bindung zur Gruppe Atl-Users in der OU HR der Domäne na.fabrikam.com auf. 3.Es hängt einen Eintrag an das Attribut member an. Hierzu verwendet es die Konstante ADS_PROPERTY_APPEND und den DN des Benutzerobjekts YoungRob. Das Benutzerobjekt YoungRob befindet sich in der OU R&D der Domäne na.fabrikam.com. 4.Es übermittelt die Änderung an Active Directory. Da der Eintrag nur angehängt wurde, bleiben alle bisherigen Einträge im Attribut members der Gruppe ebenfalls erhalten. Script 5.15: Anhängen eines Eintrags im Attribut member einer Gruppe 1Const ADS_PROPERTY_APPEND = 3 2Set objGroup = GetObject _ 3 ("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") 4 5objGroup.PutEx ADS_PROPERTY_APPEND, _ 6 "member", Array("cn=YoungRob,ou=R&D,dc=NA,dc=fabrikam,dc=com") 7 8objGroup.SetInfo
Einen Eintrag aus einem Multiwert-Attribut einer Gruppe löschen Vorausgesetzt, dass Script 5.14 und Script 5.15 ausgeführt wurde, löscht Script 5.16 einen Eintrag aus dem Attribut member der Gruppe. Die Gruppe hat danach nur noch zwei Mitglieder. Hierzu führt das Script die folgenden Schritte aus: 1.Es definiert die Konstante ADS_PROPERTY_DELETE auf den Wert 4 (löschen). 2.Es baut eine Bindung zur Gruppe Atl-Users in der OU HR der Domäne na.fabrikam.com auf. 3.Es löscht einen Eintrag aus dem Attribut. Hierzu verwendet es die Konstante ADS_PROPERTY_DELETE und den DN des Benutzerobjekts MyerKen. 4.Es übermittelt die Änderung an Active Directory. Da nur der eine Eintrag gelöscht wurde, bleiben alle anderen Einträge im Attribut members der Gruppe erhalten. Script 5.16: Löschen eines Eintrags aus dem Attribut member einer Gruppe Seite 261 von 394
1Const ADS_PROPERTY_DELETE = 4 2Set objGroup = GetObject _ 3 ("LDAP://cn=Atl-Users,OU=HR,dc=NA,dc=fabrikam,dc=com") 4 5objGroup.PutEx ADS_PROPERTY_DELETE, _ 6 "member", Array("cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com") 7 8objGroup.SetInfo
Löschen eines Multiwert-Attributs einer Gruppe Script 5.17 löscht das Attribut member der Gruppe Atl-Users. Hierzu geht das Script folgendermaßen vor: 1.Es definiert die Konstante ADS_PROPERTY_CLEAR auf den Wert 4 (löschen). 2.Es baut eine Bindung zur Gruppe Atl-Users in der OU HR der Domäne na.fabrikam.com auf. 3.Es löscht das Attribut. indem es alle Einträge entfernt. Hierzu verwendet es die Konstante ADS_PROPERTY_CLEAR und den Wert 0. 4.Es übermittelt die Änderung an Active Directory. Script 5.17: Löschen des Attributs member einer Gruppe 1Const ADS_PROPERTY_CLEAR = 1 2Set objGroup = GetObject _ 3 ("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") 4 5objGroup.PutEx ADS_PROPERTY_CLEAR,"member", 0 6 7objGroup.SetInfo
Multiwert-Attribute eines Benutzerkontos aktualisieren Die Änderung von Multiwert-Attributen eines Active Directory-Objekts funktioniert unabhängig vom Typ des Objekts immer gleich. Um dies zu demonstrieren, aktualisiert Script 5.18 die Multiwert-Attribute url und otherTelephone des Benutzerkontos MyerKen. Hierzu führt das Script die folgenden Schritte durch: 1.Es definiert die Konstante ADS_PROPERTY_UPDATE mit dem Wert 2. Diese Konstante wird später für die Methode PutEx verwendet. Der Wert 2 bedeutet, dass die Einträge eines Multiwert-Attributs aktualisiert werden. 2.Es baut eine Bindung zum Benutzerobjekt MyerKen in der OU HR der Domäne na.fabrikam.com auf. 3.Es aktualisiert die Attribute url und otherTelephone. Im Gegensatz zum Attribut member einer Gruppe können Sie in diese beiden Attribute beliebige Werte schreiben. 4.Es überträgt die Änderungen an der Gruppe an Active Directory. Da PutEx mit dem Parameter 2 (aktualisieren) aufgerufen wurde, werden die vorhandenen Einträge die neuen ersetzt. Script 5.18: Aktualisieren der Attribute url und otherTelephone eines Benutzerkontos
Seite 262 von 394
1 Const ADS_PROPERTY_UPDATE = 2 2 Set objGroup = GetObject _ 3 ("LDAP://cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com") 4 5 objGroup.PutEx ADS_PROPERTY_UPDATE, _ 6 "url", Array("http://www.microsoft.com", _ 7 "http://www.fabrikam.com/na","http://www.contoso.com") 8 9 objGroup.PutEx ADS_PROPERTY_UPDATE, _ 10 "otherTelephone", Array("555-0180", "555-0182", "555-0183") 11 12objGroup.SetInfo
Lesen von Multiwert-Attributen Da ein Multiwert-Attribut mehr als einen Eintrag haben kann, müssen Sie die Methode GetEx zum Lesen der Einträge verwenden. Die Einträge werden als Array vom Typ Variant zurückgeben. Der Datentyp Variant kann jede beliebige Art von Daten enthalten. Wichtig: Auch die Methode Get kann Einträge eines Multiwert-Attributs zurückgeben. Trotzdem sollten Sie es nur für Einzelwert-Attribute verwenden. Weitere Informationen zu diesem Thema finden Sie im Abschnitt Cachen von Daten. Um die Inhalte eines Arrays anzuzeigen, benötigen Sie Scriptcode, der die Elemente des Arrays durchgeht. Eine For-Each-Schleife ist hierzu ideal geeignet. Wenn wir die benötigte Schleife einmal außer Acht lassen, benötigen Sie zwei Schritte, um einen Multiwert-Attribut auszulesen: 1.Verbinden mit dem Active Directory-Objekt, dessen Attribute Sie lesen möchten. 2.Lesen von einem oder mehreren Attributen. Der Unterschied in diesem Schritt liegt in der Verwendung der Methode GetEx statt Get wie bei einem Einzelwert-Attribut. Außerdem wird eine For-Each-Schleife verwendet, um den zurückgegebenen Array durchzugehen und dessen Inhalte anzuzeigen. Lesen eines Multiwert-Attributs einer Gruppe Script 5.19 liest die Elemente des Multiwert-Attributs member der Gruppe Atl-Users aus und zeigt diese an. Bevor Sie das Script ausführen, sollten Sie noch einmal Script 5.14 ausführen (um sicherzustellen, dass die Gruppe auch Mitglieder hat). Das Script geht folgendermaßen vor: 1.Es baut eine Bindung mit dem Gruppenobjekt Atl-Users in der OU HR der Domäne na.fabrikam.com auf. 2.Es verwendet eine For-Each-Schleife, um die Elemente des Attributs zu lesen und auszugeben. Script 5.19: Auslesen des Attributs member einer Gruppe 1Set objGroup = GetObject _ 2 ("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") 3 4For Each Member in objGroup.GetEx("member")
Seite 263 von 394
5 Wscript.Echo Member 6Next
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, sollte es die folgenden Informationen ausgeben: CN=LewJudy,OU=Sales,dc=NA,DC=fabrikam,DC=com CN=MyerKen,OU=HR,dc=NA,DC=fabrikam,DC=com
Lesen von Multiwert-Attributen eines Benutzerkontos Script 5.20 liest die Einträge url und otherTelephone des Benutzerkontos MyerKen. 1.Es baut eine Bindung zum Benutzerkonto MyerKen in der OU HR der Domäne na.fabrikam.com auf. 2.Es verwendet eine For-Each-Schleife, um alle Elemente des Attributs url auszugeben. 3.Es verwendet eine weitere Schleife, um alle Elemente des Attributs otherTelephone auszugeben. Script 5.20: Auslesen der Attribute url und otherTelephone eines Benutzerkontos 1 Set objGroup = GetObject _ 2 ("LDAP://cn=MyerKen,ou=HR,dc=NA,dc=fabrikam,dc=com") 3 4 For Each url in objGroup.GetEx("url") 5 Wscript.Echo url 6 Next 7 8 For Each otherTelephone in objGroup.GetEx("otherTelephone") Wscript.Echo otherTelephone 9 10Next
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, erhalten Sie die folgende Ausgabe: http://www.contoso.com http://www.fabrikam.com/na http://www.microsoft.com 555-0183 555-0182 555-0180
Die folgenden wichtigen Erkenntnisse können wir aus den Scripten dieses Abschnittes gewinnen: • •
Sie führen alle die gleichen grundlegenden Schritte aus: Aufbau einer Bindung und Ausgeben der Elemente über die For-Each-Schleife. Sie verwenden alle die gleiche Methode (GetEx) - egal um was für ein Objekt es sich handelt.
Das Cachen von Daten Bis jetzt haben Sie relativ wenig über den lokalen Zwischenspeicher (Cache) erfahren, in dem ADSI die Eigenschaften hinterlegt, beziehungsweise ändert. Sie wissen, dass die Methoden Get und GetEx Attribute lesen. Beide Methoden verwenden in Hintergrund jedoch die Methode GetInfo um die Attributwerte aus Active Directory in den lokalen Zwischenspeicher Seite 264 von 394
zu laden. Auch Sie können die Methode GetInfo (beziehungsweise GetInfoEx) verwenden - so können Sie steuern, welche Werte sich im Zwischenspeicher befinden. Weitere Informationen über den Zwischenspeicher finden Sie im Abschnitt Schritt 2: Ausführen von Aufgaben dieses Kapitels. Wenn Sie verstehen, wie der lokale Zwischenspeicher arbeitet, wird dies verhindern, dass Sie versehentlich falsche Attributwerte auslesen. Wie die Methode Get Attributwerte abfragt Get fragt Attribute aus dem lokalen Zwischenspeicher entweder als String oder als VariantArray ab - abhängig vom Typ des Attributs und seinen Inhalten: •
Einzelwert-Attribute - Get gibt einen String zurück.
•
Multiwert-Attribut mit einem Eintrag -Get gibt einen String zurück.
•
Multiwert-Attribut mit mehreren Einträgen -Get gibt einen Variant-Array zurück.
Wichtig: Ein Attribut, das keinen Wert enthält, wird von ADSI nicht berücksichtigt. Daher werden solche Attribute niemals in den lokalen Zwischenspeicher eingelesen. In allen bisherigen Beispielen wurde vorgeschlagen, für Einzelwert-Attribute die Methode Get und für Multiwert-Attribute die Methode GetEx zu verwenden. Mit dieser Methode müssen Sie nicht erst prüfen, ob es sich beim Rückgabewert um einen String oder um ein Variant-Array handelt. Wenn Sie jedoch wissen, dass das abzufragende Attribut ein Einzelwert-Attribut ist oder wenn es sich um ein Multiwert-Attribut handelt, bei dem Sie die Anzahl der Einträge kennen, dann ist die Methode Get deutlich effizienter als die Methode GetEx. Wenn ein Attribut noch nicht in den lokalen Zwischenspeicher geladen wurde, dann ruft die Methode Get bei ihrer Verwendung explizit die Methode GetInfo auf. Als Ergebnis lädt GetInfo die meisten Attribute in den lokalen Zwischenspeicher - mit Ausnahme von operativen Attributen und Attributen, die sich bereits im Zwischenspeicher befinden. Operative Attribute sind die Attribute, deren Werte nicht gespeichert sind, sondern erst dann vom Domänencontroller berechnet werden, wenn Sie abgefragt werden - das Attribut canonicalName ist zum Beispiel ein solches Attribut. Wenn Sie die Methode Get danach erneut verwenden, dann wird das gelesene Attribut aus dem Zwischenspeicher gelesen - GetInfo wird nicht erneut aufgerufen. Wie die Methode GetEx Attribute abfragt Die Methode GetEx gibt die Werte des abgefragten Attributs immer als Variant-Array zurück. Daher müssen Sie auch immer eine For-Each-Schleife zur Abfrage der Werte verwenden. Wenn Sie nicht wissen, ob Sie es mit einem Einzelwert- oder Multiwert-Attribut zu tun haben, dann kann es einfacher sein, die Methode GetEx statt Get zu verwenden. Sie erhalten dann alle Attributwerte als Variant-Array und können alle Aufrufe gleich handhaben. Wenn sich das Attribut noch nicht im Zwischenspeicher befindet, ruft auch die Methode GetEx intern die Methode GetInfo auf. Attribute, die sich bereits im Zwischenspeicher befinden, werden auch durch einen expliziten Aufruf von GetInfo nicht überschrieben.
Seite 265 von 394
Die Methode GetInfo explizit aufrufen Sie können den lokalen Zwischenspeicher über die Methode GetInfo explizit neu laden oder aktualisieren. Wenn Sie die Methode aufrufen, lädt sie alle Attribute (außer den operativen Attributen) in den Zwischenspeicher. Sie sollten die Methode selbst aufrufen, wenn Sie sicher sein wollen, dass im Zwischenspeicher die aktuellsten Werte vorhanden sind. Die Syntax der Methode GetInfo sieht folgendermaßen aus: object.GetInfo
Script 5.21 lädt die meisten Attribute des Gruppenobjekts Atl-Users in den lokalen Zwischenspeicher. 1.Es baut eine Bindung an die Gruppe Atl-Users in der OU HR der Domäne na.fabrikam.com auf. 2.Es verwendet die Methode GetInfo, um die Attribute des Gruppenobjekts in den lokalen Zwischenspeicher zu laden. Script 5.21: Expliziter Aufruf von GetInfo 1Set objGroup = _ 2 GetObject("LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com") 3objGroup.GetInfo
Sie sollten mit dem Aufruf von GetInfo vorsichtig sein – alle Änderungen, die im Zwischenspeicher an Attributen vorgenommen wurden und die noch nicht an Active Directory übermittelt wurden, gehen mit dem Aufruf verloren. Sie sollten also sicherstellen, dass Sie vor dem Aufruf von GetInfo alle Änderungen mit SetInfo übermittelt haben. In Zeile 2 des folgenden Codes wird das Attribut description auf den Wert Human Resources gesetzt (natürlich nur im lokalen Zwischenspeicher). In Zeile 3 wird dann der lokale Zwischenspeicher an Active Directory übertragen. 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2objOU.Put "description", "Human Resources" 3objOU.SetInfo
Sehen Sie sich im Gegensatz dazu das folgende Script an: 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2objOU.Put "description", "Human Resources" 3objOU.GetInfo 4objOU.SetInfo
In Zeile 2 wird weiterhin der Wert Human Resources in das Attribut description geschrieben. In Zeile 3 wird dann jedoch der lokale Zwischenspeicher explizit neu geladen - daher wird der Wert des Attributs überschrieben (wenn die Methode GetInfo von Get oder GetEx aufgerufen wird, ist dies nicht weiter schlimm - hier wird der Wert nur dann geladen, wenn er im Zwischenspeicher noch nicht vorhanden ist).
Die Methode GetInfoEx Statt alle Attribute neu in den Zwischenspeicher zu laden, können Sie sich auf die gewünschten Attribute beschränken - hierzu verwenden Sie die Methode GetInfoEx. Über diese Methode können Sie übrigens auch die operativen Attribute abfragen. Auch wenn Sie Seite 266 von 394
die Last auf dem Domänencontroller gering halten möchten, dann ist GetInfoEx hierzu ideal geeignet. Die Syntax der Methode sieht folgendermaßen aus: object.GetInfoEx Attributes, 0
Der Parameter Attributes ist ein Array mit dem lDAPDisplayName der Attribute, die Sie neu laden möchten. Der zweite Parameter (0) ist reserviert. Er muss bei jedem Aufruf der Methode GetInfoEx angegeben werden. Um die Attribute an die Methode GetInfoEx zu übergeben, erstellen Sie einen Array aus den entsprechenden lDAPDisplayName-Werten dieser Attribute. Script 5.22 lädt auf diese Weise zwei Attribute neu: description und dnsHostName. 1.Es baut eine Bindung zum Computerobjekt SEA-SQL-01 im Container Computers der Domäne na.fabrikam.com auf. 2.Es initialisiert eine Variable mit dem Namen arrAttributes mit einem Array mit den lDAPDisplayName-Werten der beiden Attribute (description und dnsHostName). 3.Es verwendet die Methode GetInfoEx, um explizit die beiden Attribute neu zu laden. Script 5.22: Attribute über GetInfoEx neu laden 1Set objComputer = GetObject _ 2 ("LDAP://cn=SEA-SQL-01,cn=Computers,dc=NA,dc=fabrikam,dc=com") 3arrAttributes = Array("description", "dnsHostName") 4objComputer.GetInfoEx arrAttributes, 0
Wie bei der Methode GetInfo müssen Sie auch bei der Methode GetInfoEx vorsichtig sein, wenn Sie Attribute ändern. Vergessen Sie nicht die geänderten Attribute über SetInfo an Active Directory zu übermitteln, bevor Sie sie neu laden. Ansonsten gehen die Änderungen verloren. In den Zeilen 3 und 4 des folgenden Scripts werden zum Beispiel die Werte der Attribute description und dnsHostName im lokalen Zwischenspeicher geändert. Wenn die gleichen Attribute in Zeile 7 über GetInfoEx neu geladen werden, sind die geänderten Werte verloren. In Zeile 9 werden so mit SetInfo die alten (neu geladenen) Werte zurück an Active Directory übermittelt. 1Set objComputer = GetObject _ 2 ("LDAP://cn=SEA-SQL-01,cn=Computers,dc=NA,dc=fabrikam,dc=com") 3objComputer.Put "description", "SQL Computer 1" 4objComputer.Put "dnsHostName", "sea-sql-01.na.fabrikam.com" 5 6arrAttributes = Array("description", "dnsHostName") 7objComputer.GetInfoEx arrAttributes, 0 8objComputer.SetInfo
Das Problem umgehen Sie, wenn Sie die Zeile 8 (den Aufruf von SetInfo) in Zeile 5 verschieben.
Seite 267 von 394
Kopieren, Verschieben und Umbenennen von Objekten Kopieren von Objekten Um ein Objekt zu kopieren, benötigen Sie zuerst eine Quelle [aus der?] die Sie kopieren können. Außerdem müssen Sie festlegen, welche Attribute des Objekts Sie kopieren möchten. Um ein Objekt zu kopieren, kombinieren Sie einfach mehrere Aufgaben, die Sie bereits weiter oben in diesem Kapitel kennen gelernt haben: 1.Erstellen Sie als erstes das Zielobjekt. Weitere Informationen hierzu finden Sie im Abschnitt Erstellen von Active DirectoryObjekten weiter oben in diesem Kapitel. 2.Lesen Sie die Attribute des Quellobjekts aus, die auf das Zielobjekt kopiert werden sollen. Weitere Informationen hierzu finden Sie in den Abschnitten Lesen von Attributen von Active Directory-Objekten und Lesen von Multiwert-Attributen weiter oben in diesem Kapitel. 3.Schreiben Sie die Attributwerte in das Zielobjekt. Weitere Informationen finden Sie in den Abschnitten Ändern eines Active Directory-Objekts und Ändern von Multiwert-Attributen weiter oben in diesem Kapitel. Die Scripte in diesem Abschnitt zeigen Ihnen, wie Sie ein Objekt kopieren können. Das erste Beispiel kopiert zum Beispiel ein Computerobjekt. Kopieren eines Computerobjekts Script 5.23 kopiert ausgewählte Attribute eines Computerobjekts mit dem Namen SEA-PM-01 in ein neues Computerobjekt mit dem Namen SEA-SQL-01. Das Computerobjekt, dass als Vorlage verwendet wird (SEA-PM-01) befindet sich im Container Computers der Domäne na.fabrikam.com. Das neue Computerobjekt wird ebenfalls im Container Computers erstellt. 1.Das Script baut eine Bindung auf den Container Computers der Domäne na.fabrikam.com auf. Achten Sie darauf, dass der Container Computers den Typ CN hat (Außer dem Container Domain Controllers haben alle Container direkt unter der Domäne das Attribut CN). 2.Es erstellt ein Computerobjekt mit den Namen sea-sql-01. 3.Es setzt das verpflichtende Attribut sAMAccountName des neuen Objekts auf den Wert seasql-01. 4.Es überträgt das neue Computerobjekt an Active Directory. Hiermit ist das neue Computerobjekt fertig. 5.Das Script baut eine Bindung auf das Computerobjekt SEA-PM-01 in dem Container Computers der Domäne na.fabrikam.com auf. Dieses Computerobjekt dient als Vorlage des Kopiervorgangs auf das neue Computerobjekt. 6.Es erstellt einen Array mit dem Namen arrAttributes, das die lDAPDisplayNames der optionalen Attribute enthält, die auf das neue Computerobjekt kopiert werden sollen. 7.Es verwendet eine For-Each-Schleife, um das Array durchzugehen. •
Es liest jedes Attribut aus dem Vorlagen-Computerobjekt aus. Seite 268 von 394
•
Es schreibt das ausgelesene Attribut in das neue Computerobjekt.
8.Es überträgt die Änderungen am neuen Computerobjekt an Active Directory. Script 5.23: Kopieren eines Computerobjekts 1 Set objCompt = GetObject("LDAP://cn=Computers,dc=NA,dc=fabrikam,dc=com") 2 Set objComptCopy = objCompt.Create("computer", "cn=SEA-SQL-01") 3 objComptCopy.Put "sAMAccountName", "sea-sql-01" 4 objComptCopy.SetInfo 5 6 Set objComptTemplate = _ 7 GetObject("LDAP://cn=SEA-PM8 01,cn=Computers,dc=NA,dc=fabrikam,dc=com") 9 arrAttributes = Array("description", "location") 10 11For Each strAttrib in arrAttributes 12 strValue = objComptTemplate.Get(strAttrib) 13 objComptCopy.Put strAttrib, strValue 14Next 15 objComptCopy.SetInfo
Auch wenn in Script 5.23 nur zwei Attribute kopiert werden, können Sie natürlich so viele Attribute kopieren, wie Sie möchten. Kopieren eines Benutzerkontos Script 5.24 kopiert ausgewählte Einzelwert- und Multiwert-Attribute vom Benutzerkonto HuffArlene auf ein neues Benutzerkonto mit den Namen BarrAdam. Das Benutzerkonto, das als Vorlage verwendet wird (HuffArlene), befindet sich in der OU HR der Domäne na.fabrikam.com. Das neue Benutzerkonto (BarrAdam) wird ebenfalls in der OU HR erstellt. Das Script geht hierbei folgendermaßen vor: 1.Es definiert die Konstante ADS_PROPERTY_UPDATE mit dem Wert 2 (diese wird später von der Methode PutEx verwendet, um einen Eintrag in einem Multiwert-Attribut zu ändern (in Zeile 21). 2.Es baut eine Bindung auf die OU HR in der Domäne na.fabrikam.com auf. 3.Es erstellt ein Benutzerkonto mit dem Namen BarrAdam. 4.Es setzt das verpflichtende Attribut sAMAccountName auf den Wert barradam. 5.Es übermittelt das neue Benutzerkonto an Active Directory. 6.Es baut eine Bindung auf das Benutzerkonto HuffArlene in der OU HR der Domäne na.fabrikam.com auf. 7.Es erstellt ein Array mit dem Namen arrSVAttributes, das die lDAPDisplayNames-Werte der optionalen Einzelwert-Attribute enthält, die auf das neue Benutzerkonto kopiert werden sollen. 8.Es erstellt ein weiteres Array mit dem Namen arrMVAttributes, das die lDAPDisplayNames-Werte der optionalen Multiwert-Attribute enthält, die auf das neue Benutzerkonto kopiert werden sollen.
Seite 269 von 394
9.Es verwendet eine For-Each-Schleife, um das Array arrSVAttributes durchzugehen. In der Schleife verwendet es die Methode Get, um die einzelnen Attribute aus dem Benutzerkonto auszulesen, das als Vorlage dient und die Methode Put, um diese Attribute in das neue Benutzerkonto zu schreiben. 10.Es verwendet eine weitere For-Each-Schleife, um den Vorgang mit dem Array arrMVAttributes und den Multiwert-Attributen zu wiederholen. 11.Es übermittelt die Änderungen an dem neuen Benutzerkonto an Active Directory. Script 5.24: Kopieren eines Benutzerkontos 1 Const ADS_PROPERTY_UPDATE = 2 2 Set objOU = GetObject("LDAP://OU=HR,dc=NA,dc=fabrikam,dc=com") 3 Set objUserCopy = objOU.Create("user", "cn=BarrAdam") 4 objUserCopy.Put "sAMAccountName", "barradam" 5 objUserCopy.SetInfo 6 7 Set objUserTemplate = _ 8 GetObject("LDAP://cn=HuffArlene,ou=HR,dc=NA,dc=fabrikam,dc=com") 9 10arrSVAttributes = Array("description", "department", _ "company", "wWWHomePage") 11 12arrMVAttributes = Array("url", "otherTelephone") 13 14For Each strAttrib in arrSVAttributes 15 strValue = objUserTemplate.Get(strAttrib) objUserCopy.Put strAttrib, strValue 16 17Next 18 19For Each strAttrib in arrMVAttributes arrValue = objUserTemplate.GetEx(strAttrib) 20 objUserCopy.PutEx ADS_PROPERTY_UPDATE, strAttrib, arrValue 21 22Next 23 24objUserCopy.SetInfo
Aus den Scripten dieses Abschnitts können wir die folgenden wichtigen Schlussfolgerungen ziehen: • •
Zum Kopieren von Active Directory-Objekten sind drei Schritte nötig: ein Objekt erstellen, Attribute aus dem Vorlagen-Objekt lesen und die Attribute in das neue Objekt schreiben. Es gibt keine Methode, um eine direkte Kopie zu erstellen.
Verschieben und Umbenennen von Objekten Zum Umbenennen und zum Verschieben wird dieselbe Methode verwendet: MoveHere. Im Gegensatz zum Kopieren ist das Verschieben und Umbenennen über ein ADSI-Script relativ einfach. Mit der Methode sind die folgenden Aktionen möglich: • • •
Verschieben eines Objekts in einen anderen Container in der gleichen Domäne. Umbenennen eines Objekts im gleichen Container . Umbenennen und Verschieben eines Objekts in einen anderen Container der gleichen Domäne. Seite 270 von 394
• •
Verschieben eines Objekts in eine andere Domäne. Umbenennen und Verschieben eines Objekts in eine andere Domäne.
Das Verschieben in eine andere Struktur oder Gesamtstruktur ist mit der Methode MoveHere nicht möglicht. Bedingungen für das Verschieben über Domänengrenzen hinweg Es gibt einige objektspezifische Einschränkungen beim Verschieben über Domänengrenzen hinweg. Die folgenden Bedingungen müssen erfüllt sein, damit dies möglich ist: • • • • • •
Die Zieldomäne muss im nativen Modus ausgeführt werden. Sowohl Ziel- als auch Quelldomäne müssen die Kerberos-Authentifizierung verwenden. Die Verschieben-Operation muss von der Quelldomäne aus durchgeführt werden. Sie benötigen die entsprechenden Berechtigungen, um das Objekt aus der Quelldomäne zu entfernen und in der Zieldomäne anzulegen. Das Verschieben von Containern mit Inhalten ist nicht möglich. Security-Principals wie zum Beispiel Benutzer, Gruppe und Computer können nicht in eine andere Domäne verschoben werden, wenn sie Mitglieder einer oder mehrerer globaler Gruppen sind.
Die Methode MoveHere erwartet zwei Argumente. Beim ersten Argument handelt es sich um den ADsPfad des zu verschiebenden oder umzubenennenden Objekts. Der DN im ADsPfad legt fest, wo sich das Objekt im Moment befindet. Das zweite Argument ist der RDN des zu verschiebenden Objekts oder den neuen Namen des umzubenennenden Objekts. Wenn Sie das Objekt nur verschieben und nicht umbenennen möchten, dann können Sie als zweiten Parameter die Konstante vbNullString angeben. In Tabelle 5.3 sind die Argumente der Methode MoveHere noch einmal zusammengefasst. Tabelle 5.3: Argumente der Methode MoveHere Argument
Type ErforderlichStandardwertBeschreibung
ADsPath
StringJa
Keiner
Name des Providers und der DN des Quellobjekts, das verschoben oder umbenannt werden soll.
RelativeDistinguishedNameStringJa
Keiner
Das Attribut (cn=name) des Objekts, das verschoben wird oder der neue Name des umzubenennenden Objekts. Wenn das Objekt nicht umbenannt werden soll, verwenden Sie die Konstante vbNullString.
Unabhängig ob Sie ein Objekt umbenennen oder verschieben wollen, müssen Sie zwei grundlegende Schritte durchführen: 1.Eine Bindung an den Active Directory-Container aufbauen, der als Ziel für die Verschiebeoder Umbenenn-Operation dienen soll. Seite 271 von 394
2.Verschieben oder Umbenennen des Objekts. Wie die Methode Delete, arbeitet auch die Methode MoveHere direkt in Active Directory. Daher ist es nicht notwendig die Methode SetInfo aufzurufen. Das Ziel der drei Scripte in diesem Abschnitt ist das Umbenennen eines veröffentlichten Druckers, das Verschieben einer Gruppe in eine OU der gleichen Domäne und das Verschieben eine OU in eine andere Domäne. Umbenennen eines veröffentlichten Druckers Script 5.25 benennt den Drucker Printer1 in HRPrn1 um. Der Drucker bleibt in der OU HR der Domäne na.fabrikam.com. 1.Das Script baut eine Bindung zur OU HR in der Domäne na.fabrikam.com auf. Hierdurch wird die OU HR zum Zielcontainer der Umbenenn-Operation. 2.Es benennt den Drucker Printer1 in HRPrn1 um. Als erster Parameter der Methode MoveHere wird der ADsPfad des umzubenennenden Objekts angegeben (der LDAP-Provider und der DN des Druckers Printer1). Der zweite Parameter ist der neue Name des Druckers. Script 5.25: Umbenennen eines veröffentlichten Druckers 1Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 2 3objOU.MoveHere _ 4 "LDAP://cn=Printer1,ou=HR,dc=NA,dc=fabrikam,dc=com", "cn=HRPrn1"
Verschieben einer Gruppe von einem Container in einen anderen Script 5.26 verschiebt die Gruppe Atl-Users aus der OU HR in den Container Users der Domäne na.fabrikam.com. Beachten Sie, dass die Gruppe nicht verändert wird - sie wird nicht umbenannt und die Gruppenmitgliedschaften werden nicht verändert. 1.Das Script baut eine Bindung auf den Container Users der Domäne na.fabrikam.com auf. Der Container Users wird somit zum Ziel der Verschiebeoperation. 2.Es verschiebt die Gruppe Atl-Users aus der OU HR in den Container Users in derselben Domäne. Der erste Parameter der Methode MoveHere ist der ADsPfad des zu verschiebenden Objekts - der LDAP-Provider und der DN der Gruppe Atl-Users group. Da keine Umbenennung durchgeführt werden soll, ist der zweite Parameter die Konstante vbNullString. Script 5.26: Verschieben einer Gruppe innerhalb der gleichen Domäne 1Set objOU = GetObject("LDAP://cn=Users,dc=NA,dc=fabrikam,dc=com") 2 3objOU.MoveHere "LDAP://cn=Atl-Users,ou=HR,dc=NA,dc=fabrikam,dc=com", _ 4 vbNullString
Verschieben einer OU von einer Domäne in eine andere Script 5.27 verschiebt die OU Management aus der Domäne fabrikam.com in die Domäne na.fabrikam.com.
Seite 272 von 394
1.Das Script baut eine Bindung an die Domäne na.fabrikam.com auf. Diese Bindung dient als Ziel für die Verschiebeoperation. 2.Es verschiebt die OU in die Domäne na.fabrikam.com. Script 5.27: Verschieben einer OU über eine Domänengrenze hinweg 1Set objDomain = GetObject("LDAP://dc=NA,dc=fabrikam,dc=com") 2 3objDomain.MoveHere "LDAP://ou=Management,dc=fabrikam,dc=com", _ 4 vbNullString
Auch wenn Script 5.27 nützlich sein könnte, so ist es in dieser Form unsinnig. Das Verschieben zwischen Domänen ist nur für leere Containerobjekte möglich. Daher muss die OU geleert werden, bevor sie verschoben wird. Wir können die folgenden wichtigen Erkenntnisse aus den Scripten dieses Abschnitts ziehen: •
•
Sie führen alle die gleichen grundlegenden Schritte durch: eine Bindung an den Zielcontainer aufbauen und die Methode MoveHere ausführen, um das Objekt zu verschieben oder umzubenennen. Sie verwenden alle die gleiche Methode (MoveHere) - egal um welchen Objekttyp es sich handelt.
Suchen Unter VBScript steht Ihnen als Abfragetechnologie ADO (ActiveX® Data Objects) zur Verfügung. ADO verwendet den ADSI OLE DB-Provider, um Informationen ausActive Directory zu lesen. OLE DB ist eine Schnittstellensammlung, die einen Zugriff auf viele unterschiedliche Datenbanken - inklusive Active Directory - ermöglicht. Die Informationen, die von ADO über den ADSI-OLE DB-Provider zurückgegeben werden, können nur gelesen werden - das Schreiben ist nicht möglich. Trotzdem können Sie natürlich mit den Informationen, die Sie über ADO erhalten, weitere ADSI-Methoden zur Änderung von Active Directory-Daten verwenden. An einer Abfrage sind normalerweise drei ADO-Objekte beteiligt: •
Connection - Dieses Objekt stellt über den ADSI-OLE DB-Provider eine Verbindung zwischen dem ADSI-Script und Active Directory zur Verfügung.
•
Command - Dieses Objekt ermöglicht es dem Script eine Abfrage gegen Active Directory durchzuführen.
•
RecordSet - Dieses Objekt nimmt die Abfrageergebnisse vom Command-Objekt entgegen.
Suchen in Active Directory Das Ziel einer Suche ist die Rückgabe eine Ergebnissatzes mit keinem oder mehreren Einträgen. Auch ein Ergebnissatz ohne Einträge kann nützlich sein - er zeigt Ihnen, dass ein Objekt nicht vorhanden ist. Das Suchen nach Active Directory-Objekten umfasst die folgenden Schritte: 1.Erstellen eines ADO-Connection-Objekts und die Verwendung der Methode Open dieses Seite 273 von 394
Objekts, um über den ADSI-OLE DB-Provider auf Active Directory zuzugreifen. 2.Erstellen eines ADO-Command-Objekts und zuweisen der Eigenschaft ActiveConnection des Objekts zum Connection-Objekt. Dieser Schritt ist notwendig, da das Command-Objekt sowohl die Verbindung als auch den Abfragestring enthält. 3.Zuweisen einer Suchanfrage (Abfragestring) zur Eigenschaft CommandText des CommandObjekts. Sie können entweder eine SQL-Syntax oder einen LDAP-Dialekt verwenden. Alle Beispiel in diesem Kapitel verwenden den LDAP-Dialekt. Weitere Informationen über die SQLSyntax finden Sie unter dem Link Active Directory Programmer's Guide unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig). 4.Ausführen der Methode Execute des Command-Objekts, um die Abfrage auszuführen und die Ergebnisse in einem RecordSet-Objekt zu speichern. 5.Verwenden der Eigenschaften des RecordSet-Objekts, um Informationen aus dem Ergebnissatz zu lesen. 6.Verwenden der Methode Close des Connection-Objekts, um die Verbindung wieder zu schließen. Dieser letzte Schritt ist optional - Sie sollten ihn allerdings auf jeden Fall durchführen. Bei größeren Scripten sparen Sie so Ressourcen. Ziel der Scripte in diesem Abschnitt ist es, Ihnen zu zeigen, wie Active Directory-Suchen über ADO und den ADSI-OLE DB-Provider durchgeführt werden. Die oben beschrieben Schritte werden von allen Scripten durchgeführt. Erstellen eines einfachen Suchscripts Script 5.28 gibt als Ergebnissatz die Namen aller Objekte in der Domäne na.fabrikam.com zurück. Hierzu führt das Script die folgenden Schritte aus: 1.Erstellen eines ADO-Connection-Objekts und die Verwendung der Methode Open dieses Objekts, um über den ADSI-OLE DB-Provider auf Active Directory zuzugreifen. 2.Erstellen eines ADO-Command-Objekts und zuweisen der Eigenschaft ActiveConnection des Objekts zum Connection-Objekt. Dieser Schritt ist notwendig, da das Command-Objekt sowohl die Verbindung als auch den Abfragestring enthält. 3.Zuweisen einer Suchanfrage (Abfragestring) zur Eigenschaft CommandText des Command-Objekts. Sie können entweder eine SQL-Syntax oder einen LDAP-Dialekt verwenden. Alle Beispiele in diesem Kapitel verwenden den LDAP-Dialekt. Weitere Informationen über die SQL-Syntax finden Sie unter dem Link Active Directory Programmer's Guide unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig). 4.Ausführen der Methode Execute des Command-Objekts, um die Abfrage auszuführen und die Ergebnisse in einem RecordSet-Objekt zu speichern. 5.Verwenden der Eigenschaften des RecordSet-Objekts, um Informationen aus dem Seite 274 von 394
Ergebnissatz zu lesen. 6.Verwenden der Methode Close des Connection-Objekts, um die Verbindung wieder zu schließen. Dieser letzte Schritt ist optional - Sie sollten ihn allerdings auf jeden Fall durchführen. Bei größeren Scripten sparen Sie so Ressourcen. 7.Erstellen eines ADO-Connection-Objekts und die Verwendung der Methode Open dieses Objekts, um über den ADSI-OLE DB-Provider auf Active Directory zuzugreifen. In Zeile 1 wird ein Connection-Objekt erstellt. In Zeile 2 wird das Connection-Objekt über den ADSI-OLE DB-Provider geöffnet (der Name - beziehungsweise die ProgID - des ADSI-OLE DB-Providers lautet ADsDSOObject). 8.Erstellen eines ADO-Command-Objekts und Zuweisen der Eigenschaft ActiveConnection zum Connection-Objekt (Zeilen 4 und 5). 9.Zuweisen des Abfragestrings zur Eigenschaft CommandText des Command-Objekts. Zeile 7 und 8 definiert die Suchbasis, das zurückzugebende Attribut und den Suchbereich. • •
•
Die Suchbasis - in eckigen Klammern ( ) eingeschlossen - definiert den Startpunkt der Suche. In diesem Fall ist dies der ADsPfad der Domäne na.fabrikam.com. Das zurückzugebende Attribut - direkt nach dem doppelten Semikolon - definiert den lDAPDisplayName der Attribute, die die Abfrage zurückgeben soll. In diesem Fall ist nur ein Attribut (name) angegeben.Wenn Sie mehrere Attribute angeben, trennen Sie diese mit Kommata. Der Suchbereich - am Ende des Abfragestrings - definiert, wo die Abfrage durchgeführt
10.Ausführen der Abfrage über die Methode Execute des Command-Objekts und Zuweisen des Rückgabewerts an das RecordSet-Objekt (Zeile 9). Der Abfragestring gibt Einträge mit einem einzelnen Feld (dem Feld name) zurück. 11.Verwenden einer While-Wend-Schleife, um alle Elemente des Objekts objRecordSet anzuzeigen. Um jeweils zum nächsten Element des RecordSet-Objekts zu gelangen, wird die Methode MoveNext verwendet. 12.Schließen des Connection-Objekts. Script 5.28: Suchen nach den Namen aller Objekt in der Domäne 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ 8 "
;;name;subtree" 9 Set objRecordSet = objCommand.Execute 10 11While Not objRecordSet.EOF 12 Wscript.Echo objRecordSet.Fields("name") 13 objRecordSet.MoveNext 14Wend 15
Seite 275 von 394
16objConnection.Close
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, gibt es die Namen aller Objekte der Domäne aus. Die Ausgabe sollte also ungefähr so aussehen: na Builtin Administrators Users ... S-1-5-11 Program Data Microsoft
Die vom Script zurück gelieferten Informationen sind jedoch aus den folgenden Gründen nur von eingeschränktem Nutzen. • • • •
Es werden die Namen aller Objekte in der Domäne na.fabrikam.com zurückgegeben. Es wird nicht deutlich, wo in der Domäne sich die einzelnen Objekte befinden. Es wird nicht deutlich, was für einen Typ die einzelnen Objekte haben. Der Ergebnissatz ist zu groß oder zu klein - je nach Ihren Anforderungen.
Damit die Ergebnisse brauchbarer werden, können Sie das Script folgendermaßen ändern: •
Sie ändern den Abfragestring - Über den Abfragestring können Sie die zurückzugebenden Attribute und die Suchbasis angeben. Außerdem können Sie Filter definieren und den Suchbereich festlegen.
•
Sie geben zusätzliche Suchoptionen über das Command-Objekt an - Über das Command-Objekt können Sie viele weitere Aspekte der Suche kontrollieren - zum Beispiel die Sortierung, die Menge der zurückgegebenen Ergebnisse oder die maximale Dauer der Suche.
Anmerkung: Eine komplette Liste der Suchoptionen und der anderen Eigenschaften von ADO finden Sie unter dem Link Active Directory Programmer's Guide unter http://www.microsoft.com/windows/reskits/webresources. Zurückgeben mehrerer Attribute über die Suche Script 5.28 hat nur einen einzelnen Wert zurückgegeben - das Attribut name. Mit weiteren Attributen wird das Ergebnis wahrscheinlich aussagekräftiger. Wenn Sie zum Beispiel das Attribut distinguishedName mit anzeigen, dann können Sie feststellen, wo sich das Objekt in Active Directory befindet. Script 5.29 gibt einen Ergebnissatz zurück, in dem die Attribute name und distinguishedName aller Objekte der Domäne na.fabrikam.com enthalten sind. Das Script arbeitet bis auf die folgenden zwei Änderungen genauso wie das vorhergehende Script: • •
Das Attribut distinguishedName wurde in den Abfragestring aufgenommen (Zeile 8). Der Wert des Attributs distinguishedName wird in der Kommandozeile ausgegeben (Zeilen 14 und 15).
Script 5.29: Suchen nach den Namen und DNs aller Objekte eine Domäne Seite 276 von 394
1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ 8 ";;distinguishedName,name;subtree" 9 10Set objRecordSet = objCommand.Execute 11 12While Not objRecordSet.EOF 13 Wscript.Echo objRecordSet.Fields("Name") 14 Wscript.Echo "[" & _ 15 objRecordSet.Fields("distinguishedName") & "]" 16 objRecordSet.MoveNext 17Wend 18 19objConnection.Close
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, dann gibt es die Namen und den DN aller Objekte in der Domäne aus. Die Ausgabe sollte ungefähr so aussehen: na [dc=NA,DC=fabrikam,DC=com] Builtin [CN=Builtin,dc=NA,DC=fabrikam,DC=com] Administrators [CN=Administrators,CN=Builtin,dc=NA,DC=fabrikam,DC=com] Users [CN=Users,CN=Builtin,dc=NA,DC=fabrikam,DC=com] ... S-1-5-11 [CN=S-1-5-11,CN=ForeignSecurityPrincipals,dc=NA,DC=fabrikam,DC=com] Program Data [CN=Program Data,dc=NA,DC=fabrikam,DC=com] Microsoft [CN=Microsoft,CN=Program Data,dc=NA,DC=fabrikam,DC=com]
Die Suche auf einen bestimmten Objekttyp einschränken Wenn Sie die Suche einschränken möchten, dann sollten Sie die Attribute objectClass oder objectCategory verwenden. Jedes Active Directory-Objekt verfügt über ein Einzelwert-Attribut mit dem Namen objectCategory. In diesem Attribut ist der DN der Klasse hinterlegt, von der das Objekt abgeleitet wurde. Das Attribut objectCategory einer Gruppe hat zum Beispiel den Wert cn=Group,cn=Schema,cn=Configuration,dc=fabrikam,dc=com. Um mit dem Ergebnissatz nur Gruppenobjekte zurückzuerhalten, geben Sie die Filterbedingung (objectCategory=Group) im Filter-Teil des Suchstrings mit an. Außerdem verfügt jedes Active Directory-Objekt über ein Multiwert-Attribut mit dem Namen objectClass. Dieses Attribut enthält eine sortierte Liste der gesamten Klassenhierarchie, von der das Active Directory-Objekt abgeleitet wurde. Um zum Beispiel die Abfrage auf Computer- und Benutzerobjekte einzuschränken, können Sie den Filter objectClass=user verwenden. Warum auch Computerobjekte? In der Active Directory-Hierarchie ist die Klasse Computer eine Unterklasse der Klasse user.
Seite 277 von 394
Da das Attribut objectCategory nur ein Einzelwert-Attribut ist, ist es besser für Suchanfragen geeignet. Wenn möglich, sollten Sie also dieses Attribut verwenden. Anmerkung: Informationen zu Active Directory-Klassen finden Sie im Abschnitt Die Active Directory-Architektur weiter unten in diesem Kapitel. Script 5.30 demonstriert, wie Sie einen Ergebnissatz auf eine bestimmte Kategorie (Computer) einschränken können. Das Script gibt die Attribute name und distinguishedName aller Computerobjekte der Domäne na.fabrikam.com zurück. Das Script arbeitet genau wie die beiden vorherigen Scripte. Als einzige Änderung wurde der Filter objectCategory=computer zum Abfragestring hinzugefügt. Script 5.30: Suchen nach den Namen und den DNs aller Computerobjekt in der Domäne 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ 8 ";(objectCategory=computer)" & _ ";distinguishedName,name;subtree" 9 10 11Set objRecordSet = objCommand.Execute 12 13While Not objRecordSet.EOF Wscript.Echo objRecordSet.Fields("Name") 14 15 Wscript.Echo "[" & _ objRecordSet.Fields("distinguishedName") & "]" 16 objRecordSet.MoveNext 17 18Wend 19 20objConnection.Close
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, gibt es die Namen und die DNs aller Computerobjekte in der Kommandozeile aus. Diese Ausgabe sieht ungefähr so aus: SEA-DC-02 [CN=SEA-DC-02,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] SEA-DC-03 [CN=SEA-DC-02,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] ... SEA-PM-01 [CN=SEA-PM-01,cn=Computers,dc=NA,DC=fabrikam,DC=com] SEA-SQL-01 [CN=SEA-SQL-01,cn=Computers,dc=NA,DC=fabrikam,DC=com]
Filter können auch kombiniert werden - außerdem werden Wildcards unterstützt. Weitere Informationen finden Sie unter dem Link Active Directory Programmer's Guide unter http://www.microsoft.com/windows/reskits/webresources. Suchen Sie hier nach dem Text "Search Filter Syntax." Angeben des globalen Katalog-Servers in der Suchbasis Nehmen wir einmal an, Sie möchten die Namen und die DNs aller Computer der Gesamtstruktur abfragen. Hierzu können Sie den globalen Katalog-Server der Stammdomäne abfragen - dieser speichert unter anderem die beiden gesuchten Attribute. Ein globaler Seite 278 von 394
Katalog-Server speichert Teile der Informationen aus allen Domänen. Um den globalen Katalog-Server abzufragen, ändern Sie den LDAP-Moniker in der Suchbasis auf GC und ändern Sie den DN auf den der Stammdomäne. Sie ändern also den ADsPfad von
auf
Script 5.31 demonstriert diesen Vorgang. Es arbeitet genau wie die vorherigen Scripte - nur der Suchstring wurde geändert (Zeile 8). Script 5.31: Suchen nach den Namen und DNs aller Computerobjekte in der Gesamtstruktur 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ 8 ";(objectCategory=computer)" & _ ";distinguishedName,name;subtree" 9 10 11Set objRecordSet = objCommand.Execute 12 13While Not objRecordSet.EOF Wscript.Echo objRecordSet.Fields("Name") 14 15 Wscript.Echo "[" & _ objRecordSet.Fields("distinguishedName") & "]" 16 objRecordSet.MoveNext 17 18Wend 19 20objConnection.Close
Wenn Sie das Script in der Stammdomäne fabrikam.com ausführen, gibt es die Namen und die DNs aller Computerobjekte der Gesamtstruktur aus. Diese Ausgabe sieht ungefähr so aus: SEA-DC-01 [CN=SEA-DC-01,OU=Domain Controllers,DC=fabrikam,DC=com] SEA-DC-04 [CN=SEA-DC-04,OU=Domain Controllers,DC=fabrikam,DC=com] SEA-DC-02 [CN=SEA-DC-02,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] SEA-DC-03 [CN=SEA-DC-03,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] ... SEA-PM-01 [CN=SEA-PM-01,cn=Computers,dc=NA,DC=fabrikam,DC=com] SEA-SQL-01 [CN=SEA-SQL-01,cn=Computers,dc=NA,DC=fabrikam,DC=com]
Den Ergebnissatz über die Nachverfolgung erweitern Wenn ein Domänencontroller einer übergeordneten Domäne (zum Beispiel der Stammdomäne) einen Ergebnissatz zusammenstellt, dann gibt er eine Liste der untergeordneten Domänen an den Clientcomputer, der das Script ausführt. Der Clientcomputer kontaktiert dann jede der untergeordneten Domänen, so dass diese den Ergebnissatz vervollständigen können. Dieser Prozess heißt Nachverfolgung.
Seite 279 von 394
Die Nachverfolgung erhöht den Netzwerkverkehr und die Last auf den Domänencontrollern. Und zwar deshalb, weil ja nicht nur der globale Katalog-Server, sondern auch die Domänencontroller abgefragt werden. Sie können die Nachverfolgung explizit aktivieren, indem Sie die Eigenschaft Chase Referrals auf den Wert &h20 setzen - Script 5.32 demonstriert dies. Es arbeitet (abgesehen von vier kleinen Änderungen) genau so wie die vorhergehenden Scripte dieses Abschnitts. Bei den Änderungen handelt es sich um die folgenden: • •
•
•
Die Konstante ADS_CHASE_REFERRALS_SUBORDINATE wird mit dem Wert &h20 definiert (Zeile 1). Die Nachverfolgung wird aktiviert, indem die Eigenschaft Chase Referrals des CommandObjekts auf den Wert der Konstante ADS_CHASE_REFERRALS_SUBORDINATE gesetzt wird (Zeilen 8 und 9). Das Attribut isCriticalSystemObject wird mit in den Abfragestring aufgenommen. Dieses Attribut wird nicht im globalen Katalog-Server gespeichert und muss daher über die Nachverfolgung von den untergeordneten Domänen abgefragt werden (Zeile 13). Der Wert des Attributs isCriticalSystemObject wird zusätzlich ausgegeben (Zeilen 19 und 20).
Script 5.32: Verwenden der Nachverfolgung bei einer Suche 1 ADS_CHASE_REFERRALS_SUBORDINATE = &h20 2 Set objConnection = CreateObject("ADODB.Connection") 3 objConnection.Open "Provider=ADsDSOObject;" 4 5 Set objCommand = CreateObject("ADODB.Command") 6 objCommand.ActiveConnection = objConnection 7 8 objCommand.Properties("Chase Referrals") = _ 9 ADS_CHASE_REFERRALS_SUBORDINATE 10 11objCommand.CommandText = _ 12 ";(objectCategory=computer);" & _ 13 "distinguishedName,name,isCriticalSystemObject;subtree" 14 15Set objRecordSet = objCommand.Execute 16 17While Not objRecordSet.EOF 18 Wscript.Echo objRecordSet.Fields("Name") 19 Wscript.Echo "isCriticalSystemObject: " & _ 20 objRecordSet.Fields("isCriticalSystemObject") Wscript.Echo "[" & _ 21 objRecordSet.Fields("distinguishedName") & "]" 22 23 objRecordSet.MoveNext 24Wend 25 26objConnection.Close
Wenn Sie das Script in der Stammdomäne (fabrikam.com) ausführen, gibt es die Namen, den DN und den Boolean-Wert von isCriticalSystemObject jedes Computerobjekts in der Gesamtstruktur aus. Die Ausgabe könnte zum Beispiel so aussehen: SEA-DC-01 isCriticalSystemObject: True
Seite 280 von 394
[CN=SEA-DC-01,OU=Domain Controllers,DC=fabrikam,DC=com] SEA-DC-04 isCriticalSystemObject: True [CN=SEA-DC-04,OU=Domain Controllers,DC=fabrikam,DC=com] SEA-DC-02 isCriticalSystemObject: True [CN=SEA-DC-02,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] SEA-DC-03 isCriticalSystemObject: True [CN=SEA-DC-03,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] ... SEA-PM-01 isCriticalSystemObject: False [CN=SEA-PM-01,cn=Computers,dc=NA,DC=fabrikam,DC=com] SEA-SQL-01 isCriticalSystemObject: False [CN=SEA-SQL-01,cn=Computers,dc=NA,DC=fabrikam,DC=com]
Den Suchbereich definieren Stellen Sie sich vor, Sie möchten nur die Computer im Container Computers einer bestimmten Domäne abfragen. Nehmen wir außerdem einmal an, der Container enthält noch weitere Untercontainer. Um eine Suche so einzuschränken, können Sie den DN im ADsPfad (die Suchbasis) folgendermaßen ändern: cn=Computers,dc=NA,dc=fabrikam,dc=com
Ein möglicher Abfragestring könnte zum Beispiel so aussehen: "" & _ ";(objectCategory=computer)" & _ ";distinguishedName,name;subtree"
Mit diesem Abfragestring werden die Attribute DN und name aller Computerobjekte im Container Computers der Domäne na.fabrikam.com abgefragt. Da der Abfragestring mit dem Wert subtree endet (der Suchbereich), werden auch die untergeordneten Container durchsucht. Die möglichen Werte für den letzten Teil des Abfragestrings (den Suchbereich) sind: •
Base - Sucht nur an der durch den DN definierten Stelle.
•
Onelevel - Sucht auch in allen unmittelbaren Untercontainern.
•
Subtree - Sucht in den unmittelbaren Untercontainern und in allen weiteren Untercontainern. Wenn Sie den Bereich nicht angeben, wird dieser Wert standardmäßig verwendet.
Script 5.33 demonstriert eine Suche ausschließlich im durch den DN angegebenen Container. Um die Suche auf die gewünschten Objekte einzuschränken, geht das Script folgendermaßen vor: 1.Es gibt den Container Computers der Domäne na.fabrikam.com als Suchbasis an. 2.Es gibt als Suchbereich onelevel an. Script 5.33: Einschränken der Suche durch den Bereich 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command")
Seite 281 von 394
5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ 8 "" & _ 9 ";(objectCategory=computer)" & _ 10 ";distinguishedName,name;onelevel" 11 12Set objRecordSet = objCommand.Execute 13 14While Not objRecordSet.EOF 15 Wscript.Echo objRecordSet.Fields("Name") 16 Wscript.Echo "[" & _ 17 objRecordSet.Fields("distinguishedName") & "]" 18 objRecordSet.MoveNext 19Wend 20 21objConnection.Close
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, gibt es die Attribute DN und name aller Computerobjekte im Container Computers der Domäne aus. Die Ausgabe könnte zum Beispiel so aussehen: SEA-PM-01 [CN=SEA-PM-01,cn=Computers,dc=NA,DC=fabrikam,DC=com] SEA-SQL-01 [CN=SEA-SQL-01,cn=Computers,dc=NA,DC=fabrikam,DC=com] ...
Die Ergebnisse der Suche sortieren Eine Sortierung können Sie über die Eigenschaft Sort On des Command-Objekts durchführen. Die Sortierung wird dann bereits auf dem Server ausgeführt - und zwar bevor er die Daten an den Clientcomputer zurückgibt, der das Script ausführt. Der Eigenschaft Sort On weisen Sie den lDAPDisplayName eines Attributs zu. Nach diesem Attribut wird die Sortierung vorgenommen. Anmerkung: Eine Sortierung nach einem Attribut, dass einen DN speichert ist, nicht möglich. Wenn Sie dies versuchen, erhalten Sie einen leeren Ergebnissatz. Script 5.34 demonstriert die Sortierung des Ergebnissatzes. Es fragt die Attribute name, distinguishedName und whenCreated aller Computerobjekte der Gesamtstruktur ab und sortiert die Ergebnisliste nach dem Attribut whenCreated. Die folgenden Änderungen wurden gegenüber den bisherigen Scripten dieses Abschnitts vorgenommen: • •
Der Eigenschaft Sort On wird der Wert whenCreated zugewiesen (Zeile 7). Das Attribute whenCreated wird mit in den Abfragestring aufgenommen (Zeile 11). Beachten Sie, dass das Script den Moniker GC verwendet. Dies funktioniert nur deshalb, weil die abgefragten drei Attribute alle vom globalen Katalog-Server gespeichert werden.
Script 5.34: Sortieren der Computerobjekte nach dem Erstellungsdatum 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;"
Seite 282 von 394
3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.Properties("Sort On") = "whenCreated" 8 9 objCommand.CommandText = _ 10 ";(objectCategory=Computer);" & _ 11 "distinguishedName,name,whenCreated;subtree" 12 13Set objRecordSet = objCommand.Execute 14 15While Not objRecordSet.EOF 16 Wscript.Echo objRecordSet.Fields("Name") 17 Wscript.Echo "[" & _ 18 objRecordSet.Fields("distinguishedName") & "]" 19 Wscript.Echo objRecordSet.Fields("whenCreated") & VbCrLf 20 objRecordSet.MoveNext 21Wend 22 23objConnection.Close
Wenn das Script in der Domäne na.fabrikam.com ausgeführt wird, dann gibt es die Attribute name, DN und whenCreated aller Computerobjekte der Domäne aus. Der Ergebnissatz ist nach dem Attribut whenCreated sortiert (vom ältesten bis zum jüngsten). Die Ausgabe könnte folgendermaßen aussehen: SEA-DC-01 [CN=SEA-DC-01,OU=Domain Controllers,DC=fabrikam,DC=com] 8/14/2002 9:59:12 AM SEA-DC-02 [CN=SEA-DC-02,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] 8/21/2002 1:53:24 PM SEA-DC-03 [CN=SEA-DC-03,OU=Domain Controllers,dc=NA,DC=fabrikam,DC=com] 8/27/2002 9:53:24 AM SEA-PM-01 [CN=SEA-PM-01,cn=Computers,dc=NA,DC=fabrikam,DC=com] 8/27/2002 11:53:24 AM ... SEA-SQL-01 [CN=SEA-SQL-01,cn=Computers,dc=NA,DC=fabrikam,DC=com] 8/27/2002 4:06:30 PM SEA-DC-04 [CN=SEA-DC-04,OU=Domain Controllers,DC=fabrikam,DC=com] 9/03/2002 2:00:03 PM
Multiwert-Attribute über eine Suche abfragen Einige Active Directory-Objekte stellen Multiwert-Attribute zur Verfügung. Auch diese Attribute können Sie über den Ergebnissatz zurückgeben lassen (zum Beispiel das Attribut member eines Gruppenobjekts). Script 5.35 gibt einen solchen Ergebnissatz zurück. Es fragt die Einzelwert-Attribute name und distinguishedName und das Multiwert-Attribut member aller Gruppen in der Domäne Seite 283 von 394
na.fabrikam.com ab. Hierzu wurden die folgenden Änderungen an dem vorherigen Scripten vorgenommen: 1.Im Abfragestring wird die Ergebnismenge über den Filter objectCategory=Group auf Gruppenobjekte eingeschränkt. Außerdem wird das Multiwert-Attribut member mit aufgenommen (Zeile 9). Da in diesem Script die Domäne na.fabrikam.com und der Moniker GC verwendet wird, muss sich in der Domäne auch ein globaler Katalog-Server befinden. Wenn es keinen globalen Katalog-Server gibt, dann müssen Sie den Moniker LDAP verwenden. 2.Es wird eine Variable mit dem Namen arrMembers mit den Inhalten des Attributs member initialisiert (diese stammen aus der Eigenschaft fields des RecordSet-Objekts (Zeile 19). 3.Mit der VBScript-Funktion IsArray wird geprüft, ob das Attribut member ein Array ist (Zeile 22). Wenn das Ergebnis dieser Prüfung False ist, dann ist das Attribut leer. • •
Wenn der Rückgabewert von IsArray den Wert True hat, dann wird eine For-Each-Schleife verwendet, um die Mitglieder der Gruppe auszugeben (Zeilen 23-25). Wenn der Rückgabewert von IsArrayFalse ist, dann wird das Wort Keine ausgegeben (Zeile 27).
Script 5.35: Suchen nach dem Namen und DNs aller Objekte der Domäne 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ ";(objectCategory=Group);" & _ 8 "distinguishedName,name,member;subtree" 9 10 11Set objRecordSet = objCommand.Execute 12 13While Not objRecordSet.EOF 14 Wscript.Echo objRecordSet.Fields("Name") 15 Wscript.Echo "[" & _ objRecordSet.Fields("distinguishedName") & "]" 16 17 Wscript.Echo "Group Member(s):" 18 19 arrMembers = objRecordSet.Fields("member") 20 21 If IsArray(objRecordSet.Fields("member")) Then For Each strMember in arrMembers 22 Wscript.Echo vbTab & strMember 23 Next 24 25 Else 26 Wscript.Echo vbTab & "Keine" 27 End If 28 Wscript.Echo VbCrLf 29 objRecordSet.MoveNext 30Wend 31 32objConnection.Close
Seite 284 von 394
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, gibt es den Namen, den DN und die Mitglieder aller Gruppenobjekte der Domäne aus: Administrators [CN=Administrators,CN=Builtin,DC=na,DC=fabrikam,DC=com] Group Member(s): CN=Enterprise Admins,CN=Users,DC=fabrikam,DC=com CN=Domain Admins,CN=Users,DC=na,DC=fabrikam,DC=com CN=Administrator,CN=Users,DC=na,DC=fabrikam,DC=com Users [CN=Users,CN=Builtin,DC=na,DC=fabrikam,DC=com] Group Member(s): CN=Domain Users,CN=Users,DC=na,DC=fabrikam,DC=com CN=S-1-5-11,CN=ForeignSecurityPrincipals,DC=na,DC=fabrikam,DC=com CN=S-1-5-4,CN=ForeignSecurityPrincipals,DC=na,DC=fabrikam,DC=com ... Pre-Windows 2000 Compatible Access [CN=Pre-Windows 2000 Compatible Access,CN=Builtin,DC=na,DC=fabrikam,DC=com] Group Member(s): None Atl-Users [CN=Atl-Users,CN=Users,DC=na,DC=fabrikam,DC=com] Group Member(s): CN=LewJudy,OU=Sales,DC=na,DC=fabrikam,DC=com CN=HuffArlene,OU=HR,DC=na,DC=fabrikam,DC=com CN=MyerKen,OU=HR,DC=na,DC=fabrikam,DC=com
Teile von Multiwert-Attributen über die Eigenschaft Range abfragen Wenn ein Multiwert-Attribut viele Einträge hat, dann können Sie über die Eigenschaft Range nur Teile dieser Einträge abfragen. Die Last auf dem abgefragten Domänencontroller wird so geringer - daher wird auch die Abfrage schneller. Wenn ein Multiwert-Attribut mehr als 1.000 Einträge hat, dann müssen Sie die Abfrage auf Teile der Einträge beschränken. Ansonsten kann es sein, dass die Abfrage nicht korrekt durchgeführt wird. Script 5.36 gibt einen Ergebnissatz mit den ersten 1.000 Einträgen des Multiwert-Attributs member der Gruppe Atl-Users in der Domäne na.fabrikam.com zurück. Anmerkung: Wenn es weniger als 1.000 Mitglieder in der Gruppe gibt, schlägt das Script fehl. Es muss bei jeder Abfrage mehr Einträge geben, als in der Eigenschaft Range definiert sind (in diesem Fall 999). Um die Anzahl der Einträge einzugrenzen, geht das Script folgendermaßen vor: 1.Das Schlüsselwort Range wurde mit in den Abfragestring aufgenommen (Zeile 9). Mit dem Wert 0-999 werden die ersten 1.000 Einträge des Attributs member der Gruppe zurückgegeben. 2.Es gibt den Text "Die ersten 1000 Mitglieder:" aus (Zeile 13). 3.Es initialisiert eine Variable mit den Namen arrMembers mit den Inhalten des Attributs member über die Eigenschaft fields des RecordSet-Objekts. Auch für die Eigenschaft Fields muss der Wert Range angegeben werden - und zwar exakt so, wie sie im Abfragestring angegeben wurde. Seite 285 von 394
Script 5.36: Einschränken der Einträge in Multiwert-Attributen über die Eigenschaft Range 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ 8 ";;" & _ 9 "member;Range=0-999;base" 10 11Set objRecordSet = objCommand.Execute 12 13Wscript.Echo "First 1000 Member(s):" 14arrMembers = objRecordSet.Fields("member;Range=0-999") 15 16For Each strMember in arrMembers 17 Wscript.Echo vbTab & strMember 18Next 19 20objConnection.Close
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, gibt es die ersten 1.000 Mitglieder der Gruppe Atl-Users aus: First 1000 Member(s): CN=UserNo988,CN=Users,DC=na,DC=fabrikam,DC=com CN=UserNo987,CN=Users,DC=na,DC=fabrikam,DC=com CN=UserNo986,CN=Users,DC=na,DC=fabrikam,DC=com ... CN=HuffArlene,OU=HR,DC=na,DC=fabrikam,DC=com CN=MyerKen,OU=HR,DC=na,DC=fabrikam,DC=com
Aus den Scripten in diesem Abschnitt können wir die folgenden wichtigen Erkenntnisse gewinnen: •
Sie führen alle die gleichen grundlegenden Schritte aus:
1.Erstellen eines ADO-Connection-Objekts unter Verwendung des ADSI-OLE DB-Providers. 2.Erstellen eines ADO-Command-Objekts und Verbinden dieses Objekts mit dem Connection-Objekt. 3.Verwenden des Command-Objekts, um einen Abfragestring zu erstellen. 4.Erstellen eines RecordSet-Objekts, um die Abfrage auszuführen und den Ergebnissatz zu speichern. 5.Verwenden des RecordSet-Objekts, um die Elemente des RecordSet auszulesen. 6.Schließen des Connection-Objekts. •
•
Mit Ausnahme von Script 5.36 werden die Einträge des RecordSet-Objekts in einer WhileWend-Schleife ausgegeben. In Script 5.36 ist dieser Schritt nicht notwendig, da es nur einen einzelnen Eintrag zurückgibt. Das Zurückgeben eines Ergebnissatzes ist der grundlegende Zweck der Suche. Andere Aufgaben, die Sie über eine Suche durchführen können, finden Sie im Abschnitt Seite 286 von 394
Administrative Aufgaben über einen Ergebnissatz durchführen weiter unten in diesem Kapitel.
Optimieren der Suchleistung Um eine Suche optimieren zu können, sind Kenntnisse über Active Directory notwendig. Auch von der Verwendung der Objekte in Ihrem Script wird die Suchleistung beeinflusst. Konsolidierung von Abfragestrings Wenn Sie ein Script schreiben, in dem viele Suchoperationen durchgeführt werden, dann überlegen Sie, wie Sie die Suchen zusammenfassen können. Schreiben Sie zum Beispiel einen Abfragestring, der mehrere Attribute zurückgibt, statt zwei oder mehr separate Suchen zu verwenden. Einschränken des Ergebnissatzes Schränken Sie den Bereich Ihrer Suche so weit wie möglich ein. Wenn Sie zum Beispiel einen Ergebnissatz mit allen Objekten einer OU benötigen, dann geben Sie als Suchbasis diesen Container an. Wenn Sie die Objekte in dem Untercontainer nicht benötigen, dann verwenden Sie zusätzlich die Eigenschaft onelevel. Verwenden Sie Suchfilter. Geben Sie zum Beispiel die Eigenschaft objectCategory=class type an - class type ist in diesem Fall der Typ des zu suchenden Objekts. Verwenden Sie eher objectCategory statt objectClass. Bei objectCategory handelt es sich um einen Einzelwert, der für Suchanfragen besser geeignet ist. Im Gegensatz zu objectClass wird objectCategory auf den globalen Katalog-Servern gespeichert und indiziert. Geben Sie Filter an (zum Beispiel cn=SEA*, der den Ergebnissatz auf Objekt beschränkt, die mit den Buchstaben SEA anfangen). Wenn Sie das Wildcard-Zeichen * verwenden, dann jedoch nur am Ende - ansonsten erzeugen Sie noch mehr Last auf dem Domänencontroller der die Anfrage verarbeitet. Schränken Sie die Suche noch weiter ein, indem Sie Filter kombinieren. Der folgende Filter schränkt die Suche zum Beispiel auf alle Computerobjekte ein, die mit SEA anfangen: (&(objectCategory=computer)(cn=SEA*))
Über die Eigenschaft Range können Sie die Zahl der zurückgegebenen Einträge von Multiwert-Attributen einschränken. Zusätzliche Eigenschaften des Command-Objekts Es gibt eine Menge Eigenschaften des Command-Objekst, die die Suchoperation beeinflussen. Diese Eigenschaften sind besonders im Umgang mit großen Ergebnisätzen sehr nützlich. In Tabelle 5.4 sehen Sie einige dieser Optionen. Tabelle 5.4: Optionen zur Verbesserung der Leistung bei großen Ergebnissätzen Option
Beschreibung
StandardwertSyntax
Page Size (paging)
Weist den Domänencontroller an, Deaktiviert eine bestimmte Anzahl von Einträgen zu verarbeiten und diese dann vor der weiteren Verarbeitung an den Client zurückzugeben.
objCommand.Properties _ ("Page Size")= Records Records ist die Anzahl der zu erzeugenden Einträge. Seite 287 von 394
Option
Beschreibung
Size Limit
Gibt die Größe des Ergebnissatzes 1.000 an. Wenn der Domänencontroller diese Grenze erreicht, wird der Ergebnissatz sofort an den Client zurückgegeben und die Suche ist beendet.
Time Limit
Timeout
StandardwertSyntax
Gibt die maximale Dauer der Keiner Suche an. Nach diesem Zeitraum gibt der Domänencontroller einen Ergebnissatz zurück - auch dann, wenn dieser noch nicht alle Ergebnisse enthält. Gibt den Zeitraum an, den der Keiner Client auf einen Ergebnissatz wartet, bevor er die Suche abbricht.
Cache Results Gibt an, ob der Ergebnissatz auf True (caching) dem Client zwischengespeichert werden soll. Bei sehr großen Ergebnissätzen kann der Speicherverbrauch auf dem Client durch das Deaktivieren des Zwischenspeicherns verringert werden. Asynchronous Gibt an, ob der Domänencontroller False die Einträge im Ergebnissatz einzeln (asynchron) oder komplett nach der Suche (synchron) senden soll.
objCommand.Properties _ ("Size Limit")= Records Records ist die Anzahl der Einträge, bei der der Domänencontroller die Suche beendet. objCommand.Properties _ ("Time Limit")= Time Time ist der maximale Zeitraum (in Sekunden), nach dem der Ergebnissatz zurückgegeben wird. objCommand.Properties _ ("Timeout")= Time Timeout ist der Zeitraum (in Sekunden) den der Client auf den Ergebnissatz wartet. objCommand.Properties _ ("Cache Results"= Boolean Wenn der Wert auf False gesetzt wird, ist das Zwischenspeichern deaktiviert.
objCommand.Properties _ ("Asynchronous"= Boolean Wenn der Wert auf True gesetzt wird, dann sendet der Domänencontroller die Einträge asynchron.
Verwenden des globalen Katalog-Servers für Suchoperationen Wenn alle Attribute einer Suchoperation auf dem globalen Katalog-Server gespeichert werden, dann verwenden Sie das Prefix GC statt LDAP. Dies ist ganz besonders wichtig, wenn Sie Ergebnisse aus mehr als einer Domäne abfragen. Wenn Sie den globalen KatalogServer verwenden, muss nur dieser die Anfrage bearbeiten - andernfalls werden möglicherweise sehr viele Domänencontroller mit in die Suche einbezogen. Wenn Sie den globalen Katalog-Server nicht verwenden, dann müssen Sie die Option Nachverfolgen zur Abfrage von Objekten aus mehreren Domänen verwenden. Sie sollten dies jedoch wenn möglich vermeiden.
Seite 288 von 394
Wenn Sie einen Ergebnissatz sortieren müssen, dann sortieren Sie nach Attributen, die indiziert und auf dem globalen Katalog-Server gespeichert sind. Informationen dazu, wie Sie feststellen, ob ein Attribut indiziert und auf dem globalen Katalog-Server gespeichert ist, finden Sie im Abschnitt Die Active Directory-Archiektur weiter unten in diesem Kapitel. Weniger Objekte verwenden Erstellen Sie nur ein Connection-Objekt und verwenden Sie dies im gesamten Script (dies gilt auch für alle anderen Objekte).
Administrative Aufgaben über einen Ergebnissatz ausführen Mit dem ADSI-OLE DB-Provider ist nur ein Lesezugriff auf Active Directory möglich - Sie können also keine Objekte direkt ändern. Sie können jedoch über den Ergebnissatz und weitere ADSI-Methoden Änderungen durchführen. Beispiele hierfür sind: • • •
Nach dem Attribut sAMAccountName eines Objekts suchen. Wenn der Ergebnissatz leer ist, dann können Sie das Objekt gefahrlos über die Methode Create erstellen. Über das Attribut objectCategory nach allen Computerobjekten suchen und die Objekte dann über die Methode Put ändern. Nach den Objekten suchen, bei denen das Attribut description zeigt, dass sie zu einer bestimmten Abteilung gehören. Diese Objekte können Sie dann zum Beispiel zur Konsolidierung in einen bestimmten Container verschieben.
Die beiden Scripte in diesem Abschnitt demonstrieren solche Vorgänge. Ein Attribut bei vielen Objekten ändern Script 5.37 ändert das Attribut location aller Computer der Domäne, deren Name mit ATL beginnt, auf den Wert Atlanta, Georgia. Die hierzu erforderlichen Schritte sind: 1.Verwenden einer ADS-Abfrage, die alle Computerobjekte zurückgibt, deren Name mit ATL anfängt. •
•
In Zeile 9 werden zwei Filter kombiniert: objectCategory=Computer und cn=ATL*. Der zweite Filter verwendet ein Wildcard-Zeichen (*), um alle Computer zu finden, deren Name mit ATL beginnt. In Zeile 10 wird das Attribut ADsPath jedes Computers im Ergebnissatz zurückgegeben.
1.Verwenden einer While-Wend-Schleife und der Methode MoveNext, um die einzelnen Einträge des RecordSet-Objekts durchzugehen. •
•
Für jeden Eintrag im Ergebnissatz wird eine Bindung an das entsprechende Active DirectoryObjekt aufgebaut. Dann wird das Attribut location geändert und die Änderung wird an Active Directory übermittelt (Zeilen 15-18). Die Anzahl der geänderten Einträge wird über die Eigenschaft RecordCount des RecordSetObjekts ausgegeben (Zeilen 21 und 22).
Script 5.37: Ändern eines Attributs von mehreren Computerobjekten 1 Set objConnection = CreateObject("ADODB.Connection")
Seite 289 von 394
2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ 8 ";" & _ 9 "(&(objectCategory=Computer)(cn=ATL*));" & _ 10 "ADsPfad;subtree" 11 12Set objRecordSet = objCommand.Execute 13 14While Not objRecordSet.EOF 15 strADsPfad = objRecordSet.Fields("ADsPfad") 16 Set objComputer = GetObject(strADsPfad) 17 objComputer.Put "location", "Atlanta, Georgia" 18 objComputer.SetInfo 19 objRecordSet.MoveNext 20Wend 21Wscript.Echo objRecordSet.RecordCount & " computers objects modified." 22 23objConnection.Close
Verschieben von Objekten, abhängig vom Wert eines bestimmten Attributs Script 5.38 verschiebt die Benutzerobjekte in die OU HR, derenAttribut department den Wert Human Resources hat. Die hierzu erforderlichen Schritte sind: 1.Verwenden einer ADO-Abfrage für alle Benutzerkonten, deren Attribut department den Wert Human Resources hat. •
•
In Zeile 9 und 10 werden drei Filter kombiniert: objectCategory=person, objectClass=user und department=Human Resources. Beachten Sie die hierbei verwendeten Klammern und die kaufmännischen Und-Zeichen (&). Das Script verwendet die Eigenschaften objectCategory und objectClass - so werden alle Benutzerkonten zurückgegeben, die Sicherheitsprinzipale sind. Weitere Informationen über diese Filterkombination finden Sie im Abschnitt Active Directory-Benutzer in diesem Buch. In Zeile 10 werden die Attribute ADsPath, distinguishedName und name aller Benutzerkonten zurückgegeben, die mit den Filterbedingungen übereinstimmen.
1.Es wird eine Bindung zur Ziel-OU für die Verschieben-Operation aufgebaut (Zeile 14). Diese Bindung könnte auch für jedes zu verschiebende Objekt in der While-Wend-Schleife neu aufgebaut werden. Es ist jedoch deutlich effizienter, die Bindung nur einmal vor der Schleife aufzubauen und diese dann in der Schleife mehrmals zu verwenden. 2.Es wird eine While-Wend-Schleife verwendet, um die Einträge im RecordSet-Objekt zu lesen (Zeile 16). • •
Die Variable strADsPath wird über die Eigenschaft field mit dem ADsPath initialisiert. Es werden zwei weitere Variablen initialisiert: strDNRecord und strDNCompare. strDNRecord speichert den Wert aus dem Attribut distinguishedName, und strDNCompare speichert den Wert distinguishedName (wird aus dem Attribut DN und dem Pfad zur OU HR zusammengesetzt). Über die Variable strDNCompare wird festgestellt, ob sich das Benutzerkonto aus strDNRecord im Moment in der OU HR befindet. Seite 290 von 394
•
Wenn das Benutzerkonto noch nicht in der OU ist (wenn also strDNRecord nicht gleich strDNCompare ist), dann wird die Methode MoveHere verwendet, um das Konto in die OU zu verschieben. Als Letztes wird das Attribut distinguishedName des Benutzerkontos ausgegeben.
Script 5.38: Verschieben von mehreren Benutzerkonten mit Hilfe des Ergebnissatzes einer Suche 1 Set objConnection = CreateObject("ADODB.Connection") 2 objConnection.Open "Provider=ADsDSOObject;" 3 4 Set objCommand = CreateObject("ADODB.Command") 5 objCommand.ActiveConnection = objConnection 6 7 objCommand.CommandText = _ ";" & _ 8 "(&(&(objectCategory=person)(objectClass=user)" & _ 9 "(department=Human Resources)));" & _ 10 "ADsPath,distinguishedName,name;subtree" 11 12 13Set objRecordSet = objCommand.Execute 14 15Set objOU = GetObject("LDAP://ou=HR,dc=NA,dc=fabrikam,dc=com") 16 17While Not objRecordSet.EOF strADsPath = objRecordSet.Fields("ADsPath") 18 19 strDNRecord=LCase(objRecordSet.Fields("distinguishedName")) 20 strDNCompare=LCase("cn=" & objRecordSet.Fields("name") & _ 21 ",ou=HR,dc=NA,dc=fabrikam,dc=com") 22 23 If strDNRecord <> strDNCompare Then 24 objOU.MoveHere strADsPath, vbNullString 25 Wscript.Echo objRecordSet.Fields("distinguishedName") & " Moved." 26 27 Else 28 Wscript.Echo objRecordSet.Fields("distinguishedName") & " Not 29Moved." 30 End If 31 objRecordSet.MoveNext 32Wend 33 objConnection.Close
Aus den Scripten dieses Abschnitts können wir die folgenden wichtigen Erkenntnisse gewinnen: •
•
Beide Scripte führen die gleichen grundlegenden Schritte durch: Sie verwenden ADO, um ein Connection-, Command- und ein RecordSet-Objekt zu erstellen. Dann lesen Sie die einzelnen Einträge im RecordSet-Objekt aus. Sie verwenden die Elemente aus dem Ergebnissatz, um administrative Aufgaben durchzuführen.
Auflisten der Active Directory-Objekte in Containern Der Weg über ADO und den ADSI-OLE DB-Provider ist nicht der einzige Weg, um Informationen über einen Container zu erhalten. Sie können die Inhalte eines Containers auch über ADSI auflisten - dieser Weg ist jedoch nicht so effizient wie die Auflistung über ADO Seite 291 von 394
(vor allem bei vielen Objekten). Die Auflistung ist jedoch einfacher zu implementieren als die Suchabfrage über ADO.
Auflisten von Containerinhalten Um die Inhalte eines Active Directory-Containers aufzulisten, müssen Sie drei grundlegende Schritte durchführen: 1.Erstellen Sie eine Bindung an den Active Directory-Container, für den Sie die Inhalte auflisten möchten. Normalerweise handelt es sich hier um eine OU. 2.Schränken Sie den Ergebnissatz über die Eigenschaft Filter ein. Dieser Schritt ist optional, bei vielen Objekten kann er jedoch sehr nützlich sein. 3.Erstellen Sie eine Schleife, in der Sie weitere Aktionen mit den Objekten im Container durchführen. Script 5.39 listet die Untercontainer des Containers Configuration auf. Der Container Configuration befindet sich unter dem Stamm der Domäne fabrikam.com. Das Script geht hierzu in zwei Schritten vor: 1.Es baut eine Bindung an den Container Configuration in der Domäne auf. 2.Es verwendet eine For-Each-Schleife, um die Namen der Untercontainer auszugeben. Script 5.39: Auflisten der Untercontainernamen eines Containers 1Set objConfiguration = GetObject _ 2 ("LDAP://cn=Configuration,dc=fabrikam,dc=com") 3 4For Each objContainer in objConfiguration Wscript.Echo objContainer.Name 5 6Next
Wenn sie das Script in der Gesamtstruktur fabrikam.com ausführen, gibt es alle Untercontainer des Containers Configuration aus: CN=DisplaySpecifiers CN=Extended-Rights CN=ForestUpdates CN=LostAndFoundConfig CN=Partitions CN=Physical Locations CN=Services CN=Sites CN=WellKnown Security Principals
Das Script kann aus jeder Domäne der Gesamtstruktur ausgeführt werden. Administrative Aufgaben mit Hilfe der Auflistung durchführen Script 5.40 listet den Container Partitions auf, der sich im Container Configuration der Stammdomäne fabrikam.com befindet. Während der Auflistung werden zwei Einträge im Multiwert-Attribut upnSuffixes aktualisiert und das Script gibt die Werte in diesem Attribut aus. Es geht folgendermaßen vor: 1.Es definiert die Konstante ADS_PROPERTY_APPEND mit dem Wert 3. Diese wird später Seite 292 von 394
für die Methode PutEx verwendet (Ändern von Einträgen). 2.Es baut eine Bindung an den Container Partitions im Container Configuration der Stammdomäne auf. 3.Es fügt zwei Einträge zum Attribut upnSuffixes hinzu. 4.Es verwendet eine For-Each-Schleife, um die Einträge im Attribut upnSuffixes des Containers Partitions auszugeben. Script 5.40: Auflisten des Containers Partitions, um das Attribut upnSuffixes zu lesen und zu schreiben 1 Const ADS_PROPERTY_APPEND = 3 2 Set objPartitions = GetObject _ 3 ("LDAP://cn=Partitions,cn=Configuration,dc=fabrikam,dc=com") 4 5 objPartitions.PutEx ADS_PROPERTY_APPEND, upnSuffixes", _ 6 Array("sa.fabrikam.com","corp.fabrikam.com") 7 objPartitions.SetInfo 8 9 For Each Suffix in objPartitions.GetEx("upnSuffixes") 10 Wscript.Echo Suffix 11Next
Wenn Sie das Script in der Stammdomäne fabrikam.com ausführen fügt es zum Attribut upnSuffixes zwei Einträge hinzu und gibt dann die Inhalte des Attributs aus: corp.fabrikam.com sa.fabrikam.com
Script 5.40 funktioniert standardmäßig nur dann, wenn Ihr Benutzerkonto Mitglied der globalen Gruppe Domänen-Admins der Stammdomäne oder der Gruppe OrganisationsAdmins ist. Nur diese Gruppen haben das Recht, den Container Partitions zu bearbeiten. Einschränken der aufgelisteten Container auf einen bestimmten Objekttyp Script 5.41 listet die Inhalte des Containers Users der Domäne na.fabrikam.com auf und verwendet die Eigenschaft Filter, um die Auflistung auf Objekte vom Typ user einzuschränken. Das Script gibt dann das Attribut primaryGroupID und alle Einträge im Attribut memberOf aus. Weitere Informationen über die Eigenschaft Filter finden Sie im Abschnitt ADSI-Schnittstellen weiter unten in diesem Kapitel. Das Script geht folgendermaßen vor: 1.Es verwendet den Befehl On Error Resume Next. Mit dem Befehl werden alle Laufzeitfehler unterdrückt. In diesem Fall sollen die Fehler unterdrückt werden, die auftreten, wenn ein Attribut nicht im lokalen Zwischenspeicher gefunden werden kann. 2.Es definiert die Konstante E_ADS_PROPERTY_NOT_FOUND mit dem Wert &h8000500D. Diese wird später (in Zeile 15) für die Feststellung des ADSI-Fehlercodes verwendet (der Fehler tritt auf, wenn das Attribut memberOf nicht im lokalen Zwischenspeicher gefunden werden kann). 3.Es baut eine Bindung an den Container Users der Domäne na.fabrikam.com auf. 4.Es setzt die Eigenschaft Filter, um nur Benutzerobjekte zurückzugeben (Zeile 6). Seite 293 von 394
5.Es verwendete eine For-Each-Schleife, um Informationen über die einzelnen Benutzerkonten auszugeben. In der Schleife passiert folgendes: 1.Über die Methode Get wird der Wert des Attributs primaryGroupID abgefragt und ausgegeben (Zeile 11). 2.Über die Methode GetEx wird das Array arrMemberOf mit den Einträgen des Attributs memberOf initialisiert (Zeile 13). Wenn das Attribut memberOf nicht vorhanden ist, dann tritt ein Fehler mit der Fehlernummer aus E_ADS_PROPERTY_NOT_FOUND auf. In diesem Fall wird eine Meldung ausgegeben und das Err-Objekt wird zurückgesetzt (Zeilen 19-21). Wenn das Attribut vorhanden ist, dann werden die einzelnen Elemente des Arrays arrMemberOf ausgegeben (Zeilen 15-18). Script 5.41: Einschränken der Auflistung auf Benutzerkonten über die Eigenschaft Filter 1 On Error Resume Next 2 Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D 3 Set objOU = GetObject _ 4 ("LDAP://cn=Users,dc=NA,dc=fabrikam,dc=com") 5 6 ObjOU.Filter= Array("user") 7 8 For Each objUser in objOU 9 Wscript.Echo objUser.cn & " ist Mitglied von: " 10 Wscript.Echo vbTab & "Primary Group ID: " & _ 11 objUser.Get("primaryGroupID") 12 13 arrMemberOf = objUser.GetEx("memberOf") 14 15 If Err.Number <> E_ADS_PROPERTY_NOT_FOUND Then 16 For Each Group in arrMemberOf 17 Wscript.Echo vbTab & Group 18 Next 19 Else 20 Wscript.Echo vbTab & "Attribut memberOf ist nicht vorhanden" Err.Clear 21 22 End If 23 Wscript.Echo VbCrLf 24Next
Wenn Sie das Script in der Domäne na.fabrikam.com ausführen, gibt es das Attribut primaryGroupID und die Einträge des Multiwert-Attributs memberOf aller Benutzerkonten aus: Primary Group ID: 513 CN=Richtlinien-Ersteller-Besitzer,CN=Users,DC=TestOrga,DC=com CN=Domänen-Admins,CN=Users,DC=TestOrga,DC=com CN=Organisations-Admins,CN=Users,DC=TestOrga,DC=com CN=Schema-Admins,CN=Users,DC=TestOrga,DC=com CN=Administratoren,CN=Builtin,DC=TestOrga,DC=com Gast ist Mitglied von: Primary Group ID: 514 CN=Gäste,CN=Builtin,DC=TestOrga,DC=com Remoterouter ist Mitglied von: Primary Group ID: 513
Seite 294 von 394
Attribut memberOf ist nicht vorhanden SUPPORT_388945a0 ist Mitglied von: Primary Group ID: 513 CN=Hilfedienstgruppe,CN=Users,DC=TestOrga,DC=com
Root Directory Service Entry Ein Element, das zur Erstellung eines ADsPfads eines Objekts verwendet werden kann, ist der Root Directory Service Entry (rootDSE). Der rootDSE ist der Stamm des Verzeichnisses - er ist ein nützliches ADSI-Feature, da er es einem Script ermöglicht, wichtige Informationen über das Stammverzeichnis abzurufen. So sind Sie in der Lage, keine DNs fest in das Script eintragen zu müssen. Bis jetzt haben alle Scripte in diesem Kapitel ein ADSI-Feature verwendet, das sich serverlose Bindung nennt. Serverlose Bindung bedeutet, dass im Script kein Name eines Domänencontrollers eingetragen werden muss. Stattdessen sucht ADSI einen Domänencontroller der Standarddomäne, der die Bindungsanfrage verarbeiten kann. Bei der Standarddomäne handelt es sich normalerweise um die Domäne, an der Sie angemeldet sind. Bisher musste in allen Scripte in diesem Kapitel aufgrund der serverlosen Bindung der DN in den ADsPfaden fest eingetragen werden .Die Scripte arbeiten daher nur in der Gesamtstruktur fabrikam.com. Mit dem rootDSE können Sie die Scripte so ändern, dass sie auch in anderen Gesamtstrukturen arbeiten.
Verwendung des rootDSE Zu Verwendung des rootDSE sind drei grundlegende Schritte erforderlich. 1.Eine Bindung zum rootDSE aufbauen. 2.Verwenden der Methode Get, um Attribute des rootDSE zu lesen. 3.Verwenden der Attribute zum Erstellen des ADsPfads, um eine Bindung an einen Container oder ein Objekt zu erstellen. Die Schritte 2 und 3 werden oft in einem Schritt kombiniert. Die vier Scripte in diesem Abschnitt sollen die Verwendung des rootDSE verdeutlichen. Binden an die aktuelle Domäne Script 5.42 baut mit Hilfe des Attributs defaultNamingContext des rootDSE eine Bindung an die aktuelle Domäne auf. Die aktuelle Domäne ist die, an der der Client angemeldet ist. Indem Sie die Bindung verändern, können Sie auch eine Bindung an einem bestimmten Container der Methode aufbauen (zum Beispiel Users). Das Script führt die folgenden Schritte durch: 1.Es baut eine Bindung zum rootDSE auf. 2.Es erstellt den ADsPfad der aktuellen Domäne. 3.Es verwendet die Variable strADsPath für die Bindung an das Zielobjekt. Script 5.42: Erstellen des ADsPfads mit Hilfe des rootDSE 1Set objRootDSE = GetObject("LDAP://rootDSE") 2strADsPath = "LDAP://" & objRootDSE.Get("defaultNamingContext")
Seite 295 von 394
3Set objDomain = GetObject(strADsPath)
In der Domäne na.fabrikam.com enthält die Variable strADsPath den Wert: LDAP://DC=na,DC=fabrikam,DC=com
Binden an die Stammdomäne Script 5.43 baut eine Bindung an die Stammdomäne auf - hierbei ist es egal, an welcher Domäne der Client angemeldet ist. Script 5.43: Erstellen des ADsPfads der Stammdomäne über den rootDSE 1Set objRootDSE = GetObject("LDAP://rootDSE") 2strADsPath = "LDAP://" & objRootDSE.Get("rootDomainNamingContext") 3Set objRootDomain = GetObject(strADsPath)
Wenn Sie das Script in einer Domäne in der Gesamtstruktur fabrikam.com ausführen, enthält die Variable strADsPath den Wert: LDAP://DC=fabrikam,DC=com
Binden an den Container Configuration Script 5.44 baut eine Bindung an den Container configuration der Gesamtstruktur auf. Script 5.44: Erstellen des ADsPfads des Containers Configuration mit dem rootDSE 1Set objRootDSE = GetObject("LDAP://rootDSE") 2strADsPath = "LDAP://" & objRootDSE.Get("configurationNamingContext") 3Set objConfiguration = GetObject(strADsPath)
Wenn das Script in einer Domäne der Gesamtstruktur fabrikam.com ausgeführt wird, dann enthält die Variable strADsPath den Wert: LDAP://CN=Configuration,DC=fabrikam,DC=com
Binden an den Container Schema Script 5.45 baut eine Bindung an den Container schema der Gesamtstruktur auf. Script 5.45: Erstellen des ADsPfads des Containers Schema mit dem rootDSE 1Set objRootDSE = GetObject("LDAP://rootDSE") 2strADsPath = "LDAP://" & objRootDSE.Get("schemaNamingContext") 3Set objSchema = GetObject(strADsPath)
Wenn das Script in einer Domäne der Gesamtstruktur fabrikam.com ausgeführt wird, dann enthält die Variable strADsPath den Wert: LDAP://CN=Schema,CN=Configuration,DC=fabrikam,DC=com
Anmerkung: Eine vollständige Liste der Eigenschaften des rootDSE finden Sie unter dem Link Active Directory Programmer's Guide unter http://www.microsoft.com/windows/reskits/webresources.
Die Active Directory-Architektur Seite 296 von 394
Ein grundlegendes Verständnis der physikalischen und logischen Active Directory-Struktur hilft Ihnen bei der erfolgreichen Erstellung von ADSI-Scripten. Wenn Sie zum Beispiel wissen, wie Sie herausfinden, welche Attribute ein Objekt verwendet, dann können Sie auch über ein Script auf diese Attribute zugreifen. Außerdem sollten Sie wissen, welche Attribute auf dem globalen Katalog-Server gespeichert und indiziert werden. Die physikalische Struktur von Active Directory gibt wieder, wo die Elemente gespeichert werden und über welche Komponenten auf diese Elemente zugegriffen werden kann. Die logische Struktur definiert, wie Active Directory über ADSI und andere Werkzeuge verwaltet wird.
Die physikalische Architektur Active Directory setzt sich aus zwei primären Komponenten zusammen: dem Active Directory-Speicher und den Komponenten, über auf Active Directory zugegriffen werden kann (DLLs). Der Active Directory-Speicher stellt einen sicheren, durchsuchbaren und hierarchischen Speicher für Objekte (Benutzer, Computer, Drucker, Anwendungen, usw.) zur Verfügung. Der Active Directory-Speicher befindet sich in einer Datei mit dem Namen Ntds.dit (Windows NT® Directory Services Directory Information Tree). Bei den erforderlichen Komponenten zum Zugriff auf Active Directory handelt es sich um: •
Die Extensible Storage Engine - Schreibt und liest Daten aus dem Active DirectorySpeicher (Ntds.dit). Die Extensible Storage Engine (ESE) führt Schreiboperationen als direkte Transaktionen durch. Sie sichert die Ntds.dit über Protokolldateien - über diese können Transaktionen rückgängig gemacht werden und Wiederherstellungen der Datenbank durchgeführt werden.
•
Eine Datenbankschicht - Stellt eine objektsorientierte, hierarchische Ansicht der Daten im Active Directory-Speicher zur Verfügung.
•
Den DSA (Directory System Agent) - ADSI-Provide und andere Schnittstellen verwenden den DAS, um eine Verbindung mit der Datenbankschicht aufzubauen und auf den Active Directory-Speicher zuzugreifen. Der DSA arbeitet als Vermittler und stellt sicher, dass die Clientaktionen mit den Regeln für die entsprechenden Active Directory-Objekte übereinstimmen. Ungültige Aktionen (zum Beispiel das Schreiben eines zu langen Werts in ein Attribut oder das Erstellen eines Objekts ohne alle verpflichtenden Attribute zu definieren) lässt der DSA nicht zu. Der DSA ist außerdem ein integraler Bestandteil der Active Directory-Replikation
•
Das LDAP-Protokoll (Lightweight Directory Access Protocol) - Eine Protokollschicht, die zur Kommunikation mit LDAP-fähigen Verzeichnisdiensten wie Active Directory verwendet werden kann.
Die logische Struktur Wenn Sie einen Computer unter Windows 2000 Server zu einem Domänencontroller machen, dann wird der Active Directory-Speicher auf diesem Domänencontroller eingerichtet - vor der vollständigen Einrichtung müssen Sie die Beziehung des neuen Domänencontrollers zu den vorhandenen Domänencontrollern angeben. Der Domänencontroller kann für eine neue Domäne oder für eine vorhandene Domäne konfiguriert werden. Seite 297 von 394
Um skalierbar zu sein, kann Active Directory Tausende von Domänen unterstützen. Alle Domänen können in Form einer Hierarchie verbunden werden. Eine einzelne Hierarchie wird Struktur genannt. Mehrere Strukturen sind eine Gesamtstruktur.
Klassen und Attribute In jeder Active Directory-Gesamtstruktur gibt es ein Schema. Beim Schema handelt es sich um eine Bibliothek, in der alle Objekte hinterlegt sind, die in Active Directory erstellt werden können. Sie können sich das Schema als Sammlung von Vorlagen vorstellen. Das Schema enthält Klassen und Attribute.Klassen werden von Active Directory zur Erstellung von Objekten verwendet. Die Klassen setzen sich aus einzelnen Attributen zusammen. Die Objekte, die aufgrund einer Klasse erstellt werden, verwenden daher alle Attribute der Klasse. Bei den Objekten handelt es sich um die logischen Gegenstücke zu den tatsächlichen Objekten im Netzwerk (zum Beispiel Computer, Benutzer oder Drucker). In Abbildung 5.6 sehen Sie die Beziehungen zwischen den Attributen und Klassen im Schema und den aus diesen erstellten Objekten (Instanzen).
Abbildung 5.6: Attribute, Klassen und Objekte und deren Beziehungen Wenn ein Objekt auf Grundlage einer Klasse erstellt wird, dann wird dieser Vorgang Instanzierung genannt. Ein Objekt, das Aufgrund einer Klasse erstellt wurde, wird Instanz genannt. Jede Klasse definiert einen Objekttyp. Die Attribute in einer Klasse beschreiben diese Klasse (bzw. den aus ihr zu erstellenden Objekttyp). Die Klasse Computer enthält zum Beispiel das Attribut cn mit dem Wert Computer und das Attribut lDAPDisplayName mit dem Wert computer. Jedes der beiden Attribute hat eine Funktion. Sie haben zum Beispiel gesehen, dass das Attribut lDAPDisplayName vom LDAP-Provider zum Zugriff auf das Objekt verwendet wird. Verpflichtende (Mandatory) und optionale Attribute Zwei weitere Attribute einer Klasse sind systemMustContain und systemMayContain. Diese Attribute definieren, welche Attribute eines Objekts verpflichtend (systemMustContain) und welche Attribute optional sind. Script 5.46 liest alle verpflichtenden und optionalen Attribute der Klasse Computer aus. Hierzu führ das Script die folgenden Schritte durch: 1.Es verwendet den Befehl On Error Resume Next. 2.Es definiert die Konstante E_ADS_PROPERTY_NOT_FOUND mit dem Wert &h8000500D. Diese wird später verwendet, um festzustellen, ob die Attribute Seite 298 von 394
systemMustContain oder systemMayContain vorhanden sind (Zeilen 10 und 21). 3.Es verwendet den rootDSE, um den Wert des Attributs schemaNamingContext abzufragen. Weitere Informationen über den rootDSE finden Sie im Abschnitt Root Directory Service Entry weiter oben in diesem Kapitel. 4.Es baut über die Funktion GetObject und den Provider LDAP eine Bindung zur Klasse computer im Container schema auf. 5.Es initialisiert die Variable arrMandatoryAttributes mit dem Attribut systemMustContain (Zeile 9). •
•
Wenn hier der Fehlerwert aus der Konstante E_ADS_PROPERTY_NOT_FOUND zurückgegeben wird, dann gibt das Script eine Nachricht aus ("Keine verpflichtenden Attribute"). Andernfalls wird eine For-Each-Schleife verwendet, um die Namen aller verpflichtenden Attribute der Klasse computer zu lesen und auszugeben.
1.Es initialisiert die Variable arrOptionalAttributes mit dem Wert des Attributs systemMayContain (Zeile 20). • •
Wenn hier der Fehlerwert aus der Konstante E_ADS_PROPERTY_NOT_FOUND zurückgegeben wird, dann gibt das Script eine Nachricht aus ("Keine optionalen Attribute"). Andernfalls wird eine For-Each-Schleife verwendet, um die Namen aller optionalen Attribute der Klasse computer zu lesen und auszugeben.
Script 5.46: Auslesen der verpflichtenden und optionalen Attribute der Klasse computer 1 On Error Resume Next 2 Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D 3 strClassName = "cn=Computer" 4 5 Set objRootDSE = GetObject("LDAP://rootDSE") 6 Set objSchemaClass = GetObject("LDAP://" & strClassName & "," & _ objRootDSE.Get("schemaNamingContext")) 7 8 9 arrMandatoryAttributes = objSchemaClass.GetEx("systemMustContain") 10If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then 11 Wscript.Echo "Keine verpflichtenden Attribute" 12 Err.Clear 13Else 14 Wscript.Echo "Verpflichtende Attribute" 15 For Each strAttribute in arrMandatoryAttributes 16 Wscript.Echo strAttribute 17 Next 18End If 19 20arrOptionalAttributes = objSchemaClass.GetEx("systemMayContain") 21If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then 22 Wscript.Echo "Keine optionalen Attribute" 23 Err.Clear 24Else 25 Wscript.Echo VbCrLf & "Optionale Attribute " 26 For Each strAttribute in arrOptionalAttributes 27 Wscript.Echo strAttribute 28 Next 29End If
Seite 299 von 394
Wenn Sie das Script ausführen, gibt es die Nachricht "Keine verpflichtenden Attribute" aus und dann die optionalen Attribute: Keine verpflichtenden Attribute Optionale Attribute volumeCount siteGUID rIDSetReferences ... dNSHostName defaultLocalPolicyObject cn catalogs
Wenn Sie ein Computerobjekt erstellen, müssen Sie einen Wert für das Attribut sAMAccountName festlegen - es handelt sich hier um ein verpflichtendes Attribut der Klasse computer. Das vorhergehende Script gibt jedoch an, dass keine verpflichtenden Attribute vorhanden sind. Das Script hat hier Recht. Die Klasse computer erbt Attribute von den in der Active Directory-Hierarchie übergeordneten Klassen (dies gilt auch für viele andere Klassen). Daher verwendet die Klasse computer zwar das verpflichtende Attribut sAMAccountName, es wird jedoch nicht in der Klasse selbst definiert. Vererbung und Kategorisierung von Klassen Nicht nur Active Directory verwendet eine hierarchische Struktur (Domänen sind in Strukturen und Gesamtstrukturen zusammengefasst), sondern auch die Klassen im Schema. Einige Attribute werden in dieser Hierarchie von einer Klasse zur anderen vererbt - andere Attribute werden direkt in den Klassen definiert. Das Objekt, das aus einer Klasse erstellt wird, enthält dann alle Attribute - die geerbten und die in der Klasse definierten. Ein Computerobjekt erbt zum Beispiel Attribute von der Klasse user. Wenn wir die Klassenhierarchie weiter nach oben gehen, werden Attribute von den Klassen organizationalPerson, person und top geerbt. Die Klasse user enthält auch Attribute, die direkt über die Klassen mailRecipient und securityPrincipal angewandt werden. Die Klasse securityPrincipal enthält wiederum das Attribut sAMAccountName - dieses ist für alle Klassen, die das Attribut erben, verpflichtend. Daher erbt auch die Klasse computer das verpflichtende Attribut sAMAccountName. Es gibt noch weitere Informationen, die Sie der Klassenhierarchie entnehmen können. Die Klasse contact ist zum Beispiel kein Sicherheitsprinzipal- und zwar darum, weil die Klasse securityPrincipal nicht auf die Klasse contact angewandt wird und diese daher nicht die Attribute der Klasse securityPrincipal erbt. Klassen sind in die Kategorien Abstract, Structural, Auxiliary und 88 aufgeteilt. Die Kategorie wird im Attribut objectClassCategory einer Klasse als Integer-Wert gespeichert. •
Abstract - Die Attribute dieser Klassen werden in der Hierarchie vererbt. Es können jedoch keine Objekte aus diesen Klassen erstellt werden. Der Integer-Wert für diese Klasse ist 2.
•
Auxiliary - Die Attribute dieser Klasse erweitern eine Klasse vom Typ Structural. Es kann jedoch keine Klasse von Typ Structural oder ein Objekt aus dieser Klasse erstellt werden. Der Integer-Wert für diese Klasse ist 3.
•
Structural - Aus dieser Klasse können neue Instanzen von Objekten erstellt werden. Diese Klasse kann Attribute enthalten, die nicht von anderen Klassen geerbt werden. Der IntegerSeite 300 von 394
Wert für diese Klasse ist 1. •
88 - Diese Klassen wurden definiert, bevor es Klassenkategorien gab. Sie verhalten sich wie Klassen der Kategorie Structural - aus ihnen können Instanzen von Objekten erstellt werden. Der Integer-Wert für diese Klasse ist 0.
Active Directory ist kompatibel mit dem X.500-Standard. Daher müssen auch die 1993 in diesem Standard definierten Kategorien verwendet werden. Alle Klassen, die vor 1993 definiert wurden, entsprechen dem X.500-Standard von 1988. Dieser enthält keine Kategorien. Daher enthält die Kategorie 88 alle Klassen, die bis 1993 definiert wurden. Script 5.47 liest das Attribut objectClassCategory der Klassen Top, Mail-Recepient, SecurityPrincipal, Person, Organizational-Person, Contact, User, Computer und OrganizationalUnit. Hierzu führt das Script die folgenden Schritte durch: 1.Es initialisiert ein Array mit den allgemeinen Namen (CNs) der Klassen. Die Funktion GetObject benötigt den CN der Klasse - nicht den lDAPDisplayName. 2.Es verwendet den rootDSE, um den Wert des Attributs schemaNamingContext abzufragen. 3.Es verwendete eine For-Each-Schleife, um über die Funktion GetObject und den Provider LDAP eine Bindung an die einzelnen Klassen im Container Schema aufzubauen. 4.Es initialisiert die Variable intClassCategory mit dem Integer-Wert des Attributs objectClassCategory. 5.Es verwendet eine Select-Case-Anweisung, um den Wert der Variable intClassCategory zu prüfen und den Typ der jeweiligen Klasse anzuzeigen. Script 5.47: Lesen des Attributs objectClassCategory mehrerer Klassen 1 arrClassNames = Array _ 2 ("cn=top","cn=mail-Recipient", "cn=security-Principal", _ 3 "cn=person", "cn=Organizational-Person", _ 4 "cn=contact", "cn=user", "cn=computer", "cn=organizational5 Unit") 6 7 Set objRootDSE = GetObject("LDAP://rootDSE") 8 For Each ClassName in arrClassNames 9 Set objSchemaClass = GetObject("LDAP://" & ClassName & "," & _ 10 objRootDSE.Get("schemaNamingContext")) 11 12 intClassCategory = objSchemaClass.Get("objectClassCategory") 13 WScript.STDOUT.Write ClassName & " is " 14 Select Case intClassCategory 15 Case 0 16 Wscript.Echo "88" 17 Case 1 18 Wscript.Echo "Structural" 19 Case 2 20 Wscript.Echo "Abstract" 21 Case 3 22 Wscript.Echo "Auxiliary" 23 End Select Next
Wenn Sie das Script ausführen, zeigt es die Klassenkategorien einzelner Klassen an: cn=top is abstract
Seite 301 von 394
cn=mail-Recipient is auxiliary cn=security-Principal is auxiliary cn=person is 88 cn=Organizational-Person is 88 cn=contact is structural cn=user is structural cn=computer is structural cn=organizational-Unit is structural
Das Attribut systemAuxiliaryClass jeder Klasse gibt die Klassen vom Typ Auxiliary an, die direkt auf die Klasse angewandt werden. Script 5.48 liest das Attribut für mehrere Klassen aus: 1.Es verwendet den Befehl On Error Resume Next und definiert die Konstante E_ADS_PROPERTY_NOT_FOUND mit dem Wert &h8000500D. Über diese Konstante wird später geprüft, ob das Attribut systemAuxiliaryClass leer ist. 2.Es initialisiert ein Array mit den CNs der Klassen. 3.Es verwendet den rootDSE, um den Wert des Attributs schemaNamingContext abzufragen. 4.Es verwendet eine For-Each-Schleife, um über die Funktion GetObject und dem Provice LDAP eine Bindung zu den einzelnen Klassen im Container Schema aufzubauen. 5.Es initialisiert die Variable arrSystemAuxiliaryClass mit dem Attribut systemAuxiliaryClass (Zeilen 15 und 16). • •
Wenn hier der Fehlerwert in E_ADS_PROPERTY_NOT_FOUND zurückgegeben wird, dann wird eine entsprechende Meldung ausgegeben. Andernfalls wird eine For-Each-Schleife verwendet, um die Liste der zur Klasse zugewiesenen Klassen vom Typ Auxiliary anzuzeigen.
Script 5.48_ Lesen des Attributs systemAuxiliaryClass mehrerer Klassen 1 On Error Resume Next 2 Const E_ADS_PROPERTY_NOT_FOUND = &h8000500D 3 arrClassNames = Array _ 4 ("cn=top","cn=mail-Recipient", "cn=security-Principal", _ 5 "cn=person", "cn=Organizational-Person", _ 6 "cn=contact", "cn=user", "cn=computer", "cn=organizational7 Unit") 8 9 Set objRootDSE = GetObject("LDAP://rootDSE") 10For Each ClassName in arrClassNames 11 Set objSchemaClass = GetObject("LDAP://" & ClassName & "," & _ 12 objRootDSE.Get("schemaNamingContext")) 13 14 arrSystemAuxiliaryClass = _ 15 objSchemaClass.GetEx("systemAuxiliaryClass") 16 17 If Err.Number = E_ADS_PROPERTY_NOT_FOUND Then 18 Wscript.Echo "Keine Klassen vom Typ Auxiliary " & _ 19 " assigned to " & ClassName 20 Err.Clear 21 Else 22 Wscript.Echo "Auxiliary-Klassen in " & ClassName & ":" 23 For Each strAuxiliaryClass in arrSystemAuxiliaryClass 24 Wscript.Echo vbTab & strAuxiliaryClass 25 Next
Seite 302 von 394
26 27
Wscript.Echo End If Next
Wenn sie das Script ausführen, gibt es eine Meldung aus, die anzeigt, das die Auxiliary-Klasse mailRecipient auf die Klassen contact und user angewandt wird, und dass die AuxiliaryKlasesecurityPrincipal auf die Klasse user angewandt wird: No auxiliary classes assigned to No auxiliary classes assigned to No auxiliary classes assigned to No auxiliary classes assigned to No auxiliary classes assigned to Auxiliary classes in cn=contact: mailRecipient
cn=top. cn=mail-Recipient. cn=security-Principal. cn=person. cn=Organizational-Person.
Auxiliary classes in cn=user: securityPrincipal mailRecipient No auxiliary classes assigned to cn=computer. No auxiliary classes assigned to cn=organizational-Unit.
Wenn Sie die Klassenvererbung verstehen, dann hilft Ihnen das dabei Scripte zu schreiben, die auch die erwarteten Objekte zurückgeben. Wenn Sie zum Beispiel eine Suche nach allen Objekten durchführen, deren Attribut objectClass den Wert user enthält, dann sind Sie sicher überrascht, dass Sie auch Objekte zurückerhalten, die aus der Klasse computer erstellt wurden. Die Beziehungen zwischen den Klassen können Sie auch über ein Script anzeigen lassen. Das Attribut subClassOf gibt die übergeordnete Klasse an, von der die Klasse abgeleitet ist. Script 5.49 zeigt, wie Sie dieses Attribut abfragen. Seine Arbeitsweise entspricht den vorherigen beiden Scripten. Script 5.49: Lesen des Attributs subClassOf einer Klasse 1strClassName = "cn=computer" 2 3Set objRootDSE = GetObject("LDAP://rootDSE") 4Set objSchemaClass = GetObject("LDAP://" & strClassName & "," & _ 5 objRootDSE.Get("schemaNamingContext")) 6 7strSubClassOf = objSchemaClass.Get("subClassOf") 8Wscript.Echo "Die Klasse " & strClassName & _ 9 " ist eine untergeordnete Klasse von " & strSubClassOf
Wenn Sie das Script ausführen, erhalten Sie die folgende Ausgabe: Die Klasse cn=computer ist eine untergeordnete Klasse von user
Snap-Ins zum Anzeigen und Konfigurieren von Attributen, Klassen und Objekten Klassen und Attribute des Schemas können Sie auch über ein MMC-Snap-In anzeigen lassen. Die beiden Snap-Ins hierzu heißen Active Directory Schema und ADSI Edit. Mit dem Snap-In Active Directory Schema steht Ihnen eine grafische Schnittstelle für den Zugriff auf Klassen und Attribute zur Verfügung. Mit ADSI Edit können Sie außerdem auch noch auf alle Active Directory-Objekte zugreifen. Das Snap-In Active Directory Schema Seite 303 von 394
Auf der linken Seite des Snap-Ins sehen Sie zwei Listen: • •
Die Klassenliste mit allen Klassen des Active Directory-Schemas Die Attributliste mit allen Attributen des Schemas
Über die Klassenliste können Sie die von einer bestimmten Klasse verwendeten Attribute und die hierarchischen Beziehungen zwischen den Klassen sehen. Über die Attributliste können Sie feststellen, wie ein Attribut definiert ist (zum Beispiel, ob das Attribut auf den globalen Katalog-Server repliziert werden soll, ob es sich um ein Einzelwert- oder Multiwert-Attribut handelt, usw.). Das Snap-In Active Directory Schema müssen Sie vor der ersten Verwendung registrieren: 1.Geben Sie in der Eingabeaufforderung den Befehl regsvr32 schmmgmt ein. (Dieser Schritt muss nur einmal auf jedem Computer durchgeführt werden.) 2.Starten sie eine MMC-Konsole Start MMC, indem Sie MMC in der Eingabeaufforderung oder im Feld Ausführen eingeben. 3.Klicken Sie auf Datei, Snap-In hinzufügen/entfernen und klicken Sie auf Hinzufügen. 4.Klicken Sie auf Active Directory Schema und dann auf Hinzufügen. Das Snap-In ADSI Edit Das Snap-In ADSI Edit zeigt nicht nur Informationen über das Schema, sondern auch über die Objekte der Domäne an. Wahrscheinlich finden Sie die Informationen über das Schema jedoch im Snap-In Active Directory Schema übersichtlicher. Nachdem Sie das Snap-In ADSI Edit geladen haben, können Sie auswählen, wie Sie sich mit Active Directory verbinden möchten. Die Standard-Verbindungspunkte sind: •
LDAP://domain_controller_name/Domain - Informationen über die Objekte der Domäne.
•
LDAP://domain_controller_name/Configuration - Informationen über die Konfigurationspartition der Gesamtstruktur.
•
LDAP://domain_controller_name/RootDSE - Informationen über die Attribute des RootDSE-Objekts.
•
LDAP://domain_controller_name/Schema - Informationen über den Schemacontainer der Gesamtstruktur.
Anmerkung: Sie können natürlich auch eine Verbindung mit einem globalen Katalog-Server aufbauen und dessen Partitionen anzeigen. Das Snap-In ADSI Edit installieren Sie folgendermaßen: 1.Installieren Sie die SupportTtools (SupTools.msi) von der Windows 2000 ServerInstallations-CD (die Support Tools befinden sich im Ordner Support\Tools). 2.Starten Sie eine MMC-Konsole Start MMC, indem Sie MMC in der Eingabeaufforderung oder im Feld Ausführen eingeben.
Seite 304 von 394
3.Klicken Sie auf Datei, Snap-In hinzufügen/entfernen und klicken Sie auf Hinzufügen. 4.Klicken Sie auf ADSI Edit und dann auf Hinzufügen. 5.Klicken Sie auf der linken Seite mit der rechten Maustaste auf ADSI Edit und wählen Sie Connect to. 6.Übernehmen Sie im Fenster Connection Settings die Standardeinstellungen und klicken Sie auf OK.
Active Directory-Replikation und -Indizierung Daten werden in Active Directory repliziert. Das bedeutet, dass die Änderungen auf einem Domänencontroller an alle anderen Domänencontroller übertragen werden. Ein kritischer Teil der Verwaltung von Active Directory über ADSI-Scripte ist die Replikation. Um den Replikationsaufwand zu verringern, ist Active Directory in Partitionen aufgeteilt. Diese Partitionen werden entweder vollständig oder teilweise repliziert. Partitionen mit einer vollständigen Replikation werden an alle Domänencontroller in der Domäne oder der Gesamtstruktur übertragen. Sie können also von jedem Domänencontroller in der Domäne oder Gesamtstruktur gelesen oder geschrieben werden. Partitionen mit einer teilweisen Replikation werden nur an bestimmte Domänencontroller übertragen. Sie enthalten nur Teile von Active Directory und können nur gelesen werden. Partitionen Es gibt drei vollständig replizierte Partitionen auf jedem Domänencontroller. •
Die Schemapartition - Diese Partition enthält alle Klassen und Attribute der Gesamtstruktur.
•
Die Konfigurationspartition - Diese Partition enthält Informationen über die Konfiguration des Gesamtstruktur (zum Beispiel Domänennamen und Standorte).
•
Die Domänenpartition - Diese Partition enthält alle Objekte der jeweiligen Domäne des Domänencontrollers. Im Gegensatz zu den anderen beiden Partitionen ist die Domänenpartition für jede Domäne unterschiedlich. Schema- und Konfigurationspartition werden von allen Domänen der Gesamtstruktur gemeinsam verwendet.
Attribute, die zum globalen Katalog-Server repliziert werden Alle Domänencontroller, die als globaler Katalog-Server konfiguriert sind, speichern eine teilweise Replik aller Domänenpartitionen der Domänen der Gesamtstruktur. Diese Teilreplik - der globale Katalog - enthält zwar alle Objekte der jeweiligen Domänenpartitionen, aber nur einen Teil der Attribute dieser Objekte. Wenn Sie effiziente Scripte schreiben möchten, dann sollten Sie wissen, um welche Attribute es sich handelt. Wenn Sie ohne einen globalen Katalog-Server Informationen über mehrere Domäne der Gesamtstruktur abfragen möchten, dann müssen Sie die Eigenschaft Nachverfolgen (chasing) verwenden. Diese sorgt jedoch für eine höhere Last auf den beteiligten Domänencontrollern. Sie sollten also versuchen jene Attribute zu verwenden, die auf den globalen Katalog-Server repliziert werden. Das Attribut isMemberOfPartialAttributeSet eines Attributs im Schema speichert einen Boolean-Wert. Dieser zeigt, ob das entsprechende Attribut an den globalen Katalog-Server repliziert wird. In Script 5.50 sehen Sie, wie Sie dieses Attribut abfragen können. Seite 305 von 394
1.Es initialisiert eine Variable mit dem Namen strAttributeName mit dem CN eines Attributs. In diesem Beispiel wird das Attribut given-name verwendet. 2.Es verwendet den rootDSE, um den Wert des Attributs schemaNamingContext abzufragen. 3.Es baut über die Funktion GetObject und den Provider LDAP eine Bindung an das Attribut im Container schema auf. 4.Es initialisiert die Variable blnInGC mit dem Boolean-Wert des Attributs isMemberOfPartialAttributeSet. • •
Wenn blnInGC den Wert True hat, dann wird das Attribut an den globalen Katalog-Server repliziert. Eine entsprechende Meldung wird ausgegeben. Andernfalls wird das Attribut nicht repliziert. Auch hier wird eine Meldung ausgegeben.
Script 5.50: Lesen des Attributs isMemberOfPartialAttributeSet eines Attributs 1 strAttributeName = "cn=given-name" 2 3 Set objRootDSE = GetObject("LDAP://rootDSE") 4 Set objSchemaAttribute = GetObject("LDAP://" & strAttributeName & "," & _ 5 objRootDSE.Get("schemaNamingContext")) 6 7 blnInGC = objSchemaAttribute.Get("isMemberOfPartialAttributeSet") 8 If blnInGC Then 9 Wscript.Echo strAttributeName & _ 10 " wird an den globalen Katalog-Server repliziert." 11 Else 12 Wscript.Echo "The " & strAttributeName & _ 13 " wird nicht an den globalen Katalog-Server repliziert." 14End If
Wenn Sie das Script ausführen, erhalten Sie die folgende Ausgabe: cn=given-name wird an den globalen Katalog-Server repliziert.
Nachdem Sie festgestellt haben, dass ein Attribut an den globalen Katalog-Server repliziert wird, können Sie den Provider GC statt LDAP verwenden. In Script 5.50 ist Ihnen möglicherweise aufgefallen, dass das Script nach einem Attribut (isMemberOfPartialAttributeSet) eines Attributs sucht. Auch Attribute haben Attribute. Wenn Sie sich ein Attribut über das Snap-In ADSI Edit anschauen, dann werden Sie sehen, dass die Attribute und Klassen hier als Objekte angezeigt haben - und wie Sie wissen, haben Active Directory-Objekte nun einmal Attribute. Indizierung von Attributen Ein weiterer wichtiger Aspekt bei der Leistung von Suchen ist, ob das entsprechende Attribut indiziert wird. Indizierte Attribute sind bereits sortiert - hierdurch wird die Verarbeitungslast auf dem Domänencontroller verringert. Das Attribut searchFlags eines Attributs enthält einen Integer-Wert, der unter anderem anzeigt, ob das Attribut indiziert wird. Script 5.51 zeigt, wie Sie das Attribut abfragen können. Das Script entspricht zum größten Teil Script 5.50. Es geht folgendermaßen vor: 1.Es definiert die Konstante IS_INDEXED mit dem Wert 1, um später feststellen zu können, ob das Attribut indiziert wird. Seite 306 von 394
2.Es verwendet den rootDSE, um den Wert des Attributs schemaNamingContext abzufragen. 3.Es baut über die Funktion GetObject und den Provider LDAP eine Bindung an das Attribut im Container schema auf. 4.Es initialisiert die Variable intSearchFlags mit dem Integer-Wert des Attributs searchFlags (Zeile 8). 5.Es verwendet den logischen Operator AND, um den Attributwert mit der konstante IS_INDEXED zu vergleichen. • •
Wenn der Vergleich den Wert True zurückgibt, dann wird das Attribut indiziert. Dies wird mit einer entsprechenden Ausgabe quittiert. Andernfalls wird das Attribut nicht indiziert. Auch dies wird mit einer Ausgabe bestätigt.
Script 5.51: Lesen des Attributs searchFlags, um festzustellen, ob ein Attribut indiziert wird 1 Const IS_INDEXED = 1 2 strAttributeName = "cn=given-name" 3 4 Set objRootDSE = GetObject("LDAP://rootDSE") 5 Set objSchemaAttribute = GetObject("LDAP://" & strAttributeName & "," & _ 6 objRootDSE.Get("schemaNamingContext")) 7 8 intSearchFlags = objSchemaAttribute.Get("searchFlags") 9 If IS_INDEXED AND intSearchFlags Then 10 Wscript.Echo strAttributeName & " is indexed." 11Else 12 Wscript.Echo strAttributeName & " not indexed." 13End If
Wenn Sie das Script ausführen, sehen Sie die folgende Ausgabe: cn=given-name is indexed.
Attribute, die sowohl zum globalen Katalog-Server repliziert als auch indiziert werden Im Idealfall sollten Suchoperationen Attribute verwenden, die zum globalen Katalog-Server repliziert und indiziert werden. Vorsicht: Sie können die Attribute des Schemas auch von Hand so konfigurieren, dass Sie zum globalen Katalog-Server repliziert oder indiziert werden - zum Beispiel über das Snap-In Active Directory Schema. Sie sollten jedoch bei der Änderung des Schemas Vorsicht walten lassen. Wenn Sie das Schema beschädigen, dann kann es zu schweren Fehlern in Active Directory kommen. Script 5.52 zeigt, wie Sie eine Suchoperation durchführen, mit der Sie alle Attribute zurückerhalten, die sowohl indiziert als auch zum globalen Katalog-Server repliziert werden. Das Script geht hierzu folgendermaßen vor: 1.Es definiert die Konstante IS_INDEXED mit dem Wert 1, um später feststellen zu können, ob das Attribut indiziert wird. 2.Es verwendet den rootDSE, um die Variable strADsPath mit dem Wert des Attributs schemaNamingContext zu initialisieren. Seite 307 von 394
3.Es verwendet ADO, um alle AttributeSchema-Objekte abzufragen und für diese die Attribute lDAPDisplayName, isMemberOfPartialAttributeSet und searchFlags zurückzugeben. Der Filter objectCategory=AttributeSchema gibt alle Schema-Attribute zurück. 4.Es verwendet eine While-Wend-Schleife, um alle Einträge des Ergebnissatzes zu lesen. 5.Bei jedem Eintrag im Ergebnissatz wird festgestellt, ob das Attribut an die globalen Katalog-Server repliziert wird und ob es indiziert wird (Zeilen 20 und 21). Wenn beide Bedingungen den Rückgabewert True haben, dann wird das Attribut lDAPDisplayName des Attributs ausgegeben (aus der Variable strAttribute). Script 5.52: Attribute auflisten, die indiziert und zum globalen Katalog repliziert werden 1 Const IS_INDEXED = 1 2 3 Set objConnection = CreateObject("ADODB.Connection") 4 objConnection.Open "Provider=ADsDSOObject;" 5 6 Set objCommand = CreateObject("ADODB.Command") 7 objCommand.ActiveConnection = objConnection 8 9 Set objRootDSE = GetObject("LDAP://rootDSE") 10strADsPath = "" 11objCommand.CommandText = strADsPath & _ 12 ";(objectCategory=AttributeSchema);" & _ 13 14"lDAPDisplayName,isMemberOfPartialAttributeSet,searchFlags;onelevel" 15 16Set objRecordSet = objCommand.Execute 17Wscript.Echo "Attribute die indiziert und zum GC repliziert werden: " 18While NOT objRecordSet.EOF 19 strAttribute = objRecordSet.Fields("lDAPDisplayName") 20 If objRecordSet.Fields("isMemberOfPartialAttributeSet") AND _ 21 (IS_INDEXED AND objRecordSet.Fields("searchFlags")) Then 22 Wscript.Echo strAttribute 23 End If 24 objRecordSet.MoveNext 25Wend 26 objConnection.Close
Wenn Sie das Script ausführen, erhalten Sie eine Ausgabe wie die folgende: Attribute die indiziert und zum GC repliziert werden: AltSecurityIdentities cn displayName mail ... name sAMAccountName sAMAccountType servicePrincipalName sIDHistory sn
Seite 308 von 394
Operative Attribute Nicht alle Attributwerte werden im Verzeichnisdienst gespeichert. Einige Attributwerte werden erst dann berechnet, wenn Sie abgefragt werden. Diese Attribute nennt man operative Attribute. Sie werden wie alle anderen Attribute auch im Schema definiert. Sie sollten wissen, welche Attribute operative Attribute sind, da Sie diese Attribute nicht in den lokalen Zwischenspeicher laden können, bevor Sie nicht explizit die Methoden GetInfo oder GetInfoEx aufgerufen haben. Script 5.53 fragt ab, welche Attribute im Schema operativ sind. Hierzu geht es folgendermaßen vor: 1.Es definiert die Konstante ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED mit dem Wert &h4. 2.Es verwendet den rootDSE, um die Variable strADsPath mit dem Attribut schemaNamingContext zu initialisieren. 3.Es verwendet ADO, um alle Objekte vom Typ AttributeSchema abzufragen und die Attribute lDAPDisplayName und systemFlags dieser Attribute zurückzugeben. 4.Es verwendet eine While-Wend-Schleife, um jeden Eintrag im Ergebnissatz durchzugehen. 5.Für jeden Eintrag im Ergebnissatz stellt es fest, ob das Attribut operativ ist (Zeilen 19 und 20). •
Wenn das der Fall ist, dann zeigt es dass Attribut lDAPDisplayName des Attributs an (aus der Variable strAttribute).
Script 5.53: Prüfen, ob ein Attribut operativ ist 1 Const ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED = &h4 2 3 Set objConnection = CreateObject("ADODB.Connection") 4 objConnection.Open "Provider=ADsDSOObject;" 5 6 Set objCommand = CreateObject("ADODB.Command") 7 objCommand.ActiveConnection = objConnection 8 9 Set objRootDSE = GetObject("LDAP://rootDSE") 10strADsPath = "" 11objCommand.CommandText = strADsPath & _ ";(objectCategory=AttributeSchema);" & _ 12 13 "lDAPDisplayName,systemFlags;onelevel" 14 15Set objRecordSet = objCommand.Execute 16Wscript.Echo "Operative Attribute: " 17While NOT objRecordSet.EOF strAttribute = objRecordSet.Fields("lDAPDisplayName") 18 19 If ADS_SYSTEMFLAG_ATTR_IS_CONSTRUCTED AND _ 20 objRecordSet.Fields("systemFlags") Then 21 Wscript.Echo strAttribute 22 objRecordSet.MoveNext 23 End If 24Wend 25 26objConnection.Close
Seite 309 von 394
Wenn Sie das Script ausführen, erhalten Sie eine Ausgabe wie die folgende: Operative Attribute: allowedAttributes allowedAttributesEffective allowedChildClasses allowedChildClassesEffective aNR attributeTypes canonicalName createTimeStamp ...
Die ADSI-Architektur Wenn Sie saubere ADSI-Scripte schreiben möchten, dann ist es wichtig, dass Sie die Architektur von Active Directory kennen und verstehen. Auch das ADSI-Objektmodell sollten Sie kennen - inklusive der ADSI-Dateien, der logischen Komponenten, der verfügbaren ADSI-Provider und der möglichen Schnittstellen. Das ADSI-Objektmodell ADSI ist eine Sammlung von DLLs (Dynamic Link Libraries), die von Clientanwendungen wie zum Beispiel dem Windows Script Host (WSH) und dem Snap-In Active Directory Benutzer und Computer zum Zugriff auf Active Directory genutzt werden können. Wenn Sie eine Bindung in einem ADSI-Script einrichten, verwendet das Script den Moniker im ADsPfad, um die entsprechende ADSI-Provider-DLL zu laden (wenn Sie zum Beispiel den Moniker LDAP angegeben, wird die LDAP-DLL geladen - Adsidp.dll). Die ADSIProvider-DLL erstellt ein COM-Objekt (Component Object Model). Das COM-Objekt wird dem Script als Referenz zurückgegeben und diese wird über den Befehl Set einer Variablen zugewiesen. Jedes Objekt stellt Schnittstellen zur Verfügung. Eine Schnittstelle ist eine Gruppe von Methoden. Mit diesen Methoden können Sie bestimmte Aufgaben durchführen. Um Active Directory zu bearbeiten, haben Sie zum Beispiel die Methoden Create, Put, Get und Delete verwendet - diese Methoden werden alle über eine Schnittstelle mit dem Namen IADs zur Verfügung gestellt. In Abbildung 5.7 sehen Sie die Zusammenhänge zwischen einer Bindung, einem Provider, einem COM-Objekt, seinen Schnittstellen und Active Directory.
Abbildung 5.7: Ein COM-Objekt zum Zugriff auf ein Active Directory-Objekt erstellen
Seite 310 von 394
In Abbildung 5.7 sehen Sie, wie der LDAP-Provider eine Instanz eines COM-Objekts erstellt. Der Provider stellt fest, dass MyerKen in der OU HR der Domäne na.fabrikam.com ein Benutzerobjekt ist. Daher erstellt er ein COM-Objekt mit den Schnittstellen zur Arbeit mit Benutzerobjekten. In der Abbildung sind nur drei Schnittstellen zu sehen. IADs und IADsUser sind zwei Schnittstellen, die ADSI-Scripte bei der Arbeit mit Benutzerobjekten verwenden, und die Schnittstelle IUnknown wird an jedes COM-Objekt angehängt - sie ist die Basisschnittstelle für alle anderen Schnittstellen.. Mit dem Befehl Set erhalten Sie in der Variable objUser eine Referenz auf das neue Objekt. ADSI-Komponenten Unter Windows NT-basierten Computern finden Sie ADSI im Ordner systemroot\System32. Es setzt sich aus den Dateien ActiveDS.dll, ActiveDS.tlb und DLLs (alle DLLs, deren Name mit ads anfängt - zum Beispiel Adsldp.dll). Außerdem werden bei der Installation von ADSI einige andere DLLs aktualisiert - zum Beispiel Wldap32.dll. ADSI ist eine Systemkomponente von Windows 2000, Windows XP und Windows Server 2003. Unter diesen Betriebssystem muss also nichts installiert werden. Unter den Betriebssystemen Windows® 95, Windows® 98, Windows® Millennium Edition (Me) oder Windows NT 4.0 sieht das anders aus. Informationen zur Installation von ADSI unter diesen Betriebssystemen finden Sie unter dem Link Active Directory Service Interfaces unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig).
ADSI-Schichten Die ADSI-Architektur können Sie sich als fünf Schichten vorstellen: • • • • •
Verzeichnis oder Namespace ADSI-Provider ADSI-Router und Registrierung Eigenschafts-Zwischenspeicher ADSI-Anwendung
Seite 311 von 394
Abbildung 5.8: Die ADSI-Architektur Die fünf Schichten aus Abbildung 5.8 finden sich auf zwei Seiten wieder. Die unterste Schicht (Verzeichnis oder Namespace) befindet sich auf den Computern, die Active Directory zur Verfügung stellen (den Domänencontrollern), und die restlichen Schichten befinden sich auf den Clients. Verzeichnis oder Namespace Der Verzeichnisdienst (Active Directory) speichert Informationen und stellt diese Informationen Benutzern und Anwendungen zur Verfügung. Beim Verzeichnisdienst handelt es sich um eine Datenbank. Ein Namespace ist ein Bereich, in dem Namen aufgelöst werden können. Der DNSNamespace (Domain Name System) ist zum Beispiel ein Domänenbaum, der Hostnamen und IP-Adressen speichert. Die Clientcomputer verwenden den DNS-Namespace, um Hostnamen in IP-Adressen aufzulösen. Ein Verzeichnis-Namespace ist ein Bereich, in dem die Namen von Verzeichniselementen in die entsprechenden Objekte aufgelöst werden können. Bei Active Directory handelt es sich beim Namespace um die Gesamtstruktur. Die Namen der einzelnen Active Directory-Objekte können über diesen Namespace in Objekte (zum Beispiel Domänen, Benutzerkonten, OUs und Computer) aufgelöst werden. Der Provider ADSI ist kein Teil von Active Directory selbst. Es stellt nur die Schnittstellen zu Active Directory und dessen Namespace zur Verfügung. Über die folgenden Namespaces können Sie auf Active Directory zugreifen: • •
LDAP SAM-Datenbank (Security Accounts Manager) Seite 312 von 394
• • •
IIS-Metabase (Internet Information Services) NDS (Novell NetWare Directory Services) NetWare Bindery
ADSI-Provider greifen auf den Namespace zu und erstellen virtuelle Gegenstücke für die Objekte, die Sie über die ADSI-Schnittstellen bearbeiten wollen. Unter den ADSI-DLLs gibt es hierzu einige Provider-DLLs (Tabelle 5.5). Tabelle 5.5: ADSI-Provider und die Verzeichnisdienste, auf die sie zugreifen Provider
Zugriff auf
LDAP
LDAP-Verzeichnisse der Versionen 2 und 3 - inklusive Active Directory.
GC
Globaler Katalog der Active Directory-Domäne. Der Provider GC entspricht dem Provider LDAP - er verwendet jedoch die TCP-Portnummern 3268.
ADSI OLE DB
Active Directory für Suchoperationen.
WinNT
Windows NT-Domänen und die lokale Kontendatenbank unter Windows NT/Windows 2000/Windows XP/Windows Server 2003.
IIS
IIS-Metabase
NDS
Novell NetWare Directory Services.
NWCOMPAT Novell Netware Bindery. ADSI-Provider arbeiten als Vermittler zwischen einem Verzeichnis-Namespace und einer ADSI-Anwendung. Alle Scripte in diesem Kapitel bauen eine Bindung an den LDAPProvider oder den GC-Provider auf. Anmerkung: Bei den Providernamen wird zwischen Groß- und Kleinbuchstaben unterschieden. Achten daher Sie besonders beim Provider WinNT auf die korrekte Schreibweise. Die Bindung an ein Verzeichnis unterscheidet sich bei den unterschiedlichen Providern. Sie beginnt jedoch immer mit dem Namen des Providers, gefolgt von einem Doppelpunkt und zwei Slash-Zeichen. Danach muss der Pfad zu dem Verzeichnis angegeben werden, zu dem Sie die Bindung aufbauen möchten. Wie bereits weiter oben in diesem Kapitel erklärt, handelt es sich beim ADsPfad (AdsPath) um den Namen des Providers und den Pfad zum Verzeichnisobjekt. Beispiele für ADsPfade sind: •
LDAP-Provider mit dem Pfad zu einem Domänencontroller der Domäne na.fabrikam.com: LDAP://dc=NA,dc=fabrikam,dc=com. Beachten Sie, dass in diesem Beispiel kein bestimmter Domänencontroller angegeben wird sondern nur der Name der Domäne selbst. Diese Art der Bindung wird serverlose Bindung genannt.
•
GC-Provider mit Pfad zu einem globalen Katalog-Server der Domäne Seite 313 von 394
na.fabrikam.com: GC://dc=NA,dc=fabrikam,dc=com. • • • •
WinNT-Provider mit Pfad zu einem Domänencontroller der Domäne na.fabrikam.com: WinNT://NA. IIS-Provider mit Pfad zum IIS-Server sea-dc-01.na.fabrikam.com: IIS://sea-dc-01.na.fabrikam.com. NDS-Provider mit Pfad zum Verzeichnisserver der Domäne na.fabrikam.com: NDS://server01/o=org01/dc=com/dc=fabrikam/dc=na. NWCOMPAT-Provider mit Pfad zu einem Bindery-Server: NWCOMPAT://server01.
Der Provider ADSI OLE DB verwendet dieselbe Syntax für den ADsPfad wie die Provider LDAP und GC - nur mit einigen weiteren Parametern. Diese Parameter definieren die zurückzugebenden Suchergebnisse. Tipp: Verwenden Sie den rootDSE, damit Sie keine DNs in den ADsPfad eintragen müssen. Es gibt noch einen besonderen Provider mit dem Namen ADSI-Namespace. Mit diesem Provider können Sie eine Liste aller anderen Provider zurückgeben. Script 5.54 demonstriert dies. Script 5.54: Eine Liste der installierten ADSI-Provider anzeigen 1Set objProvider = GetObject("ADs:") 2For Each Provider In objProvider 3 Wscript.Echo Provider.Name 4Next
Wenn Sie das Script ausführen, erhalten Sie eine Liste der installierten ADSI-Provider. Die folgende Ausgabe wurde zum Beispiel auf einem Computer unter Windows 2000 Professional generiert: WinNT: NWCOMPAT: NDS: LDAP: IIS:
Vorsicht: Auch wenn es möglich ist zumindest Teile der Active Directory-Objekte über den Provider WinNT zu verwalten, ist dies nicht empfehlenswert. Die Verwendung dieses Providers kann schwerwiegende Auswirkungen auf die Leistung und die Fehleranfälligkeit des Scripts haben. Router Wenn ein Script die Funktion GetObject verwendet, dann erhält der ADSI-Router eine Anfrage. Er identifiziert den Provider der angefordert wurde, sucht in der Registrierung nach Informationen über diesen Provider und lädt diesen dann in den Speicher (der Provider wird anhand der angegeben ProgID identifiziert). Der Provider übergibt das angeforderte Element später an den Router und dieser erstellt das ADSI-Objekt, das an das Script übergeben wird.
Seite 314 von 394
Eigenschafts-Zwischenspeicher Gleich nach dem Aufbau der Bindung wird ein Speicherbereich für das virtuelle Active Directory-Objekt reserviert. Das Objekt und seine Attribute werden jedoch noch nicht aus Active Directory heruntergeladen. ADSI-Anwendung Dies ist die oberste Schicht im ADSI-Schichtmodell. Die Scripte aus diesem Kapitel sind Beispiele für ADSI-Anwendungen - ebenso das Snap-In ADSI-Edit.
ADSI-Schnittstellen Jede Schnittstelle stellt Methoden zur Verfügung. Die Methoden führen entweder Aktionen aus oder geben Informationen über ein Objekt zurück. Methoden, die Aktionen ausführen, werden auch Methoden genannt. Im Gegensatz dazu werden Methoden, die einfach nur Informationen zurückgeben, Eigenschaften oder Attribute genannt. Ob eine Schnittstelle zur Verfügung steht oder nicht, hängt vom verwendeten Provider ab. Der LDAP-Provider implementiert die Schnittstelle IADsADSystemInfo zum Beispiel nicht, da diese speziell zur Abfrage von Informationen über Clientcomputer dient - der LDAPProvider fragt seine Informationen aber von Domänencontrollern ab. Außerdem verwenden Provider nicht unbedingt alle Methoden einer Schnittstelle. Der LDAPProvider implementiert zum Beispiel die Methode CopyHere der Schnittstelle IADsContainer nicht. Eine Liste der von den Providern unterstützten Schnittstellen finden Sie unter dem Link Active Directory Programmer's Guide unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig). Suchen Sie dort nach dem Text "Provider Support of ADSI Interfaces." Kategorisierung von Schnittstellen Die Kategorisierung der Schnittstellen vereinfacht die Suche nach der passenden Schnittstelle für eine bestimmte Aufgabe. In Tabelle 5.6 sehen Sie die Schnittstellenkategorien, eine allgemeine Beschreibung zu den Kategorien und die Schnittstellen in diesen Kategorien. Tabelle 5.6: Schnittstellen, die der LDAP-Provider implementiert Kategorie
Verwendung durch den LDAPProvider
Schnittstellen
Core
Allgemeine Schnittstelle für die Interaktion mit fast jedem Objekt eines Verzeichnisses.
IADs, IADsContainer, IADsNamespaces, IADsOpenDSObject
Schema
Zur Verwaltung und Erweiterung des Schemas.
IADsClass, IADsProperty, IADsSyntax
Property Cache
Zur Interaktion mit dem lokalen Zwischenspeicher.
IADsPropertyEntry, IADsPropertyList, IADsPropertyValue, IADsPropertyValue2
Persistent Object
Zur Interaktion mit bestimmten Objekttypen.
IADsGroup, IADsLocality, IADsMembers, IADsO, IADsOU, IADsPrintQueue, IADsUser Seite 315 von 394
Kategorie
Verwendung durch den LDAPProvider
Schnittstellen
Dynamic Object
Zur Interaktion mit den veröffentlichten Druckern.
IADsPrintQueueOperations
Security
Zur Interaktion mit DACLs von Objekten.
IADsAccessControlEntry, IADsAccessControlList, IADsSecurityDescriptor
NonLow-Level-Zugriff auf Objekte. IDirectoryObject, IDirectorySearch Automation Stehen über Scriptsprachen nicht zur Verfügung. Extension
Zum Hinzufügen von Methoden zu IADsExtension bestehenden Objekten.
Utility
Hilfsfunktionen.
Data Type
Verarbeitung von Attributen, die als IADsLargeInteger Datentyp Large Integer (64-Bit) gespeichert sind.
IADsDeleteOps, IADsPathname, IADsObjectOptions
LDAP-Provider-Objekte und deren Schnittstellen Indem die Schnittstellen nach den von ihnen unterstützen Objekten in Gruppen eingeteilt werden, ist es einfacher, die Methoden und Eigenschaften eines Objektes festzustellen. Eine Tabelle der ADSI-Objekttypen des DAP-Providers finden Sie unter dem Link Active Directory Programmer's Guidehttp://www.microsoft.com/windows/reskits/webresources. Suchen Sie dort nach dem Text "ADSI Objects of LDAP." Tabellen zu den anderen Providern finden Sie, indem Sie nach den Texten "ADSI Objects for WinNT", "ADSI Objects for NDS" und "ADSI Objects of NWCOMPAT" suchen. Eigenschaften von IAD Die Schnittstelle IAD stellt sechs schreibgeschützte Eigenschaften zur Verfügung, über die Sie jedes Objekt im Verzeichnis identifizieren können. •
AdsPath - gibt einen String mit dem vollqualifizierten Pfad des Objekts zurück
•
Class - gibt einen String mit dem Namen der Schemaklasse des Objekts zurück.
•
GUID - gibt einen String mit der GUID (Globally Unique Identifier) des Objekts zurück.
•
Name - gibt einen String mit dem RDN eines Objekts zurück.
•
Parent - gibt einen String mit dem ADsPfad des übergeordneten Objekts zurück.
•
Schema - gibt einen String mit der Schemaklasse des Objekts zurück.
Script 5.55 zeigt mit Hilfe der IAD-Eigenschaften Informationen über die Domäne na.fabrikam.com an. Script 5.55: Abfragen von Informationen über die IAD-Eigenschaften 1Set objDomain = GetObject("LDAP://dc=NA,dc=fabrikam,dc=com")
Seite 316 von 394
2Wscript.Echo 3Wscript.Echo 4Wscript.Echo 5Wscript.Echo 6Wscript.Echo 7Wscript.Echo
"ADsPath:" & objDomain.ADsPath "Class:" & objDomain.Class "GUID:" & objDomain.GUID "Name:" & objDomain.Name "Parent:" & objDomain.Parent "Schema:" & objDomain.Schema
Wenn Sie das Script ausführen, erhalten Sie die folgende Ausgabe: ADsPath:LDAP://dc=NA,dc=fabrikam,dc=com Class:domainDNS GUID:618a1da520255a41ab778f5dc1d8da4d Name:dc=NA Parent:LDAP://dc=fabrikam,dc=com Schema:LDAP://schema/domainDNS
Eigenschaften von IADsContainer IADsContainer stellt nur eine schreibgeschützte Eigenschaft zur Verfügung. •
Filter - gibt die Klassen der gefilterten Objekte zurück.
Script 5.56 demonstriert die Verwendung der Eigenschaft Filter. Es listet die Inhalte der OU HR auf. Script 5.56: Anzeigen der Eigenschaft Filter 1Set objOU = GetObject("LDAP://OU=HR,dc=NA,dc=fabrikam,dc=com") 2ObjOU.Filter= Array("Computer", "User") 3Wscript.Echo "Filter:" 4For each strObject in objOU.Filter Wscript.Echo vbTab & strObject 5 6Next
Wenn Sie das Script ausführen, erhalten Sie die folgende Ausgabe: Filter: Computer User
Weitere Informationen über die Methoden von IADsContainer finden Sie in den entsprechenden Abschnitten dieses Kapitels: •
Filter - Auflistender Active Directory-Objekte in einem Container
•
Create - Erstellen von Active Directory-Objekten
•
Delete - Löschen von Active Directory-Objekten
•
MoveHere - Verschieben und Umbenennen von Objekten
Scripting-Konzepte und -Technologien zur Systemadministration - Kapitel 6 - WMIScripting Veröffentlicht: 26. Apr 2004 Seite 317 von 394
(Engl. Originaltitel: WMI Scripting Primer) Bei WMI (Windows Management Instrumentation) handelt es sich um die wichtigste Verwaltungstechnologie der Microsoft Windows-Betriebssysteme. Mit WMI ist eine konsistente und einheitliche Verwaltung, Steuerung und Überwachung der einzelnen Systeme im gesamten Unternehmen möglich. WMI basiert auf Industriestandards und ermöglicht es Systemadministratoren, die Konfiguration von Arbeitsstationen, Servern, Anwendungen, Netzwerken und anderen Komponenten abzufragen, zu ändern und zu überwachen. Scripte können die WMI-Script-Bibliothek verwenden.
WMI-Überblick Ein grundlegendes Verständnis von WMI ist zur Systemadministration mindestens genauso wichtig wie das Wissen um Active Directory und ADSI. WMI stellt ein konsistentes Modell einer verwalteten Umgebung zur Verfügung. Für jede verwaltbare Ressource gibt es eine entsprechende WMI-Klasse. Sie können sich eine WMI-Klasse als kurze Beschreibung der Eigenschaften einer verwalteten Ressource und der Aktion, die WMI zur Verwaltung der Ressource durchführen kann, vorstellen. Anmerkung: Was ist eine 'verwaltete Ressource'? Im Zusammenhang mit diesem Kapitel ist eine verwaltete Ressource jedes Objekt, das über WMI verwaltet werden kann (Hardware, Software, Benutzerkonten, usw.). Überlegen Sie sich, wie Sie Arbeitsstationen und Server bis jetzt verwalten und überwachen. Sie verwenden eine Vielzahl an administrativen Werkzeugen, um diverse unterschiedliche Ressourcen zu verwalten (zum Beispiel Festplatten, Ereignisprotokolle, Dateien, Ordner, Dateisystem, Netzwerkkomponenten, Betriebssystemeinstellungen, Leistungsdaten, Drucker, Prozesse, Registrierungseinstellung, Dienste, Freigaben, Benutzer und Gruppen). Wenn Sie das WMI-Modell und dessen Anwendung verstanden haben, dann können Sie diese Komponenten alle über die WMI-Scripting-Bibliothek verwalten. Die Scripting-Bibliothek ermöglicht Ihnen die Arbeit mit WMI-Klassen und den entsprechenden verwalteten Ressourcen. Sie können also die unterschiedlichsten Ressourcen über einen gemeinsamen Weg verwalten. WMI entspricht einem Industriestandard der DMTF (Distributed Management Task Force). Die DMTF ist eine Organisation, die mit Herstellern von Schlüsseltechnologien (inklusive Microsoft) und Standardisierungsgremien zusammenarbeitet, um übergreifende Verwaltungslösungen zu entwickeln. Die Architektur von WMI basiert auf den Ideen der WBEM-Initiative (Web-Based Enterprise Management) der DMTF. WMI wurde 1998 das erste Mal veröffentlicht und als Komponente über das Service Pack 4 für Microsoft® Windows NT® bereitgestellt. WMI ist inzwischen ein integraler Bestandteil aller Windows-Betriebssysteme - inklusive Microsoft Windows 2000, Microsoft® Windows® XP und Windows Server 2003. Die Möglichkeiten von WMI Mit dem WSH und VBScript können Sie WMI-Scripte für die folgenden Bereiche schreiben: •
Verwaltung von Computern unter Windows Server 2003, Windows XP Professional und Windows 2000 Mit WMI-Scripten können Sie Ereignisprotokolle, Dateisystem, Drucker, Prozesse, Seite 318 von 394
Registrierungseinstellung, geplante Tasks, Sicherheitseinstellung, Dienste, Freigaben und eine Vielzahl anderer Komponenten verwalten. •
Netzwerke Sie können zum Beispiel Dienste wie DNS, clientseitige Einstellungen (zum Beispiel die TCP/IP-Einstellungen) und SNMP-Geräte verwalten.
•
Echtzeitüberwachung Sie können zum Beispiel das Auftreten von Ereignissen, die Leistungsanzeige, das Dateisystem oder die Registrierung überwachen.
•
Serveranwendungen Sie können zum Beispiel MOM-, SMS-, Exchange- und SQL-Server überwachen.
In einigen Fällen stellt WMI ihnen die gleichen Funktionalitäten wie ein Kommandozeilenwerkzeug oder eine GUI-Anwendung zur Verfügung. In anderen Fällen kann WMI Aufgaben ausführen, die über keinen anderen Weg durchführbar sind. Vor WMI war es zum Beispiel nicht möglich, über ein Script die Menge des installierten RAMs abzufragen - zumindest nicht ohne ein Drittanbieter-Tool. Mit WMI haben Sie die Möglichkeit, das installierte RAM von jedem Computer abzufragen, auf dem Sie über die nötigen administrativen Rechte verfügen. Script 6.1 demonstriert dies. Anmerkung: Das Script sieht zugegebenermaßen auf den ersten Blick nicht ganz einfach aus. Sie werden jedoch feststellen, dass es sich beim größten Teil des Scripts um Bausteine handelt, die Sie in jedem WMI-Script unverändert wieder verwenden können. Script 6.1: Abfragen und Anzeigen des installierten Hauptspeichers 1 strComputer = "." 2 3 Set objSWbemServices = GetObject("winmgmts:\\" & strComputer) 4 Set colSWbemObjectSet = _ 5 objSWbemServices.InstancesOf("Win32_LogicalMemoryConfiguration") 6 7 For Each objSWbemObject In colSWbemObjectSet 8 Wscript.Echo "Physikalischer Speicher in KB: " & _ 9 objSWbemObject.TotalPhysicalMemory 10Next
Wenn Sie das Script unter CScript ausführen, dann zeigt es den installierten Hauptspeicher in KB an. Die Ausgabe sieht zum Beispiel so aus: Total Physical Memory (kb): 261676
Wie arbeitet das Script nun? Wenn Sie auf die fett geschriebenen Teile des Scripts achten, dann werden Sie feststellen, dass das Script zwei Aufgaben ausführt: 1.Es verbindet sich mit einer WMI-Klasse mit dem Namen Win32_LogicalMemoryConfiguration. WMI-Klassen repräsentieren die verwaltbaren Ressourcen eines Computers. Wie der Name schon andeutet, ermöglicht es Ihnen die Klasse Win32_LogicalMemoryConfiguration Informationen über den Hauptspeicher abzufragen. 2.Es gibt den Wert einer Eigenschaft mit dem Namen TotalPhysicalMemory aus. WMI-Klassen haben Eigenschaften. Die Klasse Win32_LogicalMemoryConfiguration hat zum Beispiel eine Eigenschaft, über die Sie den installierten Hauptspeicher abfragen Seite 319 von 394
können. Die Eigenschaften der Klassen entsprechen normalerweise den Eigenschaften der Ressourcen, die durch die Klasse repräsentiert werden. Festplatten haben zum Beispiel Eigenschaften wie Leseköpfe (Heads), Sectoren (Sectors) und Zylinder (Cylinders). Daher verfügt auch die Klasse Win32_DiskDrive über Eigenschaften wie TotalHeads, TotalSectors und TotalCylinders. Zusätzlich zum physikalischen Speicher gibt es unter Windows-basierten Computern den virtuellen Speicher. Es ist daher nicht wirklich überraschend, dass die Klasse Win32_LogicalMemoryConfiguration eine Eigenschaft mit dem Namen TotalVirtualMemory zur Verfügung stellt. Wie Sie die Größe des virtuellen Speichers abfragen können, sehen Sie in Script 6.2. Der einzige wirkliche Unterschied zu Script 6.1 ist das fett geschriebene Element. Script 6.2: Abfragen und Anzeigen des virtuellen Speichers 1 strComputer = "." 2 3 Set objSWbemServices = GetObject("winmgmts:\\" & strComputer) 4 Set colSWbemObjectSet = _ 5 objSWbemServices.InstancesOf("Win32_LogicalMemoryConfiguration") 6 7 For Each objSWbemObject In colSWbemObjectSet 8 Wscript.Echo "Virtueller Speicher in KB: " & _ 9 objSWbemObject.TotalVirtualMemory 10Next
WMI kann natürlich für mehr als nur zur Rückgabe von Informationen über den Speicher eines Computers verwendet werden. Script 6.3 fragt zum Beispiel die Namen, den Status und den Starttyp aller installierten Dienste ab. Script 6.3: Abfragen und Anzeigen von Informationen über Dienste 1 strComputer = "." 2 3 Set objSWbemServices = GetObject("winmgmts:\\" & strComputer) 4 Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service") 5 6 For Each objSWbemObject In colSWbemObjectSet 7 Wscript.Echo " Name: " & objSWbemObject.DisplayName & vbCrLf & _ " Status: " & objSWbemObject.State & vbCrLf & _ 8 9 " Starttyp: " & objSWbemObject.StartMode 10Next
Wenn Sie das Script unter CScript ausgeführt wird, dann erhalten Sie eine Ausgabe wie die folgende (nur Teile der Ausgabe): Name: Windows-Zeitgeber Status: Running Starttyp: Auto Name: Webclient Status: Running Starttyp: Auto Name: Windows-Verwaltungsinstrumentation Status: Running Starttyp: Auto Name: Dienst für Seriennummern der tragbaren Medien Status: Stopped Starttyp: Manual Name: Treibererweiterungen für Windows-Verwaltungsinstrumentation
Seite 320 von 394
Status: Stopped Starttyp: Manual Name: WMI-Leistungsadapter Status: Stopped Starttyp: Manual Name: Automatische Updates Status: Running Starttyp: Auto Name: Konfigurationsfreie drahtlose Verbindung Status: Running Starttyp: Auto
Wenn Sie sich Script 6.3 genauer ansehen, dann werden Sie zwei Dinge bemerken: •
•
Statt der Klasse Win32_LogicalMemoryConfiguration verwendet das Script eine Klasse mit dem Namen Win32_Service. Warum? Weil das Script Informationen über Dienste zurückgeben soll und nicht über den Hauptspeicher. Wenn das Script zum Beispiel Informationen über einen Monitor zurückgeben würde, dann würde es die Klasse Win32_DesktopMonitor verwenden. Mit der verwalteten Ressource ändert sich also auch die verwendete Klasse. Die abgefragten Eigenschaften unterscheiden sich von den Eigenschaften in den vorherigen Scripten. Warum? Die Klasse Win32_Service stellt natürlich andere Eigenschaften als die Klasse Win32_LogicalMemoryConfiguration zur Verfügung. Eine Klasse stellt immer die Eigenschaften zur Verfügung, die auch die verwaltete Ressource hat, die durch die Klasse repräsentiert wird.
Wenn Sie nun ein Muster erkennen, dann haben Sie bereits einen großen Schritt beim Erlernen von WMI-Scripting hinter sich. WMI-Scripte fragen die Informationen über die verwalteten Ressourcen fast alle gleich ab. Es ändern sich nur die Namen der Klassen und deren Eigenschaften. Wie Sie in diesem Kapitel sehen werden, arbeiten WMI-Scripts normalerweise in drei Schritten: 1.Sie verbinden Sich mit dem WMI-Dienst. 2.Sie fragen Informationen über WMI-Klassen ab. 3.Sie verarbeiten die Informationen auf irgendeine Weise weiter (zum Beispiel werden diese ausgegeben). Alle WMI-Scripte folgen diesem Muster. Stellen Sie sich zum Beispiel vor, Sie möchten ein Script schreiben, das die Einträge aus dem Ereignisprotokoll abfragt und anzeigt. Mit dem Scriptcode aus Script 6.1 können Sie ein solches Script ganz schnell erstellen - Script 6.4 demonstriert dies. Anmerkung: Bevor Sie Script 6.4 ausführen, sollten Sie sich bewusst sein, dass es möglicherweise sehr lange zur Ausführung benötigt - abhängig davon, wie viele Einträge im Ereignisprotokoll vorhanden sind. Script 6.4: Abfragen und Anzeigen von Ereignissen 1 strComputer = "." 2 3 Set objSWbemServices = GetObject("winmgmts:\\" & strComputer)
Seite 321 von 394
4 Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_NTLogEvent") 5 For Each objSWbemObject In colSWbemObjectSet 6 Wscript.Echo _ "Protokolldatei: " & objSWbemObject.LogFile & vbCrLf & _ 7 8 "Nummer: " & objSWbemObject.RecordNumber & vbCrLf & _ 9 "Typ: " & objSWbemObject.Type & vbCrLf & _ 10 "Uhrzeit: " & objSWbemObject.TimeGenerated & vbCrLf & _ "Quelle: " & objSWbemObject.SourceName & vbCrLf & _ 11 12 "Kategorie: " & objSWbemObject.Category & vbCrLf & _ 13 "Kateogrie-Name: " & objSWbemObject.CategoryString & vbCrLf & _ 14 "Ereignis: " & objSWbemObject.EventCode & vbCrLf & _ "Benutzer: " & objSWbemObject.User & vbCrLf & _ 15 16 "Computer: " & objSWbemObject.ComputerName & vbCrLf & _ 17 "Nachricht: " & objSWbemObject.Message & vbCrLf 18Next
Die WMI-Architektur Die ersten Seiten dieses Kapitels sollten einen wichtigen Punkt herausstellen. WMI-Scripting muss nicht schwer sein - es kann sogar sehr einfach sein. Wie Sie gesehen haben, können Sie bereits über eine einzelne Vorlage und geringe Änderungen Hunderte von Scripten zur Abfrage einer riesigen Menge von Informationen erstellen. Einige wichtige Probleme wurden jedoch bis jetzt verschwiegen. Sie werden uns zum Beispiel zustimmen, wenn wir sagen, dass Sie über die Klasse Win32_NTLogEvent Ereignisse aus dem Ereignisprotokoll abfragen können. Woher wissen Sie aber, dass es eine Klasse mit dem Namen Win32_NTLogEvent gibt? Oder woher wissen Sie, dass es eine Klasse mit dem Namen Win32_Service und den Eigenschaften Name, Description und State gibt? Sie können Hunderte von unterschiedlichen Scripten aus dem ersten Script erstellen - jedoch nur, wenn Sie wissen, welche Klassen und Eigenschaften Sie verwenden können. Tatsächlich ist das Schreiben des Scripts der einfache Teil von WMI - der schwere Teil ist es, herauszufinden, was über WMI verwaltet werden kann und was nicht. Hierbei helfen Ihnen die aufgabenbasierten Abschnitte dieses Kapitels. Im Abschnitt Dateien und Ordner lesen Sie zum Beispiel mehr über die entsprechenden WMI-Klassen (und deren Methoden und Eigenschaften) - was aber, wenn Sie Grafikkarten, Monitore oder Netzwerkkarten verwalten wollen? Leider gibt es in diesem Buch nicht für alle verwaltbare Ressourcen einen entsprechenden Abschnitt. Heißt das, Sie können nur die im Buch beschriebenen Ressourcen verwalten? Natürlich nicht - wenn Sie wissen wie WMI funktioniert, dann können Sie alle Bereiche von WMI nutzen. Wenn Ihnen klar ist, wo und wie WMI Informationen speichert, dann können Sie die vorhin gestellten Fragen ganz einfach beantworten. Welche Klassen stehen mir zur Verfügung? Wie sind die Namen dieser Klassen? Welche Eigenschaften und Methoden kann ich über die Klasse verwenden? Dieser und der folgende Abschnitt beschäftigen sich mit dem Common Information Model (CIM) und beschreiben die WMI-Architektur. Als erstes sehen wir uns die drei grundlegenden WMI-Schichten an (Abbildung 6.1): • •
Konsumenten WMI-Infrastuktur
Seite 322 von 394
•
Verwaltete Ressourcen
Dieser Abschnitt schließt mit einer Einführung in die WMI-Sicherheit. Hierbei handelt es sich zwar nicht um eine WMI-Schicht, es ist jedoch trotzdem wichtig zu wissen, wie Sicherheit unter WMI funktioniert - und zwar bevor Sie anfangen, Scripte zu schreiben.
Abbildung 6.1: Die WMI-Architektur
Verwaltete Ressourcen Die grundlegende Schicht der WMI-Architektur bilden die verwalteten Ressourcen. Eine verwaltete Ressource ist jede logische oder physikalische Komponente, die über WMI zugreifbar und verwaltbar ist. Bei diesen Ressourcen handelt es sich unter anderem um: Computer, Festplatten, Peripheriegeräte, Ereignisprotokolle, Dateien, Ordner, Dateisysteme, Netzwerkkomponenten, Betriebssystem-Subsysteme, Leistungsindikatoren, Drucker, Prozesse, Registrierungseinstellungen, Sicherheit, Dienste, Freigaben, SAM-Benutzer und Gruppen, Active Directory, Windows-Installer, WDM-Gerätetreiber und SNMP. Eine WMI-Ressource kommuniziert mit WMI über einen Provider. Für das virtuelle Gegenstück einer verwalteten Ressource wird oft der Begriff Instanz verwendet. Entsprechend dem WMI-SDK ist eine Instanz 'ein Repräsentant eines physikalischen Objekts, das zu einer bestimmten Klasse gehört'. Sehen Sie sich zum Beispiel Script 6.5 an. Es gibt die Laufwerksbuchstaben für alle logischen Laufwerke eines Computers zurück. Script 6.5: Abfragen und Anzeigen der logischen Laufwerke 1strComputer = "."
Seite 323 von 394
2 3Set objSWbemServices = GetObject("winmgmts:\\" & strComputer) 4Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_LogicalDisk") 5 6For Each objSWbemObject In colSWbemObjectSet 7 Wscript.Echo objSWbemObject.DeviceID 8Next
Abhängig von den logischen Laufwerken des Computers sieht die Ausgabe des Scripts ungefähr so aus: A: C: D: E:
Jeder dieser Laufwerksbuchstaben stellt zwei Dinge dar: Erstens ein tatsächliches logisches Laufwerk und zweitens eine Instanz der Klasse Win32_LogicalDiskDrive. Anmerkung: Was ist, wenn es keine Instanzen einer Klasse gibt? Nehmen wir an, dass Sie versuchen Informationen über die Bandlaufwerke eines Computers abzufragen - der Computer hat jedoch keine Bandlaufwerke. Wird dies zu einem Fehler führen? Nein letztendlich ist Win32_TapeDrive eine gültige WMI-Klasse. Es ist völlig unbedeutend, ob es tatsächlich Bandlaufwerke (oder Instanzen der entsprechenden Klasse) gibt.
Die WMI-Infrastruktur Die WMI-Infrastruktur ist die Vermittlungsschicht im WMI-Architekturmodell. WMI setzt sich aus drei primären Komponenten zusammen: dem CIMOM (Common Information Model Object Manager, auch WMI-Dienste genannt), dem CIM-Repository (Common Information Model, auch WMI-Repository genannt) und WMI-Providern. Zusammen bilden diese drei Komponenten die WMI-Infrastruktur. Anmerkung: Eine weitere Komponente - die WMI-Scriptbibliothek - wird weiter unten in diesem Kapitel besprochen.
WMI-Provider WMI-Provider arbeiten als Vermittler zwischen dem CIMOM und der verwalteten Ressource. Provider fordern Informationen von den verwalteten Ressourcen an und geben Informationen an diese weiter. Script 6.1 und Script 6.3 verwenden zum Beispiel den Provider Win32, um Informationen über den Hauptspeicher und die Dienste abzufragen. Script 6.4 verwendet hingegen den Provider Event Log. Provider verbergen die Implementierungsdetails für die einzelnen verwalteten Ressourcen über ein standardbasiertes und einheitliches Zugriffsmodell. WMI-Provider kommunizieren mit ihren jeweiligen verwalteten Ressourcen über die nativen APIs (Application Programming Interfaces) der verwalteten Ressource. Mit CIMOM kommunizieren die WMIProvider über die WMI-Programmierschnittstelle. Der Provider Event Log ruft zum Zugriff auf die Ereignisprotokolle zum Beispiel die Win32-EventLog-APIs auf. Warum müssen Sie das wissen? Um eine Anwendung zu erstellen, die Windows-Subsysteme verwaltet, werden normalerweise die Win32-APIs verwendet. Ohne WMI müssten Sie diese Seite 324 von 394
APIs selbst aufrufen. Hierbei gäbe es zwei Probleme: Erstens können Win32-APIs nicht über ein Script aufgerufen werden - daher müssen Sie eine Programmiersprache wie C++ oder Visual Basic verwenden. So gehen Ihnen jedoch alle Vorteile von Scripten verloren (schnelle Entwicklung, keine Entwicklungsumgebung notwendig). Und zweitens funktionieren nicht alle Win32-APIs auf die gleich Art und Weise. Wenn Sie die EventLog-APIs beherrschen, dann heißt das noch lange nicht, dass Sie die Service-APIs genauso verwenden können. Dies ist eine der grundlegenden Schwierigkeiten bei der Programmierung mit Windows-APIs. WMI-Provider lösen diese Probleme. Sie müssen sich nicht um den Aufruf der Win32-APIs kümmern - dies erledigt WMI für Sie - und Sie müssen auch nicht die Unterschiede zwischen den einzelnen APIs kennen. Stattdessen verwenden Sie die konsistenten WMI-Befehle, und WMI übersetzt diese für Sie in API-Aufrufe. WMI wendet sich natürlich nicht nur an Systemadministratoren. Auch Softwareentwickler können die WMI-Architektur verwenden. Ein Beispiel hierfür ist der Exchange Server 2000Provider. Er überwacht den Status des Exchange-Connectors. Auch viele andere Serverprodukte stellen WMI-Provider zur Verfügung (zum Beispiel MOM-, SMS-, IIS- und SQL-Server). Providers werden generell als DLLs (Dynamic-Link Libraries) implementiert. Diese DLLs befinden sich im Verzeichnis systemroot\System32\Wbem. WMI stellt unter Windows 2000, Windows XP und Windows Server 2003 bereits eine Menge Provider zur Verfügung - diese Provider werden auch Standardprovider genannt. Einige dieser Standardprovider sehen Sie in Tabelle 6.1. Tabelle 6.1: Einige WMI-Standardprovider Provider
DLL
Namespace
Active Directory
dsprov.dll
root\directory\ldapStellt Active Directory-Objekte über WMI zur Verfügung
Event Log
ntevt.dll
root\cimv2
Verwaltet das Windows-Ereignisprotokoll
Performance wbemperf.dllroot\cimv2 Counter
Stellt einen Zugriff auf Leistungsdaten zur Verfügung
Registry
stdprov.dll
Liest, schreibt, überwacht und erstellt Registrierungseinträge
SNMP
snmpincl.dll root\snmp
Stellt einen Zugriff auf SNMP-MIB-Daten und Trap-Meldungen zur Verfügung
WDM
wmiprov.dll root\wmi
Stellt einen Zugriff auf WDM-Treiber zur Verfügung
Win32
cimwin32.dll root\cimv2
Stellt Informationen über Computer, Festplatten, Peripheriegeräte, Dateien, Ordner, Dateisysteme, Netzwerkkomponenten, Betriebssystem, Drucker, Prozesse, Sicherheit, Dienste, Freigaben, SAM-Benutzer und Gruppen und vieles weitere zur Verfügung
Windows
msiprov.dll root\cimv2
Stellt einen Zugriff auf Informationen über
root\default
Beschreibung
Seite 325 von 394
Provider
DLL
Installer
Namespace
Beschreibung
installierte Software zur Verfügung
Windows Server 2003 und Windows XP bieten noch viele weitere Standardprovider. Eine vollständige Liste finden Sie im WMI Software Developers Kit (SDK). Informationen zum WMI-SDK finden Sie unter dem Link Microsoft Windows Management Instrumentation (WMI) SDK unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig).
CIMOM Das CIMOM handhabt die Interaktion zwischen Konsumenten und Providern. Alle WMIAnfragen und der gesamte Datenfluss werden über den CIMOM durchgeführt. Wenn Sie ein WMI-Script starten, dann wird das Script an CIMOM umgeleitet. Der CIMOM verarbeitet die Anfrage des Scripts jedoch nicht direkt. Nehmen wir zum Beispiel an, das Script fragt eine Liste der installierten Dienste ab. Der CIMOM ruft die Liste der Dienste in diesem Fall nicht für Sie ab - er ruft stattdessen den entsprechenden WMI-Provider auf, und dieser ruft dann die Liste ab. Wenn die Liste abgefragt ist, dann wird Sie an den CIMOM übergeben. Dieser leitet sie dann an das Script weiter. Der CIMOM wird unter Windows XP und Windows Server 2003 über den WMI-Dienst (winmgmt.exe) bereitgestellt. Der WMI-Dienst wird unter dem Prozess svchost.exe ausgeführt. Anmerkung: Unter Windows 2000 und Windows NT 4.0 SP4 wird der WMI-Dienst als separater Prozess ausgeführt. Unter Windows ME, Windows® 98 und Windows® 95 OSR 2.5 wird WMI als Standardprozess ausgeführt. Der WMI-Dienst entspricht den meisten Betriebssystemdiensten. Er kann zum Beispiel angehalten und gestartet werden: net stop winmgmt net start winmgmt
Wenn der Dienst angehalten ist und ein Script oder eine Anwendung aufgeführt wird, die den Dienst benötigt, dann wird der Dienst automatisch neu gestartet. Der CIMOM stellt die folgenden Dienste für die WMI-Infrastruktur zur Verfügung: •
Registrierung von Providern - Der CIMOM speichert die Informationen zu den Providern im CIM-Repository.
•
Routing von Anfragen - Der CIMOM verwendet die gespeicherten Provider-Informationen, um Konsumentenanfragen an den entsprechenden Provider weiterzuleiten.
•
Remotezugriff - Anfragen von Remote-Konsumenten werden vom CIMOM verarbeitet.
•
Sicherheit - Der CIMOM kontrolliert den Zugriff auf die verwalteten Ressourcen, indem er das Zugrifftoken des Benutzers überprüft.
•
Abfrageverarbeitung - Erlaubt es einem Konsumenten Abfragen gegen verwaltete Ressourcen über die WMI Query Language (WQL - WMI-Abfragesprache) durchzuführen.
•
Ereignisverarbeitung - Erlaubt es einem Konsumenten auf Ereignisse einer verwalteten Ressource zu reagieren. Seite 326 von 394
Das CIM-Repository Die Grundidee von WMI ist, dass die Konfiguration und die Verwaltung von unterschiedlichen Ressourcen über ein einheitliches Schema erfolgt. Das CIM-Repository speichert dieses Schema - es wird auch Objekt-Repository oder Klassenspeicher genannt. Das Schema definiert alle über WMI zur Verfügung stehenden Daten und basiert auf dem Common-Information-Model-Standard der DMTF. Genau wie das Active Directory-Schema ist das CIM auf Klassen aufgebaut. Eine Klasse ist eine Blaupause einer über WMI verwaltbaren Ressource. Im Gegensatz zu Active DirectoryKlassen, die statische Objekte vorgeben, stellen CIM-Klassen normalerweise dynamische Ressourcen dar. Die Instanzen der Ressourcen werden nicht im CIM-Repository gespeichert, sondern auf Anfrage eines Konsumenten dynamisch abgefragt. Das bedeutet, dass der Begriff Repository (Speicher) im Bezug auf das CIM nicht ganz richtig ist. Auch wenn das CIM ein Repository ist und auch statische Daten speichert, so ist sein primärer Zweck doch die Speicherung von Blaupausen für die verwalteten Ressourcen. Der Grund hierfür ist einfach: Der Status der meisten über WMI verwalteten Ressourcen ändert sich regelmäßig. WMI-Klassen können zum Beispiel dazu verwendet werden, Ereignisse des Ereignisprotokolls abzufragen. Genau wie Active Directory-Klassen sind auch CIM-Klassen hierarchisch organisiert. Untergeordnete Klassen erben Eigenschaften der übergeordneten Klassen. Der DMTF pflegt eine Gruppe von allgemeinen Basisklassen, von denen System- und Anwendungsentwickler (zum Beispiel Microsoft) system- oder anwendungsspezifische Klassen ableiten können. Die Klasse Win32_Process ist zum Beispiel von der Klasse CIM_Process abgeleitet (diese ist wiederum von den Klassen CIM_LogicalElement und CIM_ManagedSystemElement abgeleitet). Klassen sind in Namespaces gruppiert - logische Gruppen für bestimmte Bereiche. Der Namespace root\cimv2 enthält zum Beispiel Klassen, die allgemeine Ressourcen eines Computers oder eines Betriebssystems repräsentieren. Die Klassen, die in den bisherigen Scripten verwendet wurden (Win32_LogicalMemoryConfiguration, Win32_Service und Win32_NTLogEvent) befinden sich im Namespace root\cimv2 - sie sind jedoch nur drei von Hunderten von Klassen in den unterschiedlichen CIM-Namespaces. Anmerkung: Auch für die WMI-Sicherheit sind Namespaces wichtig. Dieses Thema wird weiter unten in diesem Kapitel besprochen. CIM-Klassen enthalten Eigenschaften und Methoden. Eigenschaften beschreiben die Konfiguration und den Status einer über WMI verwalteten Ressource. Methoden sind ausführbare Funktionen, die bestimmte Aktionen für die über WMI verwalteten Ressourcen ausführen. Unter Windows 2000 und Windows NT 4.0 SP4 ist das CIM unter systemroot\System32\Wbem\Respository\cim.rep gespeichert. Unter Windows Me, Windows 98 und Windows 95 OSR 2. ist das CIM unter %windir%\System\Wbem\Repository\cim.rep gespeichert. Unter Windows XP und Windows Server 2003 ist das CIM im Ordner systemroot\System32\Wbem\Repository\FS gespeichert und setzt sich aus den folgenden Dateien zusammen: Seite 327 von 394
•
Index.btr - Binary-tree (btree) Index
•
Index.map - Transaktionskontrolldatei
•
Objects.data - CIM-Repository
•
Objects.map - Transaktionskontrolldatei
Auch wenn das CIM auf objektorientierten Prinzipien basiert, müssen Sie trotzdem kein Experte im Schema-Design werden. Es ist jedoch wichtig, dass Sie die grundlegenden Strukturen und die Organisation des CIM-Repository verstanden haben.
WMI-Konsumenten Bei den WMI-Konsumenten handelt es sich um die letzte Schicht der WMI-Infrastruktur. Ein Konsument kann ein Script, eine Anwendung, eine Webanwendung oder ein anderes Tool sein, das auf WMI zugreift. Ein solches Script muss nicht kompliziert sein. Das folgende dreizeilige Script ist ein Beispiel für einen WMI-Konsumenten. Es fragt den verfügbaren Speicherplatz auf Laufwert C ab: Set objSWbemServices = GetObject("winmgmts:") Set objDisk = objSWbemServices.Get("Win32_LogicalDisk.DeviceID='C:'") Wscript.Echo objDisk.FreeSpace
WMI-Sicherheit WMI ist eine Technologie, die für die Systemadministration extrem nützlich ist. Scripte können genauso einfach gegen Remotecomputer wie gegen den lokalen Computer ausgeführt werden. Außerdem benötigen Sie zum Entwickeln von WMI-Scripten nicht mehr als einen Texteditor. WMI wird zwar so zur perfekten Technologie zur Systemadministration, doch leider gibt es noch eine weitere Gruppe, die WMI nutzen kann - Hacker. Wie schwer wäre es zum Beispiel, ein Script zu schreiben, das alle Computer in Ihrer Organisation einen nach dem anderen herunterfährt? Wenn wir ganz ehrlich sind, dann wäre es ganz einfach, ein solches Script zu schreiben. Dieses Script jedoch auszuführen wird deutlich schwerer. Das liegt daran, dass die Sicherheit ein wichtiger Teil der WMI-Infrastruktur ist. WMI wurde so entworfen, dass solche Aktivitäten, wie die oben beschriebenen, verhindert werden. Stellen Sie sich zum Beispiel vor, ein Hacker versucht über WMI einen Ihrer Computer herunterzufahren. Dieser Versuch wird fehlschlagen. Warum? Weil nur Administratoren ein Script gegen einen Remotecomputer ausführen können. Solange der Hacker kein Administrator ist, ist er nicht in der Lage, einen Computer über WMI herunterzufahren (wenn der Hacker Administrator ist, dann kann er auch ohne ein Script eine Menge Probleme verursachen). Was ist, wenn ein Hacker das Script per E-Mail an einen Benutzer schickt und diesen dazu bringt, das Script auszuführen? Auch das wird nicht funktionieren. Ein WMI-Script, das etwas tut, benötigt immer administrative Rechte. In den meisten Organisationen verfügen die Benutzer jedoch nicht über solche Rechte. WMI wird immer unter den Rechten der Person ausgeführt, die das Script gestartet hat. WMI-Sicherheit ist eine Erweiterung der anderen Windows-Sicherheits-Subsysteme und setzt sich aus den folgenden Komponenten zusammen: Seite 328 von 394
• • •
WMI-Namespace-Sicherheit DCOM-Sicherheit (Distributed COM) Standard Windows-Betriebssystem-Sicherheit
WMI-Namespace-Sicherheit Bevor ein Benutzer sich mit WMI verbinden kann - egal ob auf einem lokalen Computer oder einem Remotecomputer, - wird das Zugriffstoken des Benutzers auf die entsprechenden Rechte überprüft. Die benötigten Rechte sind im CIM-Repository gespeichert. Standardmäßig hat die Gruppe Administratoren einen Vollzugriff auf WMI und das gesamte CIM-Repository - sowohl auf dem lokalen Computer als auch auf Remotecomputern. Alle anderen Benutzer haben über die Gruppe Jeder die Rechte Konto aktivieren, Methoden ausführen und Anbieterschreibzugriff auf dem lokalen Computer. In Tabelle 6.2 sehen Sie die verfügbaren WMI-Berechtigungen - diese können Sie über die Registerkarte Sicherheit des Snap-Ins Windows-Verwaltungsinfrastruktur (WMI) (systemroot\System32\Wmimgmt.msc) konfigurieren. Anmerkung: Unter Windows NT 4.0 SP4, Windows 98 und Windows 95 OSR 2.5 müssen Sie die Anwendung Wbemcntl.exe verwenden. Sie finden diese unter Windows NT 4.0 SP4 im Ordner systemroot\System32\Wbem. Tabelle 6.2: WMI-Namespace-Berechtigungen Berechtigung
Beschreibung
AdministratorenJeder
Methoden ausführen
Ermöglicht das Ausführen von Methoden, die von den WMI-Klassen oder -Instanzen exportiert wurden.
x
x
Vollständiger Ermöglicht umfassenden Schreibzugriff Lese-/Schreib-/Löschzugriff auf alle WMI-Objekte, -Klassen und -Instanzen.
x
EingeschränkterErmöglicht den SchreibSchreibzugriff zugriff auf statische WMI-Objekte.
x
AnbieterErmöglicht den Schreibschreibzugriff zugriff auf von Anbietern bereitgestellte Objekte.
x
x
Konto aktivieren
Ermöglicht den Lesezugriff auf WMI-Objekte.
x
x
Remoteaktivierung
Ermöglicht den Remotezugriff auf den Namespace.
x
Sicherheit lesen
Ermöglicht den schreibx geschützten Zugriff auf WMI-Sicherheitsinformationen. Seite 329 von 394
Berechtigung
Beschreibung
AdministratorenJeder
Sicherheit bearbeiten
Ermöglicht den Lese- und Schreibzugriff auf WMISicherheitsinformationen
x
WMI-Berechtigungen werden auf Namespace-Ebene auf alle Klassen im entsprechenden Namespace zugewiesen - sie gelten damit auch für alle untergeordneten Namespaces die Einstellungen von dem entsprechenden Namespace erben. Standardmäßig werden Berechtigungen nur dem Namespace root zugewiesen und an alle untergeordneten Namespaces vererbt. Eine Sicherheitsprüfung wird dann durchgeführt, wenn sich ein Benutzer mit dem CIMOM verbindet. Daher werden Berechtigungsänderungen nur dann aktiv, wenn der Benutzer eine neue Verbindung aufbaut.
DCOM-Sicherheit DCOM ist die Architektur, über die die WMI-Scripting-Bibliothek mit dem WMI-Dienst zusammenarbeitet. Sie stellt einen Mechanismus zur Verfügung, der Impersonation genannt wird (Personifizierung - auch mit dem Begriff 'Ausführen als' bezeichnet - zur Vereinfachung wird in diesem Text der Begriff Personifizierung verwendet). Die Personifizierung legt fest, unter welchen Benutzerrechten der WMI-Dienst ein Script ausführt. Standardmäßig führt der WMI-Dienst Script unter den Anmeldeinformationen des angemeldeten Benutzers aus - dies ist auch die empfohlene Einstellung. Sie können dem WMI-Dienst das Recht geben, über Ihre Anmeldeinformationen auf andere DCOM-basierte Dienste zuzugreifen. Dieser Vorgang wird Delegierung genannt - mit ihm sind jedoch einige Risiken verbunden. Was für Risiken sind das? Standardmäßig unterstützt DCOM nur eine 'EinzelschrittPersonifizierung'. Stellen Sie sich zum Beispiel vor, Sie führen ein Script auf Computer A aus, das Informationen von Computer B abfragen soll. Das Script kann für den 'einzelnen Schritt' von Computer A zu Computer B eine Personifizierung durchführen. Was passiert jedoch, wenn Computer B Informationen von einem dritten Computer abfragen muss? Standardmäßig kann das Script für diesen 'Doppelschritt' keine Personifizierung mehr durchführen. Es schlägt daher fehl. Es ist natürlich möglich, Computer B ebenfalls zu erlauben, Ihre Anmeldeinformationen zu verwenden - auch den Computer D, E und G können Sie dies erlauben. Hier tritt jedoch das Sicherheitsrisiko zu tage. Bei zwei Computern können Sicherheitsprobleme auf die beiden Computer beschränkt werden. Bei vielen Computern sind diese auch alle von Sicherheitsproblemen betroffen. Die unterschiedlichen DCOM-Sicherheitsebenen werden weiter unten in diesem Kapitel im Abschnitt WMI-Scripte erstellen besprochen. Dort erfahren Sie auch mehr zu entsprechenden Sicherheitsmaßnahmen.
Windows-Betriebssystem-Standardsicherheit Zusätzlich zu den WMI- und DCOM-spezifischen Sicherheitseinstellung verwendet WMI auch die Standardsicherheitseinstellung des Betriebssystems. Nehmen wird zum Beispiel einmal an, jemand wird durch die Sicherheitseinstellungen eines Ordners daran gehindert, in den Ordner zu schreiben. Wenn diese Person diesen Vorgang über ein WMI-Script Seite 330 von 394
auszuführen versucht, dann gelten die Sicherheitseinstellungen des Ordners auch für das Script. Anmerkung: Was passiert, wenn das Script trotzdem Schreiboperationen in diesem Ordner durchführen muss? In diesem Fall müssen Sie entweder die NTFS-Berechtigungen des Ordners ändern, oder das Script unter einem Benutzerkonto ausführen, das die entsprechenden Rechte besitzt.
Das Common Information Model (CIM) Wenn Sie ein Haus bauen möchten, dann benötigen Sie jemanden, der die Baupläne lesen und interpretieren kann. Wenn Sie ein elektronisches Gerät bauen möchten, dann benötigen Sie jemanden, der sich mit Schaltplänen auskennt. Wenn Sie WMI-Scripte schreiben möchten, dann müssen Sie wissen, wie Sie mit dem WMI-Blaupausen arbeiten: dem CIM-Repository. Das CIM-Repository ist das WMI-Schema - in ihm sind alle Klassendefinitionen für alle Ressourcen gespeichert, die über WMI verwaltet werden können. Um die Wichtigkeit von CIM und den CIM-Klasse zu unterstreichen, sehen Sie sich Script 6.6 und Script 6.7 an. Script 6.6 ist eine erweiterte Version von Script 6.3 - es gibt Informationen über die installierten Dienste zurück. Script 6.6: Abfragen von Informationen über die installierten Dienste 1 strComputer = "." 2 3 Set objSWbemServices = GetObject("winmgmts:\\" & strComputer) 4 Set colServices = objSWbemServices.InstancesOf("Win32_Service") 5 6 For Each objService In colServices 7 Wscript.Echo "Name: " & objService.Name & vbCrLf & _ 8 "Angez. Name: " & objService.DisplayName & vbCrLf & _ 9 "Beschreibung: " & objService.Description & vbCrLf & _ 10 "Pfadname: " & objService.PathName & vbCrLf & _ 11 "Starttyp: " & objService.StartMode & vbCrLf & _ 12 "Status: " & objService.State & vbCrLf 13Next
Script 6.7 ist eine weitere Variante des Scripts. Es verwendet dieses Mal jedoch die Klasse Win32_OperatingSystem. Wie Sie vielleicht erkennen, gibt es Informationen über das installierte Betriebssystem zurück. Script 6.7: Abfragen von Informationen über das installierte Betriebssystem 1 strComputer = "." 2 3 Set objSWbemServices = GetObject("winmgmts:\\" & strComputer) 4 Set colOperatingSystems = _ 5 objSWbemServices.InstancesOf("Win32_OperatingSystem") 6 7 For Each objOperatingSystem In colOperatingSystems 8 Wscript.Echo "Name: " & objOperatingSystem.Name & vbCrLf & _ " & objOperatingSystem.Caption & vbCrLf & _ 9 "Caption: 10 "CurrentTimeZone:" & objOperatingSystem.CurrentTimeZone & vbCrLf & _ 11 "LastBootUpTime: " & objOperatingSystem.LastBootUpTime & vbCrLf & _
Seite 331 von 394
12 "LocalDateTime: " & objOperatingSystem.LocalDateTime & vbCrLf & _ 13 "Locale: " & objOperatingSystem.Locale & vbCrLf & _ 14 "Manufacturer: " & objOperatingSystem.Manufacturer & vbCrLf & _ 15 "OSType: " & objOperatingSystem. OSType & vbCrLf & _ 16 "Version: " & objOperatingSystem.Version & vbCrLf & _ 17 "ServicePack: " & objOperatingSystem.ServicePackMajorVersion & _ 18 "." & objOperatingSystem.ServicePackMinorVersion & vbCrLf & _ 19 "Windows Dir: " & objOperatingSystem.WindowsDirectory 20Next
Es gibt zwischen diesen beiden Scripten zwei Unterschiede: der verwendete Klassenname und die Eigenschaften der Klassen. Das erste Script verwendet zum Beispiel die Eigenschaften DisplayName, StartMode und State - das zweite Script fragt die Eigenschaften LastBootUpTime, Version und ServicePackMajorVersion ab. Dass Sie das gleiche Script verwenden können, um so unterschiedliche Informationen abzufragen, verdeutlicht die wichtige Rolle von CIM-Klassen. Wenn Sie ein Script erstellt haben, können Sie über dieses Scripte viele verschiedene Informationen abfragen. Zu wissen, wie die Klassen und deren Eigenschaften heißen, ist jedoch nur ein Teil der Arbeit. Bevor Sie WMI voll ausnutzen können, müssen Sie etwas mehr über die Struktur des CIM-Repository und der WMI-Klassen wissen. Und zwar aus zwei Gründen. • •
Wenn Sie das CIM-Repository kennen, wird es Ihnen leichter fallen, die Ressourcen eines Computers oder einer Software zu identifizieren. Wenn Sie die Klassen kenn, werden Sie leichter feststellen können, welche Aufgaben Sie über WMI ausführen können.
Beide Gründe sind unabhängig vom verwendeten WMI-Werkzeug. Egal ob Sie ein WMIScript oder eine andere Anwendung nutzen - Sie müssen wissen wie das CIM-Repository aufgebaut ist und wie die WMI-Klassen anzuwenden sind. Ein genauso wichtiger Grund ist, dass das CIM-Repository eine hervorragende Dokumentation der verfügbaren verwalteten Ressourcen ist. Wenn Sie Informationen über eine WMI-Klasse benötigen, können Sie natürlich das WMI-SDK verwenden. Was machen Sie aber, wenn Sie zum Beispiel wissen möchten, ob eine bestimmte Klasse, Methode oder Eigenschaft unter einer bestimmten Windows-Version unterstützt wird? In einem solchen Fall können Sie das CIM-Repository abfragen. Nehmen wird einmal an, Sie haben sich das folgende Script aus dem Script Center im Microsoft TechNet besorgt: Const JOIN_DOMAIN = 1 Const ACCT_CREATE = 2 Set objNetwork = CreateObject("Wscript.Network") strComputer = objNetwork.ComputerName Set objComputerSystem = GetObject _ ("winmgmts:{impersonationLevel=Impersonate}!\\" & strComputer & _ "\root\cimv2:Win32_ComputerSystem.Name='" & strComputer & "'") ReturnValue = objComputerSystem.JoinDomainOrWorkGroup _ ("FABRIKAM", "password", "FABRIKAM\shenalan", NULL, JOIN_DOMAIN+ACCT_CREATE)
Sie möchten wissen, ob das Script unter Windows 2000 ausgeführt werden kann. Wie sich herausstellt kann es nicht ausgeführt werden - und zwar, da die Klasse Win32_ComputerSystem unter Windows 2000 keine Methode JoinDomainOrWorkGroup zur Verfügung stellt. Die Methode wurde erst unter Windows XP hinzugefügt. Seite 332 von 394
Aber wie finden Sie das heraus - außer über das Ausprobieren des Scripts? Ein Weg wäre die Verwendung der WMI-Tools, die weiter unten in diesem Kapitel im Abschnitt Das CIMRepository erkunden beschrieben werden. Ein deutlich flexiblerer Ansatz ist jedoch die Verwendung der WMI-Scripting-Bibliothek. Eine nützliche Eigenschaft von WMI ist die Tatsache, dass Sie mit der Scripting-Bibliothek Informationen über WMI selbst abfragen können. Sie können zum Beispiel WMI-Scripte schreiben, die alle Namespaces und Klassen im CIM-Repository ausgeben. Sie können auch ein Script schreiben, das alle installierten Provider ausgibt.
Blaupausen WMI vereinheitlicht die Konfiguration und Verwaltung von Ressourcen über das CIMRepository - dem Schema von WMI. Stellen Sie sich das Schema als Blaupause oder Modell vor, das für ein echtes Objekt steht. In Abbildung 6.2 sehen Sie eine konzeptuelle Ansicht der internen Struktur und Organisation des CIM-Repository. Wie Sie sehen verwendet CIM für das Datenmodell Klassen. Es enthält jedoch weit mehr Klassen als im Diagramm zu sehen. In Abbildung 6.2 werden drei wichtige CIM-Konzepte gezeigt: 1.Das CIM-Repository ist in mehrere Namespaces aufgeteilt. 2.Jeder Namespace kann eine oder mehrere der folgenden Klassengruppen enthalten: • •
Systemklassen Kernklassen oder allgemeine Klassen
3.Es gibt vier primäre Klassentypen: abstrakt, statisch und dynamisch. •
• • •
Eine abstrakte Klasse ist eine Vorlage von der neue abstrakte oder nicht-abstrakte Klassen abgeleitet werden. Sie kann nicht zur Erstellung von Instanzen verwalteter Objekte verwendet werden. Eine statische Klasse definiert im CIM-Repository gespeicherte Daten. Normalerweise handelt es sich hierbei um die WMI-Konfiguration und operative Daten. Eine dynamische Klasse ist eine Klasse, die zur Erstellung neuer Instanzen verwalteter Ressourcen verwendet wird. Eine assoziative Klasse ist eine abstrakte, statische oder dynamische Klasse, die eine
Seite 333 von 394
Abbildung 6.2: Strukturelle Ansicht des CIM-Repository (WMI-Schema)
Namespaces CIM-Klassen sind in Namespaces organisiert. Namespaces sind Partitionierungsmechanismen, mit denen das CIM den Bereich und die Sichtbarkeit von Klassendefinitionen steuert. Jeder Namespace im CIM enthält eine logische Gruppe mit den Klassen zu einer bestimmten Technologie oder zu einem bestimmten Bereich der Verwaltung. Namespaces sind das gleiche wie Ordner in einem Laufwerk. Wie Ordner bieten auch Namespaces einen Platz, um zusammengehörige Informationen zu speichern. Ein Ordner mit dem Namen Scripts enthält wahrscheinlich Scripte - ein Namespace mit dem Namen MicrosoftActiveDirectory enthält wahrscheinlich Informationen über Active Directory. So wie Sie nur eine Datei mit dem Namen C:\Scripts\WMI_Script.vbs haben können, so können Sie auch nur eine Klasse mit dem Namen root\cimv2:Win32_Process haben.
Seite 334 von 394
Anmerkung: Ein Unterschied zwischen Ordner und den WMI-Namespaces ist, dass Ordner oft tief verschachtelt sind. Namespaces sind hingegen sind selten mehr als drei Ebenen tief. Die meisten Klassen zur Systemadministration befinden sich zum Beispiel unter root\cimv2 also schon in der zweiten Ebene. Alle Klassen in einem Namespace müssen einen eindeutigen Klassennamen haben. Klassen in einem Namespace können nicht von Klassen in einem anderen Namespace abgeleitet werden. Daher finden Sie in unterschiedlichen Namespaces identische Systemklassen, Kernklassen und allgemeine Klassen. Die meisten Klassen zur Verwaltung von Ressourcen befinden sich im Namespace root\cimv2. root\cimv2 ist jedoch nicht der einzige Namespace, auf den Sie achten müssen. Der Provider Registry speichert seine Klassendefinitionen zum Beispiel im Namespace root\default. Einen Namespace angeben Jedes WMI-Script verbindet sich im ersten Schritt mit einem Namespace. Die folgende Codezeile verbindet das Script zum Beispiel mit dem Namespace root\cimv2 auf dem lokalen Computer (die Verbindung wird deshalb mit dem lokalen Computer aufgebaut, weil kein Computername angegeben ist): Set objSWbemServices = GetObject("winmgmts:root\cimv2")
Eine Verbindung wird auch dann hergestellt, wenn kein Namespace im Verbindungs-String angegeben wird: Set objSWbemServices = GetObject("winmgmts:")
Wenn keine Namespace definiert wurde, dann verbindet sich das Script mit dem StandardNamespace. Dieser Standard-Namespace wird im folgenden Registrierungschlüssel definiert: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WBEM\Scripting\Default Namespace Wenn Sie bei der Abfrage von verwalteten Ressourcen keinen Namespace angeben, sucht WMI die Klassendefinition nur im Standard-Namespace. Wenn dieser dort nicht zu finden ist, kommt es zum Fehler WBEM_E_INVALID_CLASS (0x80041010). Anmerkung: Verwechseln Sie nicht den Standard-Namespace mit dem Namespace root\DEFAULT. Solange Sie den Standard-Namespace nicht auf root\DEFAULT setzen, haben die beiden nichts mit einander zu tun. Normalerweise ist der Namespace root\cimv2 der Standard-Namespace. Er kann jedoch auch ganz einfach geändert werden. Um sicherzustellen, dass Ihr Script korrekt ausgeführt wird, sollten Sie daher immer einen Namespace angeben. Das folgende Codestück zeigt, wie Sie einen Namespace angeben. Es verwendet außerdem die Variable strComputer, um den Computernamen anzugeben, gegen den das Script ausgeführt wird: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Anmerkung: Warum wurde der Wert von strComputer auf "." gesetzt? Bei WMI-Scripting ist der erste Teil eines Objektpfads immer der Name des Computers. Wenn kein Computername oder nur ein Punkt angegeben wird, dann wird das Script gegen den lokalen Computer ausgeführt. Durch die Variable, die auf den Wert "." gesetzt wird, erreichen Sie Seite 335 von 394
zwei Dinge: Das Script wird gegen den lokalen Computer ausgeführt und Sie haben die Möglichkeit, einen anderen Computer einzutragen und das Script so ganz einfach gegen einen Remotecomputer auszuführen. Konfigurieren des Standard-Namespace Sie können die WMI-Scripting-Bibliothek zusammen mit der Klasse Win32_WMISetting verwenden, um den Standard-Namespace auszulesen und zu ändern. Script 6.8 und Script 6.9 demonstrieren dies. Bei Win32_WMISetting handelt es sich um eine dynamische Klasse mit operativen Parametern des WMI-Dienstes. Über die Eigenschaft ASPScriptDefaultNamespace können Sie auf den Standard-Namespace zugreifen. Script 6.8 verwendet die bereits bekannten drei Schritte: verbinden, abfragen und anzeigen. Wie oben empfohlen, wird beim Verbinden ein Namespace für die Klasse Win32_WMISetting angegeben. Script 6.8: Abfragen des Standard-Namespace 1 strComputer = "." 2 3 Set objSWbemServices = _ 4 GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 5 Set colWMISettings = objSWbemServices.InstancesOf("Win32_WMISetting") 6 7 For Each objWMISetting in colWMISettings 8 Wscript.Echo "Der Standard-Namespace ist: " & _ objWMISetting.ASPScriptDefaultNamespace 9 10Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: Der Standard-Namespace ist: root\cimv2
Um den Standard-Namespace zu ändern, können Sie Script 6.8 erneut verwenden - mit einer wichtigen Änderung: Statt die Eigenschaft zu lesen, gehen Sie folgendermaßen vor: 1.Sie schreiben einen neuen Wert in die Eigenschaft. 2.Sie rufen die Methode SWbemObject.Put_ auf, um die Änderung an die WMI-Ressource zu übermitteln. Diese Aufgabe wird in einer For-Each-Schleife durchgeführt. Und zwar darum, weil die Methode InstancesOf immer eine Collection zurückgibt. Dies passiert auch dann, wenn es nur eine Instanz der Ressource gibt (so wie bei der Klasse Win32_WMISetting). Script 6.9: Konfigurieren des Standard-Namespace 1 strComputer = "." 2 3 Set objSWbemServices = _ 4 GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 5 Set colWMISettings = objSWbemServices.InstancesOf("Win32_WMISetting") 6 7 For Each objWMISetting in colWMISettings 8 objWMISetting.ASPScriptDefaultNamespace = "root\cimv2" 9 objWMISetting.Put_ 10Next
Abrufen von Namespaces Seite 336 von 394
Über WMI-Scripting können Sie nicht nur Informationen über Ressourcen abrufen, sondern auch Informationen über das CIM. Die einzige Änderung zu den vorherigen Scripten ist in diesem Fall der verwendete Klassenname. Namespace-Informationen werden im CIM als statische Instanzen der Klasse __NAMESPACE gespeichert. Im Gegensatz zu verwalteten Ressourcen, bei denen die Informationen über einen Provider abgerufen werden, werden die Informationen von Instanzen statischer Klassen direkt im CIM gespeichert und von dort auch ohne einen WMIProvider abgerufen. Script 6.10 verwendet die Klasse __NAMESPACE, um alle Namespaces direkt unter dem Namespace root abzurufen. Script 6.10: Abrufen der Namespaces direkt unter root 1strComputer = "." 2 3Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root") 4Set colNameSpaces = objSwbemServices.InstancesOf("__NAMESPACE") 5 6For Each objNameSpace In colNameSpaces 7 Wscript.Echo objNameSpace.Name 8Next
Wenn Sie das Script unter Windows 2000 ausführen, erhalten Sie die folgende Ausgabe: DEFAULT SECURITY CIMV2 WMI directory
Die Liste der Namespaces variiert je nach der verwendeten Windows-Version und der installierten WMI-Version. Auch WMI-Anwendungen beeinflussen die Liste. Unter Windows XP mit installiertem Microsoft® Office XP und dem .NET Framework erhalten Sie zum Beispiel die folgende Liste: SECURITY RSOP Cli WMI CIMV2 MSAPPS10 Policy Microsoft DEFAULT directory subscription NetFrameworkv1
Script 6.10 gibt nicht alle Namespaces auf dem Computer aus. Es zeigt nur die Namespaces an, die sich direkt unter dem Namespace root befinden. Um alle Namespaces anzuzeigen, müssen Sie das Script verändern. Wie Script 6.11 zeigt, ist dies jedoch nicht so schwer, wie Sie vielleicht denken. Script 6.10 wird einfach in ein rekursives Script umgewandelt. Hierzu wird der Hauptteil des Scripts in eine Subroutine verschoben. Das Script geht folgendermaßen vor: 1.Es initialisiert die Variable strComputer mit dem Namen des Zielcomputers. 2.Es ruft die Subroutine EnumNameSpaces auf und übergibt ihr den Start-Namespace root. Der Inhalte der Subroutine EnumNameSpaces ist mit Script 6.10 identisch - mit einer Seite 337 von 394
wichtigen Ausnahme: 1.Die Subroutine gibt als erstes das Argument aus, mit dem sie aufgerufen wurde: strNameSpace. Die Variable strNameSpace definiert den Namespace, der bei jedem Aufruf der Subroutine im Verbindungs-String verwendet wird. Beim ersten Aufruf der Subroutine enthält strNameSpace den Wert "root". 2.Sie verwendet die VBScript-Funktion GetObject, um eine Verbindung mit dem Namespace aufzubauen, der in der Variable strNameSpace angegeben ist. 3.Nach dem Verbindungsaufbau fragt die Subroutine alle Namespaces direkt unter dem Namespace ab, mit dem die Verbindung aufgebaut wurde. 4.Die Subroutine verwendet eine For-Each-Schleife, um die Liste der direkten UnterNamespaces aufzulisten. Statt die Namen der Namespaces jedoch auszugeben, ruft sich die Subroutine selbst mit dem jeweiligen Namen des Unter-Namespaces auf. 3.Die Schritte a bis d werden so lange durchgeführt, bis alle Namespaces aufgelistet sind. Script 6.11: Abfragen aller CIM-Namespaces 1 strComputer = "." 2 Call EnumNameSpaces("root") 3 4 Sub EnumNameSpaces(strNameSpace) 5 Wscript.Echo strNameSpace 6 Set objSWbemServices = _ 7 GetObject("winmgmts:\\" & strComputer & "\" & strNameSpace) 8 Set colNameSpaces = objSWbemServices.InstancesOf("__NAMESPACE") 9 For Each objNameSpace In colNameSpaces 10 Call EnumNameSpaces(strNameSpace & "\" & objNameSpace.Name) 11 Next 12End Sub
Unter Windows 2000 Advanced Server erhalten Sie die folgende Ausgabe: root root\DEFAULT root\SECURITY root\CIMV2 root\CIMV2\Applications root\CIMV2\Applications\MicrosoftIE root\CIMV2\ms_409 root\WMI root\directory root\directory\LDAP root\directory\LDAP\ms_409 root\MicrosoftNLB root\MicrosoftNLB\ms_409
Klassenkategorien Wie Sie in Abbildung 6.2 gesehen haben, gibt es drei generelle Kategorien von Klassen: system, Kern und allgemein und erweitert. Systemklassen
Seite 338 von 394
Systemklassen sind Klassen, die die interne WMI-Konfigurationen zur Verfügung stellen zum Beispiel die Namespace-Konfiguration, die Namespace-Sicherheit, die Registrierung von Providern und die Ereignisbenachrichtigung. Wenn Sie sich das CIM anschauen, dann können Sie die Systemklassen leicht an den beiden Unterstrichen erkennen. Systemklassen sind entweder von Typ abstrakt oder statisch. Abstrakte Systemklassen sind Vorlagen zum Ableiten (oder Definieren) anderer abstrakter oder statischer Systemklassen. Statische Systemklassen definieren die WMI-Konfiguration im CIM -Repository. Die Systemklasse __Win32Provider stellt zum Beispiel Informationen zur Providerregistrierung zur Verfügung. Wie bereits mit der Systemklasse __NAMESPACE demonstriert, können Sie WMI nutzen, um die statischen Instanzen der Systemklassen abzufragen. Script 6.12 fragt zum Beispiel alle Instanzen der Klasse __Win32Provider im Namespace root\cimv2 ab. Script 6.12: Abfragen der im Namespace root\comv2 registrierten Win32-Provider 1strComputer = "." 2Set objSWbemServices = _ 3 GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 4Set colWin32Providers = objSWbemServices.InstancesOf("__Win32Provider") 5 6For Each objWin32Provider In colWin32Providers Wscript.Echo objWin32Provider.Name 7 8Next
Wenn Sie das Script unter Windows 2000 ausführen, erhalten Sie die folgende Ausgabe: CIMWin32 WBEMCORE MS_Power_Management_Event_Provider MS_NT_EVENTLOG_PROVIDER MS_NT_EVENTLOG_EVENT_PROVIDER SECRCW32 MSIProv NT5_GenericPerfProvider_V1
Wahrscheinlich werden Sie Systemklassen eher selten verwenden - mit einer Ausnahme: WMI-Überwachungsscripts abbonieren bestimmte WMI-Ereignisse, um bei deren Auftreten benachrichtigt zu werden. Dieses Thema wird jedoch später in diesem Kapitel besprochen. Kernklassen und allgemeine Klassen Diese Klassen haben zwei Aufgaben. Erstens sind sie die abstrakten Klassen, von denen System- und Softwareentwickler technologiespezifische Erweiterungsklassen ableiten können. Zweitens definieren Sie Ressourcen im Bezug auf bestimmte Bereiche der Verwaltung, die nicht von einer bestimmen Technologie oder Implementierung abhängen. Theoretisch sind dies Ressourcen, die jedes Betriebssystem unterstützt. Die DMTF definiert einen Satz an Kernklassen und allgemeinen Klassen. Diesen können Sie über den Prefix CIM_ prefix erkennen. Von den 275 Kernklassen und allgemeinen Klassen im Namespace root\cimv2 sind fast alle abstrakte Klassen. Da Sie keine Instanzen von abstrakten Klassen erstellen können, werden Sie diese eher selten verwenden. Sie werden normalerweise nur von System- und Softwareentwicklern genutzt. Vier der 275 Klassen sind dynamische Klassen. Diese vier Klassen sind CIM_DataFile, CIM_DirectoryContainsFile, CIM_ProcessExecutable und CIM_VideoControllerResolution. Seite 339 von 394
Sie können zum Abrufen von Informationen über Ressourcen verwendet werden. Das folgende Script gibt zum Beispiel Informationen über alle möglichen Auflösungen der Grafikkarte zurück: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colVideoControllers = _ objSWbemServices.InstancesOf("CIM_VideoControllerResolution") For Each objVideoController In colVideoControllers Wscript.Echo objVideoController.HorizontalResolution Wscript.Echo objVideoController.VerticalResolution Wscript.Echo objVideoController.NumberOfColors Wscript.Echo objVideoController.RefreshRate Next
Erweiterungsklassen Erweiterungsklassen sind technologiespezifische Klassen, die von System- und Softwareentwicklern verwendet werden. Erweiterungsklassen von Microsoft sind zum Beispiel Win32_BaseService, Win32_Service, Win32_SystemServices und Win32_ComputerSystem. Die Erweiterungsklassen von Microsoft im Namespace root\cimv2 erkennen Sie am Prefix Win32_. Gehen Sie jedoch nicht davon aus, dass alle Microsoft-Erweiterungsklassen mit Win32_ beginnen - dies ist nicht der Fall. Die Klasse StdRegProv in root\DEFAULT hält sich zum Beispiel nicht an diese Regel. In der aktuellsten Version von WMI gibt es im Namespace root\cimv2 463 Erweiterungsklassen. Von den 463 Win32-Klassensind 68 abstrakte Klassen und 395 dynamische. Die meisten Klassen, die Sie in Ihren Scripten verwenden werden, sind Erweiterungsklassen. Anmerkung: Die tatsächliche Anzahl der Klassen kann von vielen verschiedenen Faktoren abhängen - zum Beispiel von der Windows-Version und der installierten WMI-Version. Auflisten der Klassen in einem Namespace Sie können ein Script schreiben, dass alle Klassen in einem bestimmen Namespace auflistet. Script 6.13 demonstriert dies, indem es alle Klassen im Namespace root\cimv2 auflistet. Im Gegensatz zu den vorherigen Scripten, die die Methode SWbemServices.InstancesOf verwendet haben, verwendet das Script jedoch die Methode SubclassesOf. Wie der Name schon sagt, gibt die Methode SubclassesOf alle untergeordneten Klassen einer bestimmten Klasse oder eines bestimmten Namespaces zurück. Wie InstancesOf gibt auch SubclassesOf die Unterklassen als Collection zurück. Jedes Element in der Collection ist eine Klasse. Ein neues Element in Script 6.13 ist die Eigenschaft objClass.Path_.Path. Sie wird in der ForEach-Schleife verwendet. Wie in den anderen Scripten dieses Kapitels, geht die Schleife alle Elemente der von der Methode SubclassesOf zurückgegebenen Collection durch. Im Gegensatz zu den vorherigen Scripten ist Path_ jedoch eine Eigenschaft von SWbemObject. Um dies zu verstehen, müssen Sie sich überlegen, in welchem Kontext das Script SWbemObject verwendet. In diesem Fall greift es nicht auf eine Instanz einer Seite 340 von 394
verwalteten Ressource zu (zum Beispiel ein Dienst, ein Prozess, etc.), sondern auf die Klassendefinition der verwalteten Ressource. Wenn Sie mit SWbemObject auf eine Instanz einer verwalten Ressourcen zugreifen, dann greifen Sie auf die Eigenschaften und Methoden zu, die über die Klassendefinition für die verwaltete Ressource definiert werden. Wenn Sie SWbemObject jedoch verwenden, um Informationen über die Klasse selbst abzurufen, dann verwenden Sie die Eigenschaften und Methoden von SWbemObject selbst - Path_ ist eine solche Eigenschaft. Path_ ist eine Referenz auf eine andere WMI-Scripting-Bibliothek mit dem Namen SWbemObjectPath. Diese stellt die Eigenschaft Path zur Verfügung. Die Eigenschaft SWbemObjectPath.Path enthält den vollqualifizierten Pfad zur Klasse, die von SWbemObject referenziert wird (objClass in Script 6.13). Script 6.13: Abrufen aller Klassen im Namespace root\cimv2 1strComputer = "." 2 3Set objSWbemServices = _ 4 GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 5Set colClasses = objSWbemServices.SubclassesOf() 6 7For Each objClass In colClasses 8 Wscript.Echo objClass.Path_.Path 9Next
Wenn Sie das Script unter Windows 2000 ausführen, erhalten Sie eine Liste mit 636 Einträgen. Ein Teil der Ausgabe sieht so aus: \\ATL-WIN2K-01\ROOT\CIMV2:Win32_NTEventlogFile \\ATL-WIN2K-01\ROOT\CIMV2:Win32_NTLogEvent \\ATL-WIN2K-01\ROOT\CIMV2:Win32_NTLogEventLog \\ATL-WIN2K-01\ROOT\CIMV2:Win32_NTLogEventUser \\ATL-WIN2K-01\ROOT\CIMV2:Win32_NTLogEventComputer \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SID \\ATL-WIN2K-01\ROOT\CIMV2:Win32_AccountSID \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SecuritySetting \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SecuritySettingOfObject \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SecuritySettingOwner \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SecuritySettingGroup \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SecuritySettingAccess \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SecuritySettingAuditing \\ATL-WIN2K-01\ROOT\CIMV2:Win32_Trustee \\ATL-WIN2K-01\ROOT\CIMV2:Win32_ACE \\ATL-WIN2K-01\ROOT\CIMV2:Win32_SecurityDescriptor \\ATL-WIN2K-01\ROOT\CIMV2:Win32_LogicalFileSecuritySetting
Sie können Script 6.13 zur Auflistung der Klassen anderer Namespaces anpassen, indem Sie einfach den Ziel-Namespace im Script ändern. Um nach Klassen zu suchen, können Sie das Script zusammen mit dem Befehl findstr.exe verwenden. Findstr.exe ist ein Kommandozeilentool, mit dem Sie in Dateien nach Text suchen können. Nehmen wir beispielsweise einmal an, Sie möchten wissen, ob Ihre Windows-Version die neue Windows XP-Klasse Win32_TSSessionSetting unterstützt. Mit dem folgenden Befehl können Sie feststellen, ob die Klasse im Namespace root\cimv2 vorhanden ist. Das Script schickt seine Ausgabe an Findstr.exe. Findstr.exe sucht dann in der Ausgabe nach dem Text "Win32_TSSessionSetting". cscript GetClasses.vbs |findstr /I "win32_tssessionsetting" Seite 341 von 394
Wenn Sie den Befehl unter Windows XP ausführen, erhalten Sie die folgende Ausgabe: \\ATL-WINXP-01\ROOT\cimv2:Win32_TSSessionSettingError \\ATL-WINXP-01\ROOT\cimv2:Win32_TSSessionSetting
Wenn sie den Befehl unter Windows 2000 ausführen, erhalten Sie keine Ausgabe. Das bedeutet, dass die Klasse Win32_TSSessionSetting unter Windows 2000 nicht unterstützt wird. Einige weitere Befehle, die Sie probieren können, sind: 1.Eine Liste aller Systemklassen im Namespace root\cimv2: cscript GetClasses.vbs |findstr /I "__" 2.Eine Liste aller Kernklassen und allgemeinen Klassen im Namespace root\cimv2: cscript GetClasses.vbs |findstr /I "CIM_" 3.Eine Liste aller Win32-Erweiterungsklassen im Namespace root\cimv2: cscript GetClasses.vbs |findstr /I "Win32_" 4.Eine Liste alle Klassen im Namespace root\cimv2, die den Text "process" enthalten: cscript GetClasses.vbs |findstr /I "process"
CIM-Klassentypen Zu diesem Zeitpunkt sollte klar sein, dass es sich bei den grundlegenden Einheiten im CIMRepository um die Klassen handelt. Genau wie im Active Directory-Schema werden WMIKonfiguratinsinformationen und verwaltete Ressourcen durch eine oder mehrere Klassen definiert. CIM-Klassen sind hierarchisch organisiert. Untergeordnete Klassen erben Eigenschaften und Methoden von den übergeordneten Klassen. Die dynamische Klasse Win32_Service erbt zum Beispiel von der abstrakten Klasse Win32_BaseService. Diese erbt von der abstrakten Klasse CIM_Service, die von der abstrakten Klasse CIM_LogicalElement und diese erbt wiederum von der abstrakten Klasse CIM_ManagedSystemElement. Diese Klassenhierarchie können Sie in Abbildung 6.2 erkennen. Tabelle 6.3 vergleicht die Eigenschaften dieser Klassen. Wie Sie sehen können, erben Unterklassen alle Eigenschaften von ihrer übergeordneten Klasse. Außerdem definieren Sie meist noch weitere, eigene Eigenschaften. Dies ist auch der Grund, aus dem ein Entwickler eine neue Unterklasse statt einer ganz neuen Klasse definiert. Die übergeordnete Klasse bringt meist bereits viele der benötigten Eigenschaften mit. CIM_ManagedSystemElementundCIM_Logical CIM_Service Win32_BaseServ Win32_Servi Element ice ce
Caption
Caption
Caption
Caption
Description
Description Description
Description
InstallDate
InstallDate
InstallDate
InstallDate
Name
Name
Name
Name
Status
Status
Status
Status
CreationClas CreationClass s
CreationClas s
Seite 342 von 394
CIM_ManagedSystemElementundCIM_Logical CIM_Service Win32_BaseServ Win32_Servi Element ice ce
Name
Name
Name
Description Description
Description
Name
Name
Name
Started
Started
Started
StartMode
StartMode
StartMode
Status
Status
Status
SystemCreati SystemCreation SystemCreati on ClassName on ClassName ClassName SystemName SystemName
SystemName
AcceptPause
AcceptPause
AcceptStop
AcceptStop
DesktopInteract DesktopInter act DisplayName
DisplayName
ErrorControl
ErrorControl
ExitCode
ExitCode
PathName
PathName
ServiceSpecific ServiceSpecif ExitCode ic ExitCode ServiceType
ServiceType
StartName
StartName
State
State
TagID
TagID Checkpoint ProcessID WaitHint
Tabelle 6.3: Vergleich der Eigenschaften von über- und untergeordneten Klassen Heißt das, dass Sie die Klasse Win32_BaseService verwenden können, um Informationen über Dienste zurückzugeben? Nein - Win32_BaseService ist eine abstrakte Klasse. Das bedeutet, dass sie nur als Vorlage für andere Klassen dient - nicht zum Abfragen von Daten. Seite 343 von 394
Komponenten einer Klasse Jede Hard- und Softwareressource, die über WMI verwaltet werden kann, wird über eine Klasse definiert. Eine Klasse ist eine Blaupause (oder Vorlage) für eine über WMI verwaltete Ressource. Alle Instanzen einer Ressource verwenden die Blaupause (oder Klassendefinition). Das bedeutet, dass alle Dienste (die auch Instanzen der Klasse Win32_Service sind) die gleichen Eigenschaften haben. Es bedeutet nicht, dass die Eigenschaften alle die gleichen Werte haben. Anmerkung: Wenn eine Eigenschaft nicht mit einem Wert konfiguriert ist, gibt WMI den Wert NULL zurück. Die Klassendefinitionen (Blaupausen) setzten sich aus Eigenschaften, Methoden und Kennzeichnern zusammen. Bevor wir uns diese Komponenten anschauen, sollten Sie wissen, wo die Klassendefinitionen eigentlich herkommen. Nehmen wir einmal an, Microsoft entscheidet sich dazu, einen neuen WMI-Provider zu erstellen, den Administratoren zu Verwaltung und Überwachung von DNS-Servern verwenden können. Das entsprechende Team bei Microsoft würde in diesem Fall mindestens zwei Dateien erstellen: Einen Provider und eine MOF-Datei (Managed Object Format). Der Provider ist die DLL, die als Vermittlungsstelle zwischen der WMI-Infrastruktur und der verwalteten Ressource arbeitet (in diesem Fall der DNS-Server). Der Provider verarbeitet WMI-Anfragen, indem er die nativen APIs der Ressource aufruft. Die MOF-Datei enthält Klassendefinitionen, die die Features des DNS-Providers beschreiben. DNS-Server verwenden zum Beispiel Zonen und Ressourceneinträge. Daher wird die MOFDatei wahrscheinlich Klassen wie MicrosoftDNS_Zone und MicrosoftDNS_ResourceRecord definieren. Wenn der DNS-Provider installiert wird, dann wird dessen DLL beim Betriebssystem und bei WMI registriert. Die MOF-Datei wird kompiliert. Danach ist die DNS-Providerklasse im CIM-Repository vorhanden und kann nun verwendet werden. MOF-Dateien sind Textdateien, die auf der MOF-Sprache basieren. Die MOF-Sprache wurde vom DMTF entwickelt. Wie Sie in Abbildung 6.3 sehen, folgt eine Klassendefinition einer wohl definierten Struktur und Syntax.
Seite 344 von 394
Abbildung 6.3: Struktur einer Klassendefinition Eigenschaften Eigenschaften beschreiben die verwaltete Ressource. Klassen verwenden Eigenschaften, um Dinge wie Konfigurationen, Status und Namen von verwalteten Ressourcen zu beschreiben. Dienste haben zum Beispiel die Eigenschaften Name, angezeigter Name, Beschreibung, Starttyp und Status. Daher hat die Klasse Win32_Service die gleichen Eigenschaften. Jede Eigenschaft hat einen Namen, einen Typ und optional einen Kennzeichner. Über den Namen greifen Sie auf die Eigenschaft zu: Wscript.Echo objService.Name Wscript.Echo objService.Description
In der MOF-Datei sieht die Eigenschaftsdefinition so aus (Name, Datentyp und Kennzeichner sind fett formatiert): [read : ToSubclass,MappingStrings{"Win32API|Service Structures|SERVICE_STATUS|dwControlsAccepted| SERVICE_ACCEPT_PAUSE_CONTINUE"} : ToSubclass] boolean AcceptPause;
Methoden Methoden führen Aktionen mit einer verwalteten Ressource aus. Mit den Methoden der Klasse Win32_Service können Sie zum Beispiel einen Dienst anhalten und starten. Jede Methode hat einen Namen, einen Rückgabewert, optionale Parameter und optionale Methoden-Kennzeichner. Das folgende Scriptstück hält zum Beispiel einen Dienst an. Wenn der Rückgabewert der Methode ungleich 0 ist, dann ist der Vorgang fehlgeschlagen (normalerweise bedeutet der Rückgabewert 0 immer, dass eine Methode erfolgreich ausgeführt wurde): errReturn = obService.StopService()
Seite 345 von 394
Wscript.Echo errReturn
Nicht alle Klassen stellen Methoden zur Verfügung. Unter Windows 2000 stellen zum Beispiel die Klassen Win32_Service, Win32_NTEventLogFile und Win32_Process Methoden zur Verfügung. Unter Windows XP gibt es noch bei einigen weiteren Klassen Methoden (zum Beispiel Win32_DiskQuota und Win32_Printer). Kennzeichner Kennzeichner sind zusätzliche Informationen über eine Klasse, eine Eigenschaft oder eine Methode. Wenn Sie Scripte schreiben, die etwas mehr machen als nur Informationen abzufragen (zum Beispiel Eigenschaften ändern oder Methoden aufrufen), werden Kennzeichner schnell sehr wichtig. Dies liegt daran, dass die Charakteristika einer Eigenschaft oder Methode beschreiben. Welche Informationen stellen Ihnen Kennzeichner nun zur Verfügung? Erstmal sollten Sie wissen, dass es drei Arten von Kennzeichnern gibt - somit gibt es auch drei unterschiedliche Informationstypen. Klassenkennzeichner Klassenkennzeichner stellen Ihnen zusätzliche Informationen über eine Klasse zur Verfügung: • • •
Die Kennzeichner Abstract (Abstrakt), Dynamic(Dynamisch) und Association (Assoziativ) zeigen den Klassentyp an. Der Kennzeichner Provider gibt an, welcher Provider für die Klasse zuständig ist. Der Kennzeichner EnumPrivileges gibt die erforderlichen Rechte zu Verwendung der Klasse an. Der Kennzeichner EnumPrivileges der Klasse Win32_NTLogEvent teilt Ihnen zum Beispiel mit, dass das Recht SeSecurityPrivilege zur Verwendung der Klasse notwendig ist.
Eigenschaftskennzeichner Eigenschaftskennzeichner stellen Informationen über eine Eigenschaft zu Verfügung: •
• •
Der Kennzeichner CIMType gibt den Datentyp der Eigenschaft an. WMI unterstützt einige unterschiedliche Datentypen (zum Beispiel String, Integer, Date, Array und Boolean). Ist es wirklich wichtig, welchen Datentyp eine Eigenschaft hat? Für die bisherigen einfachen Beispiele nicht. Stellen Sie sich jedoch vor, Sie verwenden die Klasse Win32_Printer und fragen die Eigenschaft Capabilities ab. Diese Eigenschaft gibt ein Array zurück. Konsequenterweise können Sie die Eigenschaft nicht einfach ausgeben: Wscript.Echo objPrinter.Capabilities Stattdessen müssen Sie die einzelnen Elemente des Arrays durchgehen: For Each intCapability in objPrinter.Capabilities Wscript.Echo intCapability Next Der Kennzeichner Read zeigt, ob die Eigenschaft gelesen werden kann. Der Kennzeichner Write zeigt, ob Sie die Eigenschaft verändern können. Die Eigenschaft ASPScriptDefaultNamespace der Klasse Win32_WMISetting, die in Script 6.9 verändert wird, ist zum Beispiel schreibbar. Die anderen in den bisherigen Scripten verwendeten Eigenschaften sind hingegen nur lesbar. Ausnahmen sind Eigenschaften, die Sie über eine Methode ändern. Seite 346 von 394
•
Der Kennzeichner Key zeigt an, ob die Eigenschaft ein Klassenschlüssel ist und zur eindeutigen Identifizierung einer Instanz einer verwalteten Ressource verwendet wird. DeviceID ist zum Beispiel eine Schlüsseleigenschaft von Win32_LogicalDisk.
Methodenkennzeichner Methodenkennzeichner stellen Informationen über Methoden zur Verfügung: •
• •
Der Kennzeichner Implemented zeigt an, dass eine Methode über einen Provider implementiert ist. Dies ist wichtig, da WMI-Klassen oftmals Methoden enthalten, die noch nicht funktionieren (da sie noch nicht implementiert wurden). Der Kennzeichner ValueMap definiert die Werte, die als Parameter verwendet werden können und den Rückgabewert. Der Kennzeichner Privileges definiert die zum Aufrufen der Methode erforderlichen Rechte.
Anmerkung: Es gibt noch deutlich mehr Kennzeichner. Eine komplette Liste der WMIKennzeichner finden Sie im WMI SDK. Das SDK finden Sie unter dem Link Microsoft Windows Management Instrumentation (WMI) SDK unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig). Vergleich zwischen Klassen und verwalteten Ressourcen Klassen legen fest, was Sie mit WMI machen können und was nicht. Wenn Sie eine Klasse für Dienste haben, können Sie diese verwalten - wenn nicht, dann nicht. Konsequenterweise ist es also wichtig zu wissen, welche Klassen auf einem Computer vorhanden sind. Der Umfang von WMI unterscheidet sich unter verschiedenen Betriebssystemen. Die Klasse Win32_ComputerSystem stellt unter Windows XP viele neue Eigenschaften und Methoden zur Verfügung, die unter Windows 2000 nicht vorhanden sind. Damit Ihr Script funktioniert, müssen Sie wissen, welche Eigenschaften und Methoden vorhanden sind. Anmerkung: Mit WMI können Sie keine Informationen von einem Remotecomputer abfragen, wenn WMI auf diesem nicht installiert ist. Auch die verwendeten Methoden und Eigenschaften müssen vorhanden sein. Ein WMI-Script, das lokal auf einem Computer unter Windows XP korrekt ausgeführt wird, kann daher auf einem Remotecomputer unter Windows 2000 durchaus fehlschlagen. Wie stellen Sie fest, ob eine Methode oder Eigenschaft auf einem bestimmten Computer zur Verfügung steht? Sie fragen die Klassendefinition ab. Abfragen von Klassendefinitionen Sie können die WMI-Scripting-Bibliothek verwenden, um die Klassendefinitionen von verwalteten Ressourcen abzufragen. Hierzu gibt es zwei Wege: • •
Sie verwenden die Eigenschaften SWbemObject.Qualifiers_, SWbemObject.Properties_ und SWbemObject.Methods_. Sie verwenden die Methode SWbemObject.GetObjectText_, um eine Klassendefinition in der MOF-Syntax zu erhalten. Seite 347 von 394
Verwenden der Eigenschaften Properties_, Methods_ and Qualifiers_ Script 6.14, Script 6.15 und Script 6.16 zeigen, wie Sie die Eigenschaften Properties_, Methods_ und Qualifiers_ zur Abfrage von Informationen über die Klasse Win32_Service verwenden können. Alle drei Scripte gehen nach dem gleichen Ansatz vor - es gibt allerdings auch einige Unterschiede. Script 6.14 beginnt mit der Initialisierung von drei Variablen: strComputer, strNameSpace und strClass. strComputer werden als Wert der Namen des Zielcomputers zugewiesen. strNameSpace wird der Name des Namespace zugewiesen, mit dem eine Verbindung aufgebaut werden soll, und strClass erhält als Wert den Namen der Klasse, dessen Eigenschaften abgefragt und angezeigt werden soll. Wenn diese drei Werte in einzelnen Variablen gespeichert werden, wird es einfach, das Script für andere Computer, Namespaces und Klassen zu verwenden. Mit der Collection WSH Arguments können Sie das Script sogar ganz einfach in einem Kommandozeilentool verwenden. Weitere Informationen hierzu finden Sie im Kapitel Der WSH in diesem Buch. Als Nächstes verwendet das Script die VBScript-Funktion GetObject, um eine Verbindung mit dem WMI-Dienst auf dem Zielcomputer zu erstellen. Vielleicht ist Ihnen etwas bei dem Verbindungsstring aufgefallen, der an GetObject übergeben wird. Zusätzlich zum Namespace wird auch noch der Klassenname angegeben. Dies hat weit reichende Auswirkungen auf das, was die Methode GetObject zurückgibt. Statt eine Referenz auf ein SWbemServices-Objekt zurückzugeben (wie in den bisherigen Scripten), gibt GetObject nun eine Referenz auf ein SWbemObject zurück, das die Zielklasse darstellt. Wie das? Die Antwort liegt im Objektpfad. Auch wenn Objektpfade später in diesem Kapitel noch genauer besprochen werden, wollen wir hier eine kurze Einführung geben. Sie wird Ihnen beim Verständnis des Scripts helfen. Jede WMI-Klasse und jede Instanz einer verwalteten Ressource hat einen Objektpfad. Ein Objektpfad ist so ähnlich wie ein Dateipfad. Eine Datei hat einen voll qualifizierten Pfad dieser setzt sich aus einem Gerätenamen, einem oder mehreren Ordnernamen und dem Dateinamen zusammen. Klassen und verwaltete Ressourcen haben ebenfalls solche Pfade sie setzen sich aus dem Computernamen, dem Namespace, dem Klassenamen, der Schlüsseleigenschaft der Klasse und dem Wert der Schlüsseleigenschaft zusammen. Ein solcher Objektpfad sieht zum Beispiel so aus (die eckigen Klammern trennen nur die einzelnen Teile des Objektpfads - sie werden normalerweise weggelassen): [\\ComputerName][\Namespace][:Klassenname][.Schlüsseleigenschaft='Wert']
Wenn Sie den gesamten Objektpfad oder einen Teil des Objektpfads im Verbindungsstring von GetObject verwenden, dann legt dieser den zurückgegebenen Referenztyp fest. Wenn Sie zum Beispiel nur den Computernamen-Teil des Objektpfads angeben, dann erhalten Sie eine SWbemServices-Objektreferenz zurück, die mit dem Standard-Namespace verbunden ist. Wenn Sie den Computernamen oder den Namenspace oder beides angeben, erhalten Sie ebenfalls eine Referenz auf ein SWbemServices-Objekt. Wenn Sie den Computernamen, den Namespace und den Klassennamen angeben erhalten, Sie eine Referenz auf ein SWbemObject zurück, das eine Klasse darstellt. Wenn Sie alle vier Teile des Objektpfads angeben, erhalten Sie ein SWbemObject zurück, das eine Instanz eines verwalteten Objekts darstellt - und zwar die Instanz, die durch den Wert der Schlüsseleigenschaft definiert wird. Die Elemente des Objektpfads und die zurückgegebenen Objekte sind in Tabelle 6.4 zusammengefasst. Tabelle 6.4: Elemente des Objektpfads und die zurückgegebenen Objekte Seite 348 von 394
Teile des Objektpfads
Zurückgegebenes Objekt
Computername: Set objSWbemServices = _ GetObject("winmgmts:\\WebServer")
SWbemServices (verbunden mit dem Standard-Namespace)
Computername und/ oder Namespace: Set objSWbemServices = _ GetObject ("winmgmts:\\WebServer\root\cimv2")
SWbemServices (verbunden mit den angegebenen Namespace in diesem Fall root\cimv2)
Computername, Namespace, Klassenname: Set objSWbemObject = GetObject _ ("winmgmts:\\WebServer\root\ cimv2:Win32_Service")
SWbemObject (stellt die Klasse Win32_Service dar)
Computername, Namespace, SWbemObject (stellt die Klassenname, Schlüsseleigenschaft: Instanz des Dienstes mit dem Namen Alerter dar) Set objSWbemObject = GetObject _ "winmgmts:\\WebServer\root\cimv2:" & _ Win32_Service.Name='Alerter'") Der Rest des Scripts ist ganz einfach. Es verwendet die SWbemObject-Referenz (objClass), um auf die Eigenschaften (objClass.Properties_) zuzugreifen. Properties_.property referenziert eine Collection mit den Eigenschaften der Klasse (SWbemPropertySet). Jede Eigenschaft in der Collection ist ein SWbemProperty-Objekt (objClassProperty) dessen Eigenschaftsname gelesen und ausgegeben wird. Script 6.14: Abfragen der Eigenschaften von Win32_Service über SWbemObject.Properties_ 1 strComputer = "." 2 strNameSpace = "root\cimv2" 3 strClass = "Win32_Service" 4 5 Set objClass = GetObject("winmgmts:\\" & strComputer & _ 6 "\" & strNameSpace & ":" & strClass) 7 8 Wscript.Echo strClass & " Eigenschaften " 9 Wscript.Echo " — — — — — — — — — — — — — — — " 10 11For Each objClassProperty In objClass.Properties_ 12 Wscript.Echo objClassProperty.Name 13Next
Wenn Sie das Script ausführen, werden die 25 Eigenschaften der Klasse Win32_Service angezeigt. Win32_Service Eigenschaften - - - - - - - - - - - - - - AcceptPause AcceptStop Caption CheckPoint CreationClassName Description
Seite 349 von 394
DesktopInteract DisplayName ErrorControl ExitCode InstallDate Name PathName ProcessId ServiceSpecificExitCode ServiceType Started StartMode StartName State Status SystemCreationClassName SystemName TagId WaitHint
Script 6.15 ist mit Script 6.14 bis auf eine Ausnahme identisch: Eine For-Each-Schleife geht die Collection SWbemMethodSet durch und zeigt die Eigenschaft Name jeder Methode (objClassMethod) in der Collection SWbemMethodSet an. Script 6.15: Verwenden von SWbemObject.Methods_, um die Methoden von Win32_Service abzufragen 1 strComputer = "." 2 strNameSpace = "root\cimv2" 3 strClass = "Win32_Service" 4 5 Set objClass = GetObject("winmgmts:\\" & strComputer & _ 6 "\" & strNameSpace & ":" & strClass) 7 8 Wscript.Echo strClass & " Methoden" 9 Wscript.Echo " — — — — — — — — — — — — — -" 10 11For Each objClassMethod In objClass.Methods_ 12 Wscript.Echo objClassMethod.Name 13Next
Wenn Sie das Script ausführen, zeigt es die 10 Methoden der Klasse Win32_Service an: Win32_Service Methoden - - - - - - - - - - - - - StartService StopService PauseService ResumeService InterrogateService UserControlService Create Change ChangeStartMode Delete
Script 6.16 entspricht zum größten Teil den Scripten 6.14 und 6.15 - mit drei Ausnahmen: • •
In der For-Each-Schleife werden die Elemente der Collection SWbemQualifierSet durchlaufen. Die Eigenschaft Name jedes Elementes in der Collection wird ausgegeben. Da die Klassenkennzeichner Teil der Klassendefinition sind und Kennzeichner Werte haben, Seite 350 von 394
fragt Script 6.16 auch diese ab. •
Da Kennzeichner mehrere Werte in einem Array bereitstellen können, muss dies im Script berücksichtigt werden. Über die VBScript-Funktion VarType wird daher der Typ der Variable überprüft.
Script 6.16: Abfragen der Klassenkennzeichner von Win32_Service über SWbemObject.Qualifiers_ 1 strComputer = "." 2 strNameSpace = "root\cimv2" 3 strClass = "Win32_Service" 4 5 Set objClass = GetObject("winmgmts:\\" & strComputer & _ 6 "\" & strNameSpace & ":" & strClass) 7 8 Wscript.Echo strClass & " Klassenkennzeichner" 9 Wscript.Echo " — — — — — — — — — — — — — — — " 10 11For Each objClassQualifier In objClass.Qualifiers_ 12 If VarType(objClassQualifier.Value) = (vbVariant + vbArray) Then strQualifier = objClassQualifier.Name & " = " & _ 13 Join(objClassQualifier.Value, ",") 14 15 Else strQualifier = objClassQualifier.Name & " = " & _ 16 objClassQualifier.Value 17 End If 18 19 Wscript.Echo strQualifier strQualifier = "" 20 21Next
Wenn Sie das Script ausführen, werden die Namen und Werte der fünf Klassenkennzeichner der Klasse Win32_Service ausgegeben: Win32_Service Klassenkennzeichner - - - - - - - - - - - - - - dynamic = True Locale = 1033 provider = CIMWin32 SupportsUpdate = True UUID = {8502C4D9-5FBB-11D2-AAC1-006008C78BC7}
Script 6.14 und Script 6.15 zeigen keine Methoden- und Eigenschaftskennzeichner an - die Scripte bleiben so überschaubar. Verwenden der Methode SWbemObject.GetObjectText_ Es wurde bereits darauf hingewiesen, dass Sie die Klassendefinitionen von verwalten Ressourcen über eine MOF-Datei abfragen können. Wenn Sie zum Beispiel die Klasse Win32_Service abfragen möchten, schlagen Sie in der Datei systemroot\System32\Wbem\Cimwin32.mof nach. Die direkte Verwendung von MOF-Dateien hat jedoch ihren Preis - Sie müssen jede Klasse in der Klassenhierarchie überprüfen, um die komplette Klassendefinition zu erhalten. Wenn Sie zum Beispiel die Klasse Win32_Service abfragen, dann müssen Sie auch alle fünf Klassen überprüfen, die sich in der Hierarchie über der Klasse Win32_Service befinden. Dies ist sehr mühsam - ein einfacherer Ansatz ist es, die Methode SWbemObject.GetObjectText_ zu verwenden. Script 6.17 demonstriert dies.
Seite 351 von 394
Im Gegensatz zu Script 6.14 und Script 6.16 verwendet Script 6.17 die Methode SWbemServices.Get statt die Klasse über GetObject abzufragen. Die Methode Get muss verwendet werden, damit das Flag (der Schalter) wbemFlagUseAmendedQuailifiers aktiviert werden kann. Mit diesem Flag teilen Sie WMI mit, dass es die gesamte Klassendefinition der verwalteten Ressource zurückgeben soll und nicht nur deren lokale Definition. Mit anderen Worte: Die zurückgegebenen Informationen umfassen nicht nur drei Eigenschaften, die direkt in der Klasse definiert werden, sondern auch alle von übergeordneten Klassen gerbten Eigenschaften. Die Methode Get gibt eine Referenz auf ein SWbemObject-Objekt (objClass) zurück, dass die Zielklasse darstellt, mit der die Methode GetObjectText_ aufgerufen wird. Die Methode GetObjectText_ gibt die MOF-Beschreibung der Klasse zurück. Wenn Sie GetObjectText_ verwenden, ohne das Flag wbemFlagUseAmendedQuailifiers zu aktivieren, erhalten Sie nur die Methoden, Eigenschaften und Kennzeichner, die direkt in der Klasse Win32_Service definiert werden. Die geerbten Eigenschaften und Methoden werden nicht ermittelt. Script 6.17: Abfragen der MOF-Beschreibung der Klasse Win32_Service über SWbemObject.GetObjectText_ 1 strComputer = "." 2 strNameSpace = "root\cimv2" 3 strClass = "Win32_Service" 4 5 Const wbemFlagUseAmendedQualifiers = &h20000 6 7 Set objSWbemServices = _ 8 GetObject("winmgmts:\\" & strComputer & "\" & strNameSpace) 9 Set objClass = objSWbemServices.Get(strClass, 10wbemFlagUseAmendedQualifiers) 11strMOF = objClass.GetObjectText_ 12 Wscript.Echo strMOF
Die Ausgabe des Scripts sieht so aus: [dynamic: ToInstance, provider("CIMWin32"): ToInstance, Locale(1033): Amended, UUID("{8502C4D9-5FBB-11D2-AAC1-006008C78BC7}"): ToInstance, Description("The Win32_Service class represents a service on a Win32 computer system. A service application conforms to the interface rules of the Service Control Manager (SCM) and can be started by a user automatically at system boot through the Services control panel utility, or by an application that uses the service functions included in the Win32 API. Services can execute even when no user is logged on to the system."): ToSubClass Amended] class Win32_Service : Win32_BaseService { [Description("The Caption property is a short textual description (one-line string) of the object."): ToSubClass Amended] string Caption;
Es gibt allerdings einen Vorbehalt gegenüber der Methode GetObjectText_ : Sie gibt keine Informationen über die geerbten Kennzeichner zurück.
Erkunden des CIM-Repository Die Erkundung des CIM-Repository ist wahrscheinlich der beste Weg, um mehr über die verfügbaren Klassen und deren Methoden und Eigenschaften zu lernen. Sie haben bereits ein Seite 352 von 394
Script kennen gelernt, mit dem Sie das CIM-Repository anzeigen lassen können. Sie sind jedoch nicht auf dieses Script beschränkt. Es gibt einige unterschiedliche Tools, mit denen Sie WMI-Klassen anzeigen lassen können. WMI-Tester Der WMI-Tester (Wbemtest.exe) ist ein grafisches Tool das Sie zu Interaktion mit der WMIInfrastruktur verwenden können. Sie können das CIM-Schema anzeigen lassen und Klassendefinitionen verwalten. Über den WMI-Tester können Sie dieselben Aktionen wie über ein Script ausführen. Da der WMI-Tester ein Teil der WMI-Standardinstallation ist, steht er immer zur Verfügung. CIM-Studio CIM-Studio ist Teil des WMI-SDKs. Es stellt eine webbasierte Benutzerschnittstelle zur Interaktion mit der WMI-Infrastruktur zur Verfügung. Mit CIM-Studio haben Sie die gleichen Möglichkeiten wie mit dem WMI-Tester. Scripte aus dem Resource Kit Das Microsoft Windows 2000 Server Resource Kit enthält Dutzende von Scripten zur Verwendung von WMI. Mit drei dieser Scripts (EnumClasses.vbs, EnumInstances.vbs und EnumNamespaces.vbs) können Sie ebenfalls das CIM-Schema anzeigen.
Die WMI-Scripting-Bibliothek Die WMI-Scripting-Bibliothek stellt einen Satz von Automatisationsobjekten zur Verfügung, über die Scriptsprachen wie VBScript, JScript und ActiveState ActivePerl auf die WMIInfrastruktur zugreifen können. Die WMI-Scripting-Bibliothek ist über eine einzelne Automatisationskomponente (wbemdisp.dll) implementiert - diese befindet sich im Ordner systemroot\System32\Wbem.
Das Objektmodell der WMI-Scripting-Bibliothek Die WMI-Scripting-Bibliothek setzt sich aus 19 Automatisationsobjekten zusammen. 16 von diesen Objekten sehen Sie in Abbildung 6.4. Die WMI-Scripting-Bibliothek stellt ein konsistentes und einheitliches Scriptmodell für verwaltete Ressourcen zur Verfügung. Es ist wichtig, dass Sie die Beziehungen zwischen den Automatisationsobjekten der WMIScripting-Bibliothek und den Klassendefintionen der verwalteten Ressourcen im CIMRepository verstehen. Über die WMI-Scripting-Bibliothek können Scripte eine Authentifizierung durchführen und eine Verbindung mit WMI aufbauen. So erhalten die Scripte einen Zugriff auf die Instanzen der verwalteten Ressourcen. Das Script kann dann auf die Methoden und Eigenschaften der Ressource zugreifen.
Seite 353 von 394
Abbildung 6.4: Das Objektmodell der WMI-Scripting-Bibliothek Anmerkung: Die Linien in Abbildung 6.4 zeigen an, dass das entsprechende Objekt über eine Methode oder eine Eigenschaft erstellt oder aufgerufen wird. Wenn Sie zum Beispiel die Methode SWbemLocator.ConnectServer aufrufen, dann gibt diese eine SWbemObjectSetCollection zurück. Das Diagramm gibt Ihnen einen guten Überblick über die Arbeitsweise von WMI-Scripten. Die bisherigen Scripte führten zum Beispiel aller drei grundlegenden Aufgaben aus: •
Jedes Script hat sich zuerst mit dem WMI-Dienst auf dem Zielcomputer verbunden. Hierzu wird die Funktion GetObject mit einem entsprechenden WMI-Verbindungsstring verwendet. Seite 354 von 394
Dieser besteht aus dem WMI-Moniker ("winmgmts:") und einem WMI-Objektpfad zum Zielcomputer und den entsprechenden Namespace: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & _ "\root\cimv2") Auf diese Art erhält das Script das in Abbildung 6.4 gezeigt SWbemServices-Objekt zurück. Es kann dann die Methoden des Objekts aufrufen. •
•
• •
Jedes Script ruft über die Methode InstancesOf Instanzen von verwalteten Ressourcen ab. Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service") InstancesOf gibt immer eine SWbemObjectSet-Collection zurück. Wie Sie an der Linie zwischen den Objekten SWbemServices und SWbemObjectSet sehen, handelt es sich um eine der drei Objektarten, die SWbemServices zurückgeben kann (die anderen beiden sind SWbemObject und SWbemEventSource). Jedes Script greift auf die Eigenschaften der verwalteten Ressourcen zu, indem es die Instanzen im Objekt SWbemObjectSet durchgeht. For Each objSWbemObject In colSWbemObjectSet Wscript.Echo "Name: " & objSWbemObject.Name Next Wie Sie in Abbildung 6.4 sehen, wird jede Instanz einer verwalteten Ressource in einer SWbemObjectSet-Collection durch ein SWbemObject-Objekt repräsentiert. Wenn das SWbemObjectSet-Objekt zum Beispiel aus einer Collection mit allen installierten Diensten besteht, bedeutet das: Jedes Element der Collection ist ein SWbemObject-Objekt (dies gilt für jedes SWbemObjectSet-Objekt). Jedes Objekt in der Collection ist eine Instanz eines installierten Dienstes.
Interpretieren des WMI-Scripting-Bibliothek-Objektmodells Was können Sie noch vom WMI-Scripting-Bibliothek-Objektmodell lernen? Sie sehen, dass die Objekte SWbemLocator, SWbemSink, SWbemObjectPath, SWbemNamedValueSet und SWbemLastError über die Funktion CreateObject erstellt werden. Andererseits können SWbemServices und SWbemObject über die Funktion GetObject zusammen mit einem WMIMoniker und einem WMI-Objektpfad erstellt werden. Das Objektmodell zeigt Ihnen außerdem über den Kreis mit dem S, dass sieben Objekte der WMI-Scripting-Bibliothek ein SWbemSecurity-Objekt zur Verfügung stellen. Von den 19 Automatisationsobjekten der WMI-Scripting-Bibliothek sind die beiden wichtigsten Objekte SWbemServices und SWbemObject. SWbemServices repräsentiert die authentifizierte Verbindung zu einem WMI-Namespace auf einem Computer und spielt eine wichtige Rolle bei Abfragen und der Verarbeitung von Ereignissen. Über SWbemObject rufen Sie die Methoden und Eigenschaften der verwalteten Ressource auf. Die folgende Diskussion betrachtet die Rolle die die primären WMI-Scripting-BibliothekAutomatisationsobjekte beim WMI-Scripting spielen. Wenn Sie Informationen über die einzelnen Objekte erhalten, nehmen Sie sich einen Moment Zeit und betrachten Sie die Beziehungen des Objekts zu den anderen Objekten im Objektmodell-Diagramm. Seite 355 von 394
Namenskonventionen für Variablen Die Variablen in den folgenden Beispielscripten, die eine Referenz auf WMIAutomatisationsobjekte speichern, folgen einer Namenskonvention. Die Variable wird nach dem Automatisationsobjekt benannt, auf das sie eine Referenz speichert - außerdem wird ihr der Prefix "obj" vorangestellt (zum zu zeigen, dass es sich um eine Objektreferenz handelt). Eine Variable, die eine Referenz auf ein SWbemServices-Objekt speichert, wird zum Beispiel objSWbemServices genannt. Eine Variable, die eine Referenz auf ein SWbemObject-Objekt speichert, wird objSWbemObject genannt. Eine Variable, die eine Referenz auf eine SWbemObjectSet-Collection speichert, wird colSWbemObjectSet genannt. Mit dieser Namenskonvention fällt es Ihnen leichter, mit WMI-Objekten zu arbeiten. Ihr Scriptcode wird leichter lesbar und einfacher zu pflegen. Natürlich können Sie auch beliebige Variablennamen verwenden - wenn Ihre Variablen x und y heißen, dann ist das auch in Ordnung. Die beiden folgenden Codestücke arbeiten zum Beispiel vollkommen gleich - sie verwenden nur unterschiedliche Variablennamen: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") strComputer = "." Set x = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Anmerkung: In den folgenden Abschnitten werden nicht alle Methoden und Eigenschaften jedes Automatisationsobjekts besprochen. Eine vollständige Übersicht können Sie im WMISDK finden. Weitere Informationen hierzu finden Sie unter dem Link Microsoft Windows Management Instrumentation (WMI) SDK unter http://www.microsoft.com/windows/reskits/webresources (englischsprachig).
SWbemLocator An der Spitze des WMI-Scripting-Bibliothek-Objektmodells steht das Objekt SWbemLocator. Es wird verwendet, um eine authentifizierte Verbindung mit einem WMI-Namespace aufzubauen - so ähnlich, wie die VBScript-Funktion GetObject und der WMI-Moniker "winmgmts:" zum Aufbau einer authentifizierten Verbindung zu WMI dienen. SWbemLocator wurde für zwei bestimmte Scripting-Szenarien entwickelt, die nicht über GetObject und den WMI-Moniker durchgeführt werden können. SWbemLocator wird in den folgenden Fällen benötigt: •
•
Wenn Sie sich mit einem anderen Benutzernamen und Passwort mit WMI auf einem Remotecomputer verbinden möchten. Der WMI-Moniker der Funktion GetObject stellt keine Mechanismen zur Angabe von Anmeldeinformationen zur Verfügung. Die meisten WMIAktivitäten (inklusive der auf Remotecomputern) erfordern jedoch administrative Rechte. Wenn Sie sich mit WMI verbinden möchten und das WMI-Script in einer Webseite ausführen[NA?]. Wenn Sie Scripte ausführen, die in eine HTML-Seite eingebettet sind, können Sie die Funktion GetObject aus Sicherheitsgründen nicht verwenden.
Außerdem können Sie SWbemLocator verwenden, um eine Verbindung mit WMI aufzubauen, wenn Sie den WMI-Verbindungsstring von GetObject zu kompliziert finden. Eine Referenz auf SWbemLocator erstellen Sie mit CreateObject statt mit GetObject. Sie müssen der Funktion CreateObject die ProgID von SWbemLocator übergeben ("WbemScripting.SWbemLocator" - wie in Zeile 2 des folgenden Scripts). Nachdem Sie eine Seite 356 von 394
Referenz auf das SWbemLocator-Objekt erhalten haben, rufen Sie die Methode ConnectServer auf um sich mit WMI zu verbinden und eine Referenz auf ein SWbemServicesObjekt zu erhalten (Zeile 3 im folgenden Beispielscript): strComputer = "." Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator") Set objSWbemServices = objSWbemLocator.ConnectServer(strComputer, "root\cimv2") Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service") For Each objSWbemObject In colSWbemObjectSet Wscript.Echo "Name: " & objSWbemObject.Name Next
Die Zeilen 2 und 3 sind identisch mit allen Beispielen zu GetObject, die Sie bereits gesehen haben. ConnectServer ist die einzige Methode, die von SWbemLocator zur Verfügung gestellt wird. Um ein Script mit alternativen Anmeldeinformationen auszuführen, übergeben Sie ConnectServer den Namen und das Passwort als optionale Parameter. Das folgende Script wird zum Beispiel unter den Anmeldeinformationen des Benutzers kenmyer ausgeführt - sein Passwort ist homerjs. strComputer = "atl-dc-01" Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator") Set objSWbemServices = objSWbemLocator.ConnectServer _ (strComputer, "root\cimv2", "kenmyer", "homerjs") Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service") For Each objSWbemObject In colSWbemObjectSet Wscript.Echo "Name: " & objSWbemObject.Name Next
Sie können für den Benutzernamen alternativ auch das Format Domäne\Benutzer verwenden: " fabrikam\kenmyer"
WMI-Scripte, die sich mit dem WMI-Dienst auf dem lokalen Computer verbinden, verwenden immer den Sicherheitskontext des angemeldeten Benutzers. Für diese Scripte können Sie über die Methode SWbemLocator.ConnectServer keinen abweichenden Benutzernamen und ein Passwort angeben. Sie sollten es vermeiden, Passwörter direkt in ein Script einzutragen. Stattdessen können Sie zum Beispiel die Methoden WScript.StdOut und .StdIn verwenden, um den Scriptbenutzer zur Eingabe des Passworts aufzufordern. Wenn das Script unter Windows XP Professional ausgeführt wird, können Sie die Scripteingabe sogar über das Objekt ScriptPW unkenntlich machen. Das folgende Script demonstriert die Eingabe eines Passworts. Es verwendet die Methode .StdIn, um das Passwort einzulesen und weist dieses dann der Variable strPassword zu. Da das Script die Methoden StdIn und StdOut verwendet, muss es unter CScript ausgeführt werden: strComputer = "atl-dc-01" Wscript.StdOut.Write "Geben Sie das Administrator-Passwort ein: " strPassword = Wscript.StdIn.ReadLine Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator") Set objSWbemServices = objSWbemLocator.ConnectServer _ (strComputer, "root\cimv2", "administrator", strPassword) Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service") For Each objSWbemObject In colSWbemObjectSet Wscript.Echo "Name: " & objSWbemObject.Name Next
Seite 357 von 394
SWbemServices SWbemServices hat zwei primäre Aufgaben. Erstens stellt das Objekt eine authentifizierte Verbindung zu einem WMI-Namespace auf dem Zielcomputer dar, und zweitens verwenden Sie SWbemServices zur Abfrage von verwalteten Ressourcen. Eine Referenz auf ein SWbemServices-Objekt erhalten Sie über zwei Wege: •
•
Wie Sie in den meisten bis jetzt gezeigten Scripten gesehen haben, können Sie die VBScriptFunktion GetObject zusammen mit dem WMI-Moniker "winmgmts:" verwenden. Das folgende Beispiel zeigt die einfachste Form einer WMI-Verbindung. Es verbindet sich mit dem Standard-Namespace (normalerweise root\cimv2) des lokalen Computers: Set objSWbemServices = GetObject("winmgmts:") Sie können die Methode ConnectServer eines SWbemLocator-Objekts verwenden. Weitere Informationen hierzu haben Sie im vorhergehenden Abschnitt SWbemLocator erhalten.
Nachdem Sie eine Referenz auf ein SWbemServices-Objekt haben, können Sie einer der 18 Methoden von SWbemServices aufrufen. SWbemServices kann drei unterschiedliche Objekte zurückgeben (SWbemObjectSet, SWbemObject und SWbemEventSource). Sie sollten wissen, welche Methode welches Objekt zurückgibt. Wenn sie zum Beispiel ein Objekt vom Typ SWbemObjectSet zurückerhalten, müssen Sie eine Collection durchgehen, um an die einzelnen SWbemObject-Objekte zu gelangen. Wenn Sie direkt ein SWbemObject-Objekt zurück erhalten, können Sie sofort auf die Methoden und Eigenschaften des Objekts zugreifen. Die Methoden und die Rückgaben von SWbemServices sehen Sie in Tabelle 6.5. Tabelle 6.5: Methoden von SWbemServices Name der Methode
Standardmodus
Rückgabewert
Beschreibung
AssociatorsOf
Semisynchron
SWbemObjectSet
Ruft die Instanzen von den verwalteten Ressourcen einer bestimmten Klasse ab. Sie geben einen Objektpfad an, und die Methode gibt eine verwaltete Ressource zurück.
Delete
Synchron (kann nicht geändert werden)
keiner
Löscht eine Instanz einer verwalteten Ressource (oder eine Klassendefinition im CIM-Repository).
ExecMethod
Synchron (kann nicht geändert werden)
SWbemObject
Bietet einen alternativen Weg, um eine Methode auszuführen, die über die Klassendefinition einer verwalteten Ressource definiert ist. Wird primär in Situationen verwendet, in denen eine Scriptsprache Seite 358 von 394
Name der Methode
Standardmodus
Rückgabewert
Beschreibung
keinen Parameter unterstützt (zum Beispiel JScript). ExecNotificationQuerySemisynchron
SWbemEventSourceFührt eine Ereignisabonnementabfrage durch, um Ereignisse entgegenzunehmen. Eine Ereignisabonnementabfrage ist eine Abfrage, die das Ereignis definiert, das Sie überwachen möchten. Wenn das Ereignis auftritt, übergibt die WMIInfrastruktur ein entsprechendes Ereignis an das Script.
ExecQuery
Semisynchron
SWbemObjectSet
Führt eine Abfrage aus, die eine Collection mit den Instanzen einer verwalteten Ressource zurückgibt (oder mit Klassendefinitionen). Kann verwendet werden, um eine gefilterte Collection mit Instanzen zu erhalten.
Get
Synchron (kann nicht geändert werden)
SWbemObject
Ruft eine einzelne Instanz einer verwalteten Ressource (oder Klassendefinition) über einen Objektpfad ab.
InstancesOf
Semisynchron
SWbemObjectSet
Ruft alle Instanzen einer verwalteten Ressource auf Basis eines Klassennamens ab. Standardmäßig führt InstancesOf eine "Tiefenabfrage" durch - das bedeutet, dass alle Instanzen der übergebenen Klasse und alle Instanzen der Unterklassen der übergebenen Klasse zurückgeliefert werden.
ReferencesTo
Semisynchron
SWbemObjectSet
Gibt alle Zuordnungen zurück, die eine bestimmte Ressource referenzieren. Im Gegensatz zu AssociatorsOf (die Seite 359 von 394
Name der Methode
Standardmodus
Rückgabewert
Beschreibung
dynamische Ressourcen zurückgibt), gibt ReferencesTo die entsprechende Zuordnung zurück. SubclassesOf
Semisynchron
SWbemObjectSet
Fragt alle Unterklassen einer bestimmten Klasse im CIM-Repository ab.
In Tabelle 6.5 finden Sie nur 9 der 18 Methoden von SWbemServices. Bei den restlichen 9 Methoden handelt es sich um die asynchronen Gegenstücke zu den gezeigten Methoden. Die Namen der Methoden entsprechen den in der Tabelle gezeigten Namen mit einem angehängten Suffix Async. Die asynchrone Version von ExecNotificationQuery heißt also zum Beispiel ExecNotificationQueryAsync. SWbemServices Betriebsmodi SWbemServices unterstützt drei Betriebsmodi: synchron, asynchron und semisynchron. •
Synchron - Im synchronen Modus steht das Script so lange still, bis die Methode SWbemServices ausgeführt wurde. Auch das Script ist betroffen - wenn das WMI Instanzen von verwalteten Ressourcen abfragt, wird die gesamte SWbemObjectSet-Collection im Speicher aufgebaut. Erst dann werden diese Daten an das Script zurückgegeben. Dies kann Auswirkungen auf die Leistung des Scripts und auf den Computer, auf dem das Script ausgeführt wird, haben. Die synchrone Abfrage von Tausenden von Ereignissen kann unter Windows 2000 zum Beispiel sehr lange dauern und eine große Menge an Speicher verbrauchen. Aus diesen Gründen sollten synchrone Methoden nicht ausgeführt werden eine Ausnahme sind die drei Methoden Delete, ExecMethod und Get. Diese geben keinen großen Datensätze zurück.
•
Asynchron - Im asynchronen Modus wird das Script sofort weiter ausgeführt. Um eine asynchrone Methode zu verwenden, muss Ihr Script erst ein SWbemSink-Objekt und eine spezielle Subroutine (den Event-Handler) erstellen. WMI führt die asynchrone Methode aus und benachrichtigt das Script über die Event-Handler-Subroutine, wenn die Methode beendet ist.
•
Semisynchron - Der semisynchrone Modus ist ein Kompromiss zwischen synchron und asynchron. Er bietet oft eine bessere Leistung als der synchrone Modus, und es sind keine zusätzlichen Schritte notwendig. Daher ist dies der Standardmodus für die meisten WMIAbfragen.
Im semisynchronen Modus ruft das Script die Methode auf und wird sofort weiter ausgeführt. WMI ruft die verwalteten Ressourcen währenddessen im Hintergrund ab. Nachdem die Ressourcen vollständig abgerufen sind, werden diese über eine SWbemObjectSet-Collection an das Script zurückgegeben. Sie können auf die Ressourcen zugreifen ohne darauf zu warten, dass die gesamte Collection zusammengestellt ist. Wenn Sie mit verwalteten Ressourcen arbeiten, die viele Instanzen haben (viel heißt in diesem Fall mehr als 1.000), dann gibt es gewisse Vorbehalte gegenüber semisynchronen Methoden. WMI erstellt und speichert für jede Instanz einer Ressource ein SWbemObjectObjekt. Bei vielen Instanzen kann die Leistung des Scripts und des Computers zurückgehen. Seite 360 von 394
Um dieses Problem zu umgehen, können Sie die Aufrufe von semisynchronen Methoden optimieren, indem Sie das Flag wbemFlagForwardOnly verwenden. Mit dem wbemFlagForwardOnly-Flag in Kombination mit dem Flag wbemFlagReturnImmediately teilen Sie WMI mit, dass es eine forward-onlySWbemObjectSet-Collection zurückgeben soll. Dieser Weg hat jedoch seinen Preis. Eine forward-onlySWbemObjectSet-Collection kann nur einmal abgefragt werden. Nachdem auf ein Element der Collection zugegriffen wurde, wird dieses aus dem Speicher entfernt. Mit Ausnahme der Methoden Delete, ExecMethod, Get und den neun asynchronen Methoden sollten Sie standardmäßig die semisychronen Methoden verwenden. Verwenden der Methoden von SWbemServices Zu den häufig verwendeten Methoden zählen InstancesOf, ExecQuery, Get und ExecNotificationQuery. Auch wenn Sie oft verwendet wird, ist InstancesOf nicht der empfohlene Weg, um Informationen abzurufen (auch wenn es wohl der einfachste Weg ist). Warum dies so ist, wird im nächsten Abschnitt besprochen. InstancesOf Sie haben die Verwendung der Methode InstancesOf bereits mehrmals in diesem Kapitel gesehen. Weiter oben in diesem Kapitel wurde gesagt, dass es nicht möglich ist, Instanzen von abstrakten Klassen (zum Beispiel CIM_Service) abzufragen. Dies stimmt zwar - das Standardverhalten der Methode InstancesOf könnte jedoch dafür sorgen, dass Sie dies nicht glauben. Standardmäßig führt InstancesOf eine "Tiefenabfrage" durch. Das bedeutet, dass InstancesOf alle Instanzen einer verwalteten Ressource abfragt deren Klasse Sie angegeben haben und zusätzlich auch alle Instanzen der Unterklassen unter der angegebenen Klasse. Das folgende Script fragt zum Beispiel alle Ressourcen ab, die über die die dynamische Klasse CIM_Service und alle Klasse unter dieser Klasse repräsentiert werden: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colSWbemObjectSet = objSWbemServices.InstancesOf("CIM_Service") For Each objSWbemObject In colSWbemObjectSet Wscript.Echo "Objektpfad: " & objSWbemObject.Path_.Path Next
Wenn Sie das Script ausführen, erhalten Sie nicht nur die Dienste des Computers zurück, sondern auch alle Unterklassen unter CIM_Service - inklusive Win32_SystemDriver und Win32_ApplicationService. ExecQuery Sie die Methode InstancesOf gibt auch ExecQuery immer eine SWbemObjectSet-Collection zurück[???]. Daher muss Ihr Script die von ExecQuery zurückgegebene Collection durchgehen, um auf die einzelnen Instanzen zuzugreifen: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colSWbemObjectSet = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service") For Each objSWbemObject In colSWbemObjectSet Wscript.Echo "Name: " & objSWbemObject.Name Next
Seite 361 von 394
Die anderen Methoden von SWbemServices, die eine SWbemObjectSet-Collection zurückgeben, sind AssociatorsOf, ReferencesTo und SubclassesOf. Get Im Gegensatz zu ExecQuery und InstancesOf gibt Get immer ein SWbemObject-Objekt zurück (dieses stellt eine spezifische Instanz einer verwalteten Ressource oder eine einzelne Klassendefinition dar). Um eine spezifische Instanz über die Get zu erhalten, müssen Sie Get den Objektpfad der Instanz übergeben: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set objSWbemObject = objSWbemServices.Get("Win32_Service.Name='Messenger'") Wscript.Echo "Name: " & "Angezeigter Name: " & "Starttyp: " & "Status: " &
objSWbemObject.Name & vbCrLf & _ objSWbemObject.DisplayName & vbCrLf & _ objSWbemObject.StartMode & vbCrLf & _ objSWbemObject.State
SWbemObjectSet Ein SWbemObjectSet-Objekt ist eine Collection mit SWbemObject-Objekten. Jedes SWbemObject-Objekt in der SWbemObjectSet-Collection kann eine der beiden folgenden Instanzen sein: • •
Eine Instanz einer verwalteten Ressource. Eine Instanz einer Klassendefinition.
Meistens ist das Einzige, was Sie mit einer SWbemObjectSet-Collection machen, das Auflisten der einzelnen Elemente der Collection. SWbemObjectSet jedoch stellt auch eine sehr nützliche Eigenschaft zur Verfügung (Count). Wie der Name schon sagt, gibt Count die Anzahl der Elemente der Collection zurück. Das folgende Script ruft zum Beispiel eine Collection mit allen installierten Diensten ab und gibt dann die Anzahl der gefundenen Dienste aus: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_Service") Wscript.Echo "Anzahl der installierten Dienste: " & colSWbemObjectSet.Count
Count ist deshalb so nützlich, weil Sie mit dieser Eigenschaft feststellen können, ob eine bestimmte Instanz auf einem Computer vorhanden ist. Das folgende Script ruft zum Beispiel eine Collection mit allen Diensten eines Computers auf, die den Namen W3SVC haben. Wenn Count den Wert 0 zurückgibt, dann heißt das, dass der Dienst auf diesem Computer nicht installiert ist. strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colSWbemObjectSet = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service WHERE Name='w3svc'") If colSWbemObjectSet.Count = 0 Then Wscript.Echo "Der Dienst W3SVC ist nicht installiert." Else For Each objSWbemObject In colSWbemObjectSet ' Das Script die gewünschten Aktionen durchführen ' lassen
Seite 362 von 394
Next End If
Eine Sache, die Sie bei der Verwendung von Count berücksichtigen sollen, ist das WMI keine 'Strichliste' über die Anzahl der Elemente einer Collection führt. Wenn Sie die Methode Count ausführen, kann WMI nicht sofort mit einer Zahl antworten. Stattdessen muss WMI die Elemente der Collection erst zählen. Bei einer Collection mit eine paar Elementen ist dies Verhalten sicher irrelevant - bei einer Collection mit 1.000 Elementen kann die Zählung jedoch eine ganz Weile dauern.
Schreiben von WMI-Scripten Ein Punkt, der bereits mehrmals erwähnt wurde ist, dass die meisten WMI-Scripte nach einem einfachen Muster mit drei Schritten vorgehen: 1.Sie verbinden sich mit dem WMI-Dienst. 2.Sie rufen ein WMI-Objekt oder eine Collection von WMI-Objekten ab. 3.Sie verarbeiten diese Objekte auf irgendeine Art und Weise. Bis jetzt wurden meist Eigenschaften der Objekte ausgegeben. Sie können jedoch auch Eigenschaften ändern, Methoden ausführen und Instanzen erstellen oder löschen. Damit Sie gute WMI-Scripte schreiben können, müssen Sie die Einzelheiten der drei Schritte verstehen. Der vorherige Abschnitt dieses Kapitels war sehr theoretisch - Sie haben Hintergrundinformationen dazu erhalten, was WMI ist und was man damit machen kann. In diesem Teil des Kapitels wollen wir uns einem mehr praktischen Ansatz zuwenden. Sie erhalten eine detaillierte Übersicht über die drei Schritte. 1.Die Verwendung des WMI-Monikers wird erklärt - eine vielseitige Methode, um eine Verbindung zum WMI-Dienst aufzubauen. 2.Die Verwendung von ExecQuery, einer Methode von SWbemServices, die eine Alternative Methode zur Abfrage von WMI-Daten zur Verfügung stellt, wird erklärt. 3.Sie erhalten Vorlagen, die Sie als Basis für eigene Scripte verwenden können. Außerdem werden zwei weitere wichtige Elemente von WMI-Scripten besprochen: Die Arbeit mit Daten- und Zeitwerten unter WMI und die Überwachung von Ereignissen.
Eine Verbindung zu WMI über den WMI-Moniker aufbauen Die WMI-Scripting-Bibliothek stellt zwei Mechanismen zur Verfügung, über die Sie eine Verbindung mit WMI aufbauen können: Das Objekt SWbemLocator und den WMI-Moniker 'winmgmts:'. Diese beiden Mechanismen unterscheiden sich außer durch ihre Syntax dadurch, dass Sie bei SWbemLocator einen Benutzernamen und ein Passwort angeben können und beim WMI-Moniker nicht. Das Objekt SWbemLocator wird im Abschnitt WMI Scripting Library dieses Kapitels genauer besprochen.
Das Prefix 'WinMgmts:' WMI-Moniker können aus drei Teilen bestehen: Einem verpflichtenden und zwei optionalen Teilen. Die verpflichtende Komponente ist das Prefix 'winmgmts:', mit dem alle WMIMoniker anfangen müssen: Seite 363 von 394
Set objSWbemServices = GetObject("winmgmts:")
Beim WMI-Moniker wird im Gegensatz zum ADSI-Provider übrigens nicht zwischen Großund Kleinschreibung unterschieden. Ein Moniker, der nur aus dem Prefix 'winmgmts:' besteht, ist die einfachste Form, die Sie verwenden können. Als Ergebnis erhalten Sie eine Referenz auf ein SWbemServices-Objekt, das eine Verbindung zum WMI-Dienst des lokalen Computers darstellt: 1.Es wird die WMI CLSID aus dem Registrierungsschlüssel HKCR\WINMGMTS\CLSID abgefragt. Die CLSID ({172BDDF8-CEEA-11D1-8B05-00600806D9B6}) ist eine ID, die vom Betriebssystem verwendet wird, um WMI dem entsprechenden COM-Objekt zuzuordnen. 2.Es wird ein zweiter Wert aus dem Registrierungsschlüssel HKCR\CLSID\{172BDDF8CEEA-11D1-8B05-00600806D9B6}\InProcServer32 abgefragt. Dieser Wert (normalerweise C:\Windows\System32\wbem\wbemdisp.dll) zeigt den Pfad zu dem COMObjekt an, dass das Objekt SWbemServices zur Verfügung stellt. 3.Die DLL Wbemdisp.dll wird geladen. Sie enthält die WMI-Scripting-Bibliothek und stellt das Objekt SWbemServices endgültig zur Verfügung. Nachdem eine Referenz auf ein SWbemServices-Objekt zur Verfügung steht, können Sie [folgende?]Methoden ausführen: Set objSWbemServices = GetObject("winmgmts:") Set colSWbemObjectSet = objSWbemServices.InstancesOf("Win32_LogicalDisk")
In diesem Beispiel wird eine Referenz-Variable mit dem Namen objSWbemServices mit dem Moniker 'winmgmts:' initialisiert. Diese Variable wird dann zum Aufrufen der Methode InstancesOf des SWbemServices-Objekts verwendet. Auch wenn das Beispiel nicht sehr lang ist, kann es durchaus noch kürzer werden. Sie können die gleiche Funktionalität zum Beispiel auch über eine Scriptzeile implementieren: Set colSWbemObjectSet = GetObject("winmgmts:").InstancesOf("Win32_LogicalDisk")
In diesem Beispiel wird keine Variable verwendet (objSWbemServices im vorherigen Beispiel), um die Referenz aufzunehmen. Stattdessen wird die vom Moniker erstellte Referenz auf das SWbemServices-Objekt sofort zum Aufrufen der Methode InstancesOf verwendet. Beide Beispiele produzieren das gleiche Ergebnis - eine SWbemObjectSet-Collection mit allen Instanzen der Klasse Win32_LogicalDisk des lokalen Computers.
WMI-Sicherheitseinstellungen Beim zweiten, optionalen Teil des WMI-Monikers handelt es sich um die Sicherheitseinstellungen. Die Sicherheitseinstellungen haben schon zu viel Verwirrung geführt. Dies liegt vor allem an der Einstellung Personifizierung (ImpersonationLevel) - diese verhält sich unter den unterschiedlichen Versionen von WMI verschieden. Über den Sicherheitsteil können Sie verschiedene Einstellungen angeben: • •
Personifizierungsebene - "winmgmts:{impersonationLevel=Wert}". Authentifizierungsebene - "winmgmts:{authenticationLevel=Wert}". Seite 364 von 394
• •
Authentifizierungsautorität - "winmgmts:{authority=NTLMDomäne:Domänenname}" oder "winmgmts:{authority=kerberos:Domänenname\Servername}". Gewährte oder verweigerte Rechte - zum Beispiel "winmgmts:{(Security, !RemoteShutdown)}".
Personifizierungsebene (ImpersonationLevel) Die ersten beiden Einstellungen (impersonationLevel und authenticationLevel) sind nicht WMI-spezifisch - sie werden von DCOM abgeleitet. WMI verwendet DCOM, um auf die WMI-Infrastruktur von Remotecomputern zuzugreifen. Im Kontext von WMI regelt die Personifizierung, wie weit der WMI-Dienst eines Remotecomputers Aktionen unter Ihrem Benutzerkontext ausführen darf. DCOM unterstützt vier Personifizierungsebenen: Anonymous, Identify, Impersonate und Delegate. Diese vier Ebenen sind in Tabelle 6.6 definiert. Tabelle 6.6: DCOM-Personifizierungsebenen Wert
Beschreibung
Anonymous Verbirgt die Anmeldeinformationen des Aufrufers. Diese Ebene wird von WMI im Moment nicht unterstützt. Wenn ein Script die Einstellung impersonationLevel=Anonymous verwendet, setzt WMI die Einstellung ohne Hinweis auf Identify. Identify
Ermöglicht Objekten die Anmeldeinformationen des Aufrufers abzufragen. Scripte, die diese Personifizierungsebene verwenden, werden wahrscheinlich fehlschlagen. Mit der Einstellung Identify können Sie normalerweise nicht mehr machen, als ACLs (Zugriffskontrolllisten - Access Control Lists) zu überprüfen. Sie sind mit Identify nicht in der Lage, ein Script gegen einen Remotecomputer auszuführen.
ImpersonateErmöglicht es Objekten die Anmeldeinformationen des Aufrufers auszulesen. In WMI-Scripten sollten Sie diese Personifizierungsebene verwenden. Mit ihr kann das Script Ihre Anmeldeinformationen verwenden. Delegate
Ermöglicht es Objekten, anderen Objekten das Auslesen der Anmeldeinformationen des Aufrufers zu gestatten. Mit der Einstellung Delegate kann ein Script Ihre Anmeldeinformationen auf einem Remotecomputer verwenden, und der Remotecomputer kann Ihre Anmeldeinformationen auf einem weiteren Remotecomputer verwenden. Diese Einstellung kann ein potentielles Sicherheitsrisiko darstellen. Wenn die in den Vorgang einbezogenen Benutzer- und Computerkonten in Active Directory nicht mit der Einstellung Für Delegierungszwecke vertrauen konfiguriert sind, Seite 365 von 394
Wert
Beschreibung
können Sie die Konten nicht verwenden. Der Vorgang wird in diesem Fall fehlschlagen. Die Einstellung Anonymous verhindert, wie gesagt, dass Ihre Anmeldeinformationen an ein Remoteobjekt weitergegeben werden. Die Einstellung Identify erlaubt es Remoteobjekten, die Anmeldeinformationen abzufragen. Das Remoteobjekt ist jedoch trotzdem nicht in der Lage, diese Informationen zu verwenden. Scripte, die eine dieser beiden Einstellungen verwenden, schlagen prinzipiell fehl - dies gilt sogar für die meisten Scripte, die auf einem lokalen Computer ausgeführt werden. Die Einstellung Delegate erlaubt es einem Remote-WMI-Dienst Ihre Anmeldeinformationen an Objekte weiterzugeben - dies ist ein generelles Sicherheitsrisiko. Das Standardverhalten der Einstellung Personifizierungsebene unterscheidet sich leider zwischen den unterschiedlichen WMI-Versionen. Die Versionen vor 1.5 verwenden als Standardeinstellung Identify. Seit Version 1.5 (mit Windows 2000 veröffentlicht) wurde die Standardeinstellung auf Impersonate geändert. Die Standardeinstellung wird über den folgenden Registrierungsschlüssel definiert: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WBEM\Scripting\Default Impersonation Level
Wenn Ihre WMI-Scripte nur mit Computern mit WMI-Version 1.5 oder höher arbeiten, brauchen Sie sich um die Einstellung ImpersonationLevel nicht kümmern. Um auch ältere Versionen zu unterstützen und um Fehler zu vermeiden, sollten Sie jedoch trotzdem in jedem Script die Einstellung impersonationLevel explizit angeben. Authentifizierungsebene (AuthenticationLevel) Diese Einstellung ermöglicht es Ihnen, die DCOM-Authentifizierung und -Sicherheit für eine bestimmte Verbindung anzugeben. Die Einstellungen reichen von keiner Authentifizierung bis zu einer verschlüsselten Authentifizierung pro Paket. Die sieben möglichen Einstellungen (Default, None, Connect, Call, Pkt, PktIntegrity und PktPrivacy) werden in Tabelle 6.7 beschrieben. Die Angabe der Einstellung Authentifizierungsebene sollten Sie mehr als Anfrage statt als Anweisung betrachten. Es gibt keine Garantie dafür, dass die angeforderte Authentifizierung auch tatsächlich verwendet wird. Tabelle 6.7: DCOM-Authentifizierungsebene Wert
Beschreibung
None
Keine Authentifizierung.
Default
Verwendet eine Standardsicherheitsaushandlung, um die Authentifizierungsebene festzulegen. Dies ist die empfohlene Einstellung.
Connect
Führt eine Authentifizierung nur dann durch, wenn der Client versucht sich mit einem Server zu verbinden. Nachdem die Verbindung aufgebaut wurde, wird keine weitere Authentifizierung mehr durchgeführt.
Call
Authentifiziert den Client nur bei Beginn eines jeden Aufrufs. Die Paketheader werden signiert. Die Datenpakete sind jedoch weder signiert Seite 366 von 394
Wert
Beschreibung
noch verschlüsselt. Pkt
Authentifiziert alle Datenpakete. Wie bei Call werden alle Header signiert, jedoch nicht verschlüsselt. Die Pakete selbst werden weder signiert noch verschlüsselt.
PktIntegrityAuthentifiziert alle Datenpakete. Jedes Datenpaket ist signiert. Kein Paket wird verschlüsselt. PktPrivacy Führt alle Authentifizierungen der anderen Stufen durch und verschlüsselt alle Datenpakete. Autorität (Authority) Diese Einstellung erlaubt es Ihnen, das Sicherheitsverfahren zur Authentifizierung der WMIVerbindung anzugeben. Sie können die NTLM- oder die Kerberos-Authentifizierung verwenden. Für NTLM verwenden Sie die Einstellung authority=NTLMDomäne:Domänenname, wobei Domänenname ein gültiger NTLMDomänenname sein muss. Um Kerberos zu verwenden, geben Sie die Einstellung authority=kerberos:Domänenname\Servername an. Berechtigungen Als letzte Einstellung können Sie Berechtigungen angeben. Diese Einstellung ermöglicht es Ihnen, Recht zu gewähren oder zu verweigern. Zum Beispiel könnten Sie sich selbst das Recht Security zuweisen, um unter Windows NT/2000 das Sicherheitsprotokoll abzufragen. Einige der möglichen Rechte sehen Sie in Tabelle 6.8. Tabelle 6.8: Berechtigungen Recht
Beschreibung
MachineAccount
Erforderlich, um ein Computerkonto zu erstellen.
Security
Erforderlich, um einige sicherheitsbezogene Funktionen auszuführen (zum Beispiel das Anzeigen von Überwachungsnachrichten).
TakeOwnership
Erforderlich, um den Besitz an einem Objekt zu übernehmen.
LoadDriver
Erforderlich, um Gerätetreiber zu laden und zu entladen.
SystemProfile
Erforderlich, um das Systemprofil abzufragen.
SystemTime
Erforderlich, um die Systemzeit zu ändern.
ProfileSingleProcessErforderlich, um Informationen über einen Prozess abzufragen. IncreaseBasePriorityErforderlich, um die Basispriorität Seite 367 von 394
Recht
Beschreibung
eines Prozesses zu erhöhen. CreatePagefile
Erforderlich, um eine Auslagerungsdatei zu erstellen.
Backup
Erforderlich, um Sicherungsoperationen durchzuführen.
Restore
Erforderlich, um Wiederherstellungen durchzuführen.
Shutdown
Erforderlich, um den lokalen Computer herunterzufahren.
Audit
Erforderlich, um Einträge im Überwachungsprotokoll zu generieren.
RemoteShutdown
Erforderlich, um einen Computer über das Netzwerk herunterzufahren.
Undock
Erforderlich, um aus der Dockingstation zu entfernen.
SyncAgent
Erforderlich, zur Synchronisierung von Verzeichnisdienstdaten.
Wenn Sie ein Recht verweigern möchten, können Sie dies tun, indem Sie dem Recht ein Ausrufezeichen (!) voranstellen. Der folgende Moniker gewährt zum Beispiel das Recht LockMemory und verweigert das Recht IncreaseQuota: Set objSWbemServices = GetObject _ ("winmgmts:{impersonationLevel=impersonate,(LockMemory, !IncreaseQuota)}")
Wenn Sie in einem Script Rechte gewähren, werden diese nicht auf den Benutzer übertragen. Nehmen wir zum Beispiel an, Sie führen ein Script aus, das das Recht Shutdown gewährt. Wenn Sie nicht bereits über das Recht verfügen, können Sie auch über ein Script dieses Recht nicht wahrnehmen. Um ein Recht wahrnehmen zu können, müssen zwei Bedingungen erfüllt sein: • •
Das Script muss das Recht im Verbindungsstring gewähren. Sie müssen bereits über das Recht verfügen.
Moniker mit Sicherheitseinstellungen Das Format für Moniker mit Sicherheitseinstellungen sieht folgendermaßen aus: Set objSWbemServices = GetObject("winmgmts:" "{SecuritySetting1=Wert," "SecuritySetting2=Wert," "(Recht1,!Recht2)}")
& _ & _ & _
Seite 368 von 394
Verwenden von WMI-Objektpfaden Bei der dritten Komponente des WMI-Monikers handelt es sich um den WMI-Objektpfad. Für die Sicherheitseinstellungen ist diese Komponente optional. Mit WMI-Objektpfaden können Sie eine oder mehrere der folgenden Zielressourcen eindeutig identifizieren: •
Remotecomputer - Wenn der Computer im Objektpfad nicht angegeben wird, dann wird das Script gegen den lokalen Computer ausgeführt: Set objSWbemServices = GetObject("winmgmts:") Der folgenden Moniker verbindet sich mit dem WMI-Dienst des Remotecomputers atl-dc01: Set objSWbemServices = GetObject("winmgmts:\\atl-dc-01") Computernamen werden normalerweise über den NetBIOS-Namen des Computers angegeben. DNS-Namen und IP-Adressen sind jedoch ebenfalls möglich (zum Beispiel atldc-01.fabrikam.com oder 192.168.1.1).
•
WMI-Namespace - Wenn der Namespace nicht angegeben wird, dann wird der StandardNamespace verwendet. Der folgende Moniker verbindet sich mit dem Standard-Namespace auf dem Remotecomputer atl-dc-01: Set objSWbemServices = GetObject("winmgmts:\\atl-dc-01") Dieser Moniker verbindet sich mit dem Namespace root\default auf dem Computer atl-dc01: Set objSWbemServices = GetObject("winmgmts:\\atl-dc-01\root\default")
•
Eine WMI-Klasse in einem Namespace - Wenn in einem Moniker eine Klasse angegeben wird, muss dieser von Computername und Namespace durch einen Doppelpunkt getrennt werden. Der folgende Moniker verbindet sich zum Beispiel mit der Klasse Win32_OperatingSystem: Set objSWbemObject = GetObject _ ("winmgmts:\\atl-dc-01\root\cimv2:Win32_OperatingSystem")
•
Eine Spezifische Instanz oder Instanzen einer WMI-Klasse in einem Namespace - Der folgende Moniker verbindet sich direkt mit der WMI-Instanz von Laufwerk C: Set objSWbemObject = GetObject _ ("winmgmts:\\atl-dc-01\root\cimv2:Win32_LogicalDisk.DeviceID='C:'")
Format von Objektpfaden In einem Objektpfad werden Computername und WMI-Namespace durch die Zeichen Slash oder Backslash getrennt. Gültige Objektpfade sind zum Beispiel: \\WebServer\root\cimv2 //WebServer/root/cimv2
Anmerkung: Sie können die beiden Formen sogar mischen. Dies wird jedoch nicht empfohlen, da Ihr Scriptcode so schwerer lesbar wird. Trotzdem wäre der folgende Objektpfad gültig: \\Webserver/root/cimv2. Wenn im Objektpfad eine WMI-Klasse angegeben wird, dann muss die Klasse vom Namespace durch einen Doppelpunkt getrennt werden: \\WebServer\root\cimv2:Win32_Printer
Seite 369 von 394
Um eine direkte Instanz einer Klasse (zum Beispiel einen bestimmten Dienst) anzugeben, müssen Sie den Klassennamen und durch einen Punkt (.) getrennt die Schlüsseleigenschaft angeben. Dann folgt der Wert dieser Eigenschaft der gesuchten Instanz: \\WebServer\root\cimv2:Win32_Service.Name="Alerter"
Schlüsseleigenschaften Die Schlüsseleigenschaft ist eine Eigenschaft, die zur eindeutigen Identifizierung einer Instanz verwendet werden kann. Die Schlüsseleigenschaft der Klasse Win32_Service ist zum Beispiel die Eigenschaft Name - und zwar darum, weil jeder Dienst einen eindeutigen Namen hat. Die Eigenschaft StartMode ist hingegen keine Schlüsseleigenschaft - der Starttyp kann bei vielen Diensten gleich sein. Der folgende Verbindungsstring schlägt daher fehl: Set colServices = GetObject _ ("winmgmts:\\.\root\cimv2\Win32_Service.StartMode='Auto'")
Wenn Sie versuchen dieses Script auszuführen, erhalten Sie die Fehlermeldung 'Ungültiger Syntax': Anmerkung: Was ist, wenn Sie eine Liste der Dienste mit dem Starttyp Automatisch benötigen? Dies können Sie über die Methode ExecQuery durchführen. Die Methode wird weiter unten in diesem Kapitel im Abschnitt Abfragen von verwalteten Ressourcen über die WMI-Abfragesprache besprochen. Klassen haben normalerweise nur eine Schlüsseleigenschaft - auch wenn es ein paar Ausnahmen gibt. Die Klasse Win32_NTLogEvent hat zum Beispiel zwei Schlüsseleigenschaften: Logfile und RecordNumber. Dies liegt daran, dass RecordNumber einen Eintrag in einem Ereignisprotokoll nicht eindeutig identifiziert. Es kann in den unterschiedlichen Protokollen Einträge mit den gleichen Nummern geben. Wenn Sie einen solchen Eintrag aufrufen wollen, müssen Sie daher beide Schlüsseleigenschaften angeben: \\WebServer\root\cimv2:Win32_NTLogEvent.Logfile='Application',RecordNumber= 555
Abfragen von verwalteten Ressourcen über die WMI-Abfragesprache Über WMI-Abfragen können Sie Daten oder Ereignisse nach vordefinierten Kriterien abrufen. Eine WMI-Abfrage kann zum Beispiel alle Dienste mit dem Starttyp Automatisch, die im Moment angehalten sind, zurückgeben. Eine WMI-Ereignisabfrage können Sie verwenden, um zum Beispiel beim Anhalten und Starten von Diensten benachrichtigt zu werden. Da der WMI-Abfrageprozessor ein Teil des WMI-Dienstes ist, können Sie WMI auf alle Daten des CIM abfragen. WMI-Abfragen sind ein effizienterer Mechanismus zum Abfragen von Objektinstanzen als die Methode InstancesOf. WMI-Abfragen geben nur die Instanzen und Daten zurück, die der Abfrage entsprechen. InstancesOf gibt hingegen alle Objektinstanzen einer Klasse zurück. Abfragen werden außerdem auf dem Zielcomputer im Objektpfad ausgeführt statt auf dem Computer, auf dem das Script gestartet wurde. Daher verringern WMI-Abfragen den Netzwerkverkehr. Um WMI-Abfragen durchzuführen, erstellt der Administrator einen Abfragestring mit der WMI-Abfragesprache (WMI Query Language - WQL). Der Abfragestring definiert die Kriterien der zurückzugebenden Instanzen und Daten. Der Abfragestring wird dann über eine von mehreren Methoden des SWbemServices-Objekts an WMI übergeben. Das Script erhält die Ergebnisse der Abfrage über eine SWbemObjectSet-Collection zurück. Seite 370 von 394
WQL ist eine Untermenge von ANSI-SQL (Structured Query Language). SQL wird normalerweise in Datenbanken verwendet. Der Hauptzweck von WQL ist das Abfragen von Informationen. SQL-Funktionen wie UPDATE und DELETE werden von WQL nicht unterstützt. Außerdem können Sie mit WQL keine Sortierreihenfolge angeben. Mit WQL und der Methode ExecQuery können Sie viel flexiblere Scripte als mit InstancesOf schreiben. Sie erhalten nur die Elemente, die für Sie interessant sind. Sie können zum Beispiel eine grundlegende WQL-Abfrage verwenden, um allen Eigenschaften aller Instanzen einer bestimmten Klasse abzufragen - diese Informationen würden Sie jedoch auch über die Methode InstancesOf erhalten. Mit WQL können Sie jedoch deutlich mehr machen. Zum Beispiel: • • •
Abfragen von ausgewählten Eigenschaften für alle Instanzen einer Klasse. Abfragen von allen Eigenschaften für ausgewählte Instanzen einer Klasse. Abfragen von ausgewählten Eigenschaften für ausgewählte Instanzen einer Klasse.
Solche zielgerichteten Abfragen beschleunigen die Geschwindigkeit von Abfragen in einigen Situationen deutlich - auch die Arbeit mit den zurückgegebenen Daten wird einfacher. Zielgerichtete Anfragen können außerdem die zurückgegebenen Daten deutlich einschränken. Bei Scripten, die über das Netzwerk ausgeführt werden, ist dies unter umständen sehr wichtig. In Tabelle 6.9 sehen Sie einige Werte für unterschiedliche Abfragetypen. Wie Sie sehen, ist die zurückgegebene Datenmenge teilweise sehr unterschiedlich. Tabelle 6.9: Vergleich von WMI-Abfragen Abfrage
Bytes zurückgegeben
objSWbemServices.Instances Of("Win32_Service")
157.398
objSWbemServices.ExecQuery ("SELECT * FROM Win32_Service")
156.222
objSWbemServices.ExecQuery ("SELECT Name FROM Win32_Service")
86.294
objSWbemServices.ExecQuery 88.116 ("SELECT StartMode FROM Win32_Service") objSWbemServices.ExecQuery _ ("SELECT StartMode FROM Win32_ Service WHERE State='Running'")
52.546
objSWbemServices.ExecQuery _ ("SELECT StartMode, State FROM Win32_ Service WHERE State='Running'")
56.314
objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service WHERE Name='WinMgmt'")
27.852
objSWbemServices.Get("Win32_ Service.Name='WinMgmt'")
14.860
Seite 371 von 394
Anmerkung: Das bedeutet nicht notwendigerweise, dass die Abfragen mit den wenigsten Daten die beste Lösung darstellen. Sie geben einfach nicht alle Eigenschaften zurück. Wenn Sie alle Eigenschaften benötigen, bleibt Ihnen nichts anderes übrig, als eine andere Abfrage zu verwenden.
Abfragen aller Eigenschaften aller Instanzen einer Klasse Die einfachste WQL-Abfrage ist die, die alle Eigenschaften einer Instanz einer Klasse abfragt. Hierzu gehen Sie folgendermaßen vor: • •
Sie verwenden einen Stern (*), um anzuzeigen, dass Sie alle Eigenschaften abfragen wollen. Sie geben den Namen der Klasse an.
Diese Abfrage gibt zum Beispiel alle Eigenschaften aller installierten Dienste zurück: "SELECT * FROM Win32_Service"
Der Vorteil einer SELECT * Abfrage ist, dass dieses Verfahren schnell und einfach ist. Der Nachteil ist, dass Sie möglicherweise viel mehr Informationen zurückerhalten, als Sie benötigen. Eine Abfrage, die zum Beispiel alle Eigenschaften aller installierten Laufwerke zurückgibt, produziert ca. 808 Byte an Daten. Eine Abfrage, die nur die Eigenschaft Name zurückgibt, produziert nur 145 Byte an Daten. Die Abfrage SELECT * wird in weniger als zwei Sekunden ausgeführt. Die Abfrage SELECT Name benötigt jedoch nur weniger als eine Sekunde. Möglicherweise finden Sie den Unterschied zwischen den beiden Scripten unbedeutend. In anderen Fällen kann der Unterschied jedoch dramatisch sein. Ein Script, das alle Eigenschaften aller auf einem Computer ausgeführten Threads anzeigt, benötigt hierzu ca. 7 Sekunden und gibt 105 KB Daten zurück. Ein Script, das nur das Thread-Handle zurückgibt, benötigt nur eine Sekunde und gibt nur 6 KB Daten zurück. Script 6.18 verwendet eine Standard-WQL-Abfrage, die alle Eigenschaften aller installierten Dienste zurückgibt. Script 6.18: Abfragen aller Eigenschaften aller Instanzen einer Klasse 1 strComputer = "." 2 Set objSWbemServices = _ 3 GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 4 5 Set colServices = _ 6 objSWbemServices.ExecQuery("SELECT * FROM Win32_Service") 7 8 For Each objService In colServices 9 Wscript.Echo objService.Name 10Next
Abfragen ausgewählter Eigenschaften aller Instanzen einer Klasse Wie Sie bereits wissen, wird ein Script schneller ausgeführt und gibt weniger überflüssige Daten zurück, wenn Sie nur bestimmte Eigenschaften einer Klasse abfragen. Hierzu ersetzen Sie in der Abfrage den Stern durch die Namen der zurückgegebenen Eigenschaften. Die folgende Abfrage gibt nur die Eigenschaft Name aller Instanzen der Klasse Win32_Service zurück: Seite 372 von 394
"SELECT Name FROM Win32_Service"
Wenn Sie mehrere Eigenschaften zurückgeben möchten, dann trennen Sie die Eigenschaftsnamen durch Kommata. Die folgende Abfrage gibt zum Beispiel die Eigenschaften Name und State der Klasse Win32_Service zurück: "SELECT Name, State FROM Win32_Service"
Was bedeutete es, nur ausgewählte Eigenschaften zurückzugeben? Sehen Sie sich das folgende Script an. Es ruft nur die Eigenschaft Name der Klasse Win32_Service ab, versucht jedoch auch die State auszugeben: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT Name FROM Win32_Service") For Each objService In colServices Wscript.Echo objService.Name, objService.State Next
Wenn Sie das Script unter CScript ausführen, erhalten sie die folgende Fehlermeldung: Laufzeitfehler in Microsoft VBScript: Das Objekt unterstützt diese Eigenschaft oder Methode nicht: 'objService.State'
Warum diese Fehlermeldung? State ist zwar eine gültige Eigenschaft der Klasse Win32_Service, Sie haben Sie jedoch nicht abgefragt. Daher ist die Eigenschaft State in der Collection nicht vorhanden. Da das Script die zurückgegebene Collection und nicht die Klasse selbst abfragt, kommt es zu dem Fehler. Es gibt eine Ausnahme: Die Schlüsseleigenschaft wird immer zurückgegeben - auch dann, wenn Sie in der WQL-Abfrage nicht angegeben wurde. Name ist zum Beispiel die Schlüsseleigenschaft der Klasse Win32_Service. Das folgende Script fragt nur die Eigenschaft State ab. Bei der Ausgabe kann es jedoch auch die Eigenschaft Name verwenden: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT State FROM Win32_Service") For Each objService In colServices Wscript.Echo objService.Name, objService.State Next
Wenn Sie das Script unter Cscript ausführen, schlägt es nicht fehl. Stattdessen erhalten Sie die folgende Ausgabe: Alerter Stopped ALG Stopped AppMgmt Stopped Ati HotKey Poller Stopped AudioSrv Running BITS Running
Abfragen aller Eigenschaften für ausgewählten Instanzen einer Klasse Die beiden bisherigen WQL-Abfragen haben Informationen für jede Instanz einer bestimmten Klasse zurückgegeben. Oft benötigen Sie jedoch nur eine Untermenge der Instanzen - zum Seite 373 von 394
Beispiel, wenn Sie nur Informationen über die Dienste erhalten möchten, die angehalten wurden oder nur über Festplatten (nicht über Diskettenlaufwerke oder CD-Rom-Laufwerke). In solchen Situationen ist eine Rückgabeliste mit allen Instanzen kontraproduktiv. Sie müssen die Liste erst nach den gewünschten Instanzen durchsuchen. In anderen Fällen kann die Beschränkung auf einzelne Instanzen einer Klasse einen deutlichen Zeitunterschied ausmachen. Stellen Sie sich vor, Sie möchten ein Ereignisprotokoll mit ca. 48.000 Einträgen abfragen. Ein Script, das nur 4.000 Einträge abfragt, würde schon ca. 18 Sekunden benötigen. Und wie lange braucht das Script für die gesamten 48.000 Einträge? Das weiß leider keiner - nach 41.592 Einträgen ist das Script wegen Speichermangel abgestürzt. Anmerkung: Auch wenn es schon schlimm genug ist, dass das Script abstürzt - es kommt noch schlimmer. Auch wenn das Script abgestürzt ist, führt WMI die Abfrage weiter aus. Somit kommt das ganze System zum Stillstand. Der einzige Weg, dies zu beenden, wäre ein Neustart des WMI-Dienstes. Sie sehen also: In diesem Fall ist der Unterschied tatsächlich dramatisch. Ein Script, das nur die Instanzen mit dem Ereigniscode 532 zurückgibt, ist in wenigen Sekunden beendet. Um eine solche Abfrage zu erstellen, benötigen Sie eine WHERE-Bedingung: "SELECT * FROM Klassename WHERE Eigenschaftsname = Eigenschaftswert"
Anmerkung: Dieser Beschreibung ist nicht vollkommen richtig. Außer dem Gleichzeichen (=) können Sie auch noch andere Operatoren verwenden - zum Beispiel kleiner (), größer () und ungleich (). Die folgende Abfrage gibt zum Beispiel nur die Einträge zurück, die im Systemprotokoll aufgezeichnet wurden: "SELECT * FROM Win32_NTLogEvent WHERE Logfile = 'System'"
Die Abfrage setzt sich folgendermaßen zusammen: •
Win32_NTLogEvent ist die abzufragende WMI-Klasse.
•
Logfile ist die Eigenschaft.
•
System ist der gesuchte Wert der Eigenschaft Logfile. Nur die Ereignisse, bei denen die Eigenschaft Logfile den Wert System hat, werden zurückgeben.
Wenn Sie Abfragen mit einer WHERE-Bedingung schreiben, müssen Sie darauf achten, den Wert richtig zu formatieren. Das verwendete Format hängt von der abgefragten Eigenschaft ab (String, Zahl, Boolean-Wert, usw.). Verwenden von Strings in WHERE-Bedingungen Vielleicht ist Ihnen aufgefallen, dass der Wert (System) in der vorhergehenden Abfrage in Hochkommata eingeschlossen war. Dies ist immer dann erforderlich, wenn der Wert für die WHERE-Bedingung ein String ist. Das folgende Script gibt zum Beispiel die Namen aller Dienste zurück, die angehalten sind: •
Win32_Service ist die WMI-Klasse, die abgefragt wird.
Seite 374 von 394
•
State ist die Eigenschaft.
•
Stopped ist der gesuchte Wert der Eigenschaft. Da es sich um einen String handelt muss dieser in Hochkommata eingeschlossen werden:
strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service WHERE State = 'Stopped'") For Each objService In colServices Wscript.Echo objService.Name Next
Verwenden von Zahlen- und Boolean-Werten in WHERE-Bedingungen Wenn Sie Zahlen oder Boolean-Werte (True oder False) verwenden, dann müssen diese nicht in Hochkommata eingeschlossen werden. Das folgende Script fragt zum Beispiel alle Dienste ab, die angehalten werden können: •
Win32_Service ist die abgefragte WMI-Klasse.
•
AcceptPause ist die Eigenschaft.
•
True ist der gesuchte Wert der Eigenschaft.
strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service WHERE AcceptPause = True") For Each objService In colServices Wscript.Echo objService.Name Next
Verwenden von Variablen in einer WHERE-Bedingung Statt ein Script zu schreiben, das immer die gleichen Informationen abfragt, können Sie das Script so verändern, dass Sie die gesuchten Informationen über Kommandozeilenargumente angeben können. Hierzu müssen Sie den gesuchten Wert in einer Variablen speichern und diese dann in der WQL-Abfrage verwenden. Nehmen wir einmal an, die gewünschte Abfrage soll so aussehen: SELECT * FROM Win32_Service WHERE State = 'Stopped'"
Diese Abfrage können wir nun verändern. Statt den String direkt in die Abfrage einzutragen, können Sie diesen auch in einer Variablen speichern. strState = "Stopped"
Diese variable können Sie nun in der Abfrage verwenden: "SELECT * FROM Win32_Service WHERE State ='" & strState & "'"
Wie Sie wissen, können Sie Strings mit dem kaufmännischen Und verketten. Die gilt natürlich auch für eine Mischung aus Strings und Variablen, die Strings enthalten. Wenn Sie das nächste Script unter dem Namen ServiceState.vbs speichern, können Sie es mit der folgenden Befehlszeile aufrufen: Seite 375 von 394
cscript servicestate.vbs running strState = Wscript.Arguments.Item(0) strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service WHERE State = '" & strState & "'") For Each objService In colServices Wscript.Echo objService.Name Next
Im Script wird die Zeile mit der WQL-Abfrage dann zu folgender Abfrage aufgelöst: ("SELECT * FROM Win32_Service WHERE State = 'running'")
Das Script liefert so alle im Moment ausgeführten Dienste zurück.
Zielgerichtete Abfragen über AND oder OR erstellen Mit den Schlüsselwörtern AND und OR können Sie komplexere Abfragen erstellen. Mit dem Schlüsselwort AND können Sie den Bereich einer Abfrage einschränken. Nehmen wir zum Beispiel einmal an, Sie möchten eine Liste der Dienste mit dem Starttyp Automatisch, die angehalten sind, abfragen. Hierzu sind in der WHERE-Bedingung zwei Parameter notwendig - die Eigenschaft State, die den Wert Stopped haben muss und die Eigenschaft StartMode, die den Wert Auto haben muss. Die beiden Parameter müssen Sie mit dem Schlüsselwort AND verknüpfen: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service WHERE State = 'Stopped' AND StartMode = 'Auto'") For Each objService In colServices Wscript.Echo objService.Name Next
Mit dem Schlüsselwort OR können Sie hingegen den Abfragebereich erweitern. Nehmen wir an, Sie benötigen alle Dienste, die den Status stopped oder paused haben. In diesem Fall verwenden Sie das Schlüsselwort OR. strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Service WHERE State = 'Stopped' OR State = 'Paused'") For Each objService In colServices Wscript.Echo objService.Name Next
Seite 376 von 394
Ausgewählte Eigenschaften für ausgewählte Instanzen einer Klasse zurückgeben Sie haben die Möglichkeit, nur ausgewählte Eigenschaften für ausgewählte Klassen zurückzugeben. Hierzu gehen Sie folgendermaßen vor: • •
Geben Sie die zurückzugebenden Eigenschaften an. Schränken Sie die zurückgegebenen Instanzen über eine WHERE-Bedingung ein.
Eine solche Abfrage ist sozusagen eine Kombination aus zwei Abfragen. Das folgende Script gibt nur die Eigenschaften Name und State der Dienste zurück, die angehalten werden können: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2") Set colServices = objSWbemServices.ExecQuery _ ("SELECT Name, State FROM Win32_Service WHERE AcceptPause = True") For Each objService In colServices Wscript.Echo objService.Name, objService.State Next
Schnellere Abfragen über Forward-only erzeugen In einigen Fällen können Sie eine Abfrage über die Option Forward-Only beschleunigen. Unter Windows 2000 benötigt ein Script zur Abfrage von 10.000 Ereignissen zum Beispiel 47 Sekunden. Mit der Option Forward-Only sind es nur noch 28 Sekunden. Die Differenz von 19 Sekunden scheint nicht sehr groß zu sein, aber wenn Sie das Script auf allen 50 Domänencontrollern ausführen, sieht die Sache ganz anders aus. Für die Forward-Only-Auflistung von Elementen müssen Sie der Methode ExecQuery drei Parameter angeben. Der erste Parameter ist die WQL-Abfrage. Der zweite Parameter gibt die Abfragesprache an - sie sollten Ihn entweder nicht definieren oder hier 'WQL' angeben. Der dritte Parameter ist der Wert 48. Er steht für zwei Flags (Schalter): • •
wbemFlagReturnImmediately (Dezimalwert 16) - Dieser Schalter sorgt dafür, dass ExecQuery semisynchron ausgeführt wird. wbemFlagForwardOnly (Dezimalwert 32) - Dieser Schalter sorgt dafür, dass eine ForwardOnly-Collection zurückgeben wird.
Eine Forward-Only-Auflistung wird normalerweise schneller als eine normale Abfrage ausgeführt - außerdem benötigt sie weniger Speicher. Wie Sie wissen, wird jedoch jedes Element einer solchen Collection nach der ersten Abfrage aus dem Speicher entfernt. Sie können also nicht zweimal auf ein Element zugreifen. Das folgende Script verwendet das Forward-Only-Flag und gibt Ereignisse aus dem Ereignisprotokoll zurück: Const wbemFlagReturnImmediately = 16 Const wbemFlagForwardOnly = 32 strComputer = "." Set objSWbemServices = _ GetObject("winmgmts:{(Security)}\\" & strComputer & "\root\cimv2")
Seite 377 von 394
Set colNTLogEvents = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_NTLogEvent", , _ wbemFlagReturnImmediately + wbemFlagForwardOnly) For Each objNTLogEvent In colNTLogEvents Wscript.Echo "LogFile: " & objNTLogEvent.LogFile vbCrLf & _ "RecordNumber: " & objNTLogEvent.RecordNumber vbCrLf & _ "Type: " & objNTLogEvent.Type vbCrLf & _ "TimeGenerated: " & objNTLogEvent.TimeGenerated vbCrLf & _ "Source: " & objNTLogEvent.SourceName vbCrLf & _ "Category: " & objNTLogEvent.Category vbCrLf & _ "CategoryString: " & objNTLogEvent.CategoryString vbCrLf & _ "Event: " & objNTLogEvent.EventCode vbCrLf & _ "User: " & objNTLogEvent.User vbCrLf & _ "Computer: " & objNTLogEvent.ComputerName vbCrLf & _ "Message: " & objNTLogEvent.Message vbCrLf Next
& _ & _ & _ & _ & _ & _ & _ & _ & _ & _ & _
Wenn Sie das Forward-Only-Flag verwenden, können Sie die Methode SWbemObjectSet.Count nicht mehr verwenden. Die Methode Count zählt die Elemente einer Collection, indem es sie einzeln durchgeht. Hierbei werden die Elemente einer Forward-OnlyCollection natürlich alle aus dem Speicher entfernt.
Arbeiten mit Datum- und Zeitwerten Die Handhabung von Daten und Zeiten durch WMI ist etwas verwirrend. Das folgende einfache Script gibt zum Beispiel das Installationsdatum des Betriebssystems zurück: strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2") Set colOS = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_OperatingSystem") For Each objOS in colOS Wscript.Echo objOS.InstallDate Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie die folgende Ausgabe: 20011224113047.000000+60
Das ist kein Fehler - die Ausgabe des Scripts ist tatsächlich 20011224113047.000000+60. Dieser Wert besagt, dass das Betriebssystem am 24. Dezember 2001 installiert wurde. Das Datum ist korrekt - das Problem ist, dass das Datum im UTC-Format angezeigt wird (Universal Time Coordinate). Im UTC-Format werden Daten so angezeigt: yyyymmddHHMMSS.xxxxxx±UUU. Die einzelnen Buchstaben bedeuten: •
yyyy das Jahr. Seite 378 von 394
•
mm der Monat.
•
dd der Tag.
•
HH die Stunde (im 24-Stunden Format).
•
MM die Minuten.
•
SS die Sekunden.
•
xxxxxx die Millisekunden.
•
±UUU die Differenz der lokalen Zeitzone und Greenwich Mean Time (GMT) in Sekunden.
Der Wert 20011224113047.000000+60 kann also so übersetzt werden: •
2001 Jahr.
•
12 Monat (Dezember).
•
24 Tag.
•
11 Stunden.
•
30 Minuten.
•
47 Sekunden.
•
000000 Millisekunden.
•
-480 Differenz der lokalen Zeitzone zu GMT.
Bei einem solchen Format ist es natürlich nicht ganz einfach, Daten und Uhrzeiten abzufragen. Stellen Sie sich vor, Sie möchten eine Liste mit allen Ordnern abfragen, die nach dem 3.12.2002 erstellt wurden. Klingt einfach - die Klasse Win32_Directory stellt schließlich eine Eigenschaft mit dem Namen CreationDate zur Verfügung. Unglücklicherweise können Sie als Datum nicht einfach 3/9/2002 angeben. Das folgende Script versucht dies und gibt keine Daten zurück: dtmTargetDate = #3/9/2002# strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2") Set colDirectories = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Directory WHERE CreationDate > '" & _ dtmTargetDate & "'") For Each objDirectory In colDirectories Wscript.Echo objDirectory.Name Next
Stattdessen müssen Sie das Datum im UTC-Format angeben: dtmTargetDate = "20020903000000.000000+60" strComputer = "." Set objSWbemServices = GetObject("winmgmts:\\" & _ strComputer & "\root\cimv2")
Seite 379 von 394
Set colDirectories = objSWBemServices.ExecQuery _ ("SELECT * FROM Win32_Directory WHERE CreationDate > '" & _ dtmTargetDate & "'") For Each objDirectory In colDirectories Wscript.Echo objDirectory.Name Next
Aufgrund dieser Probleme müssen Sie zwei Aufgaben ausführen können: • •
Konvertieren von WMI-Datumwerten in das Standardformat. Konvertieren von Standard-Datumwerten in das WMI-Format.
WMI-Datumwerte in das Standardformat konvertieren Auch wenn UTC-Datumwerte zuerst ein wenig verwirrend aussehen, sind sie doch relativ einfach zu konvertieren. Das liegt erstens daran, dass die Datumswerte in Form von Strings vorliegen und damit auch mit Stringfunktionen bearbeitet werden können - und zweitens daran, dass UTC ein Format mit festen Größen verwendet. Das Jahr ist zum Beispiel immer vier Ziffern lang. In Tabelle 6.10. sehen Sie diese festen Größen. Tabelle 6.10: Zeichenpositionen im UTC-Format ZeichenpositionBeschreibung
Beispielwert
1-4
Jahr - vier Zeichen.
2002
5-6
Monat - zwei Zeichen.
10
7-8
Tag - zwei Zeichen.
18
9-14
Stunden, Minuten und Sekunden - 000000 sechs Zeichen.
15
Ein Punkt
.
16-21
Millisekunden - sechs Zeichen.
000000
22-25
Unterschied zu GMT - drei Zeichen.-480
Um ein Datum zu konvertieren wählen Sie einfach die entsprechenden Teile aus. Das UTCDatum 20020710113047.000000+60 können Sie zum Beispiel so konvertieren: 1.Tag extrahieren (10). 2.Monat extrahieren (07). 3.Jahr extrahieren (2002). 4.Die extrahierten Werte zum Standardformat zusammensetzen: 10/07/2002. Sie können die einzelnen Komponenten über die VBScript-Funktionen Left und Mid extrahieren (diese Funktionen werden im Kapitel VBScript besprochen). Um zum Beispiel den Tag zu extrahieren (die Zeichen an Position 7 und 8) verwenden Sie die folgende Codezeile (dtmInstallDate ist die Variable, die den zu konvertierenden Wert enthält): Mid(dtmInstallDate, 7 ,2)
Seite 380 von 394
Die folgende Funktion konvertiert ein komplettes UTC-Datum in ein Standarddatum. Es führt die folgenden Schritte durch: WMIDateStringToDate = CDate(Mid(dtmInstallDate, 5, 2) & "/" & _ Mid(dtmInstallDate, 7, 2) & "/" & Left(dtmInstallDate, 4) _ & " " & Mid (dtmInstallDate, 9, 2) & ":" & _ Mid(dtmInstallDate, 11, 2) & ":" & Mid(dtmInstallDate, _ 13, 2))
Für den UTC-Wert 20020219145216.000000-480 wirddas folgende Datum zurückgegeben: 2/19/02 2:52:16 PM
Script 6.19 fragt das Datum ab, an dem das Betriebsystem installiert wurde. Das Datum wird über die Funktion WMIDateStringToDate in ein Standarddatum konvertiert. Script 6.19: Konvertieren eine UTC-Werts in ein Standarddatum 1 strComputer = "." 2 Set objSWbemServices = GetObject("winmgmts:\\" & strComputer & 3 "\root\cimv2") 4 Set objOS = objSWbemServices.ExecQuery("SELECT * FROM 5 Win32_OperatingSystem") 6 For Each strOS in objOS 7 dtmInstallDate = strOS.InstallDate 8 strReturn = WMIDateStringToDate(dtmInstallDate) 9 Wscript.Echo strReturn 10Next 11Function WMIDateStringToDate(dtmInstallDate) 12 WMIDateStringToDate = CDate(Mid(dtmInstallDate, 5, 2) & "/" & _ 13 Mid(dtmInstallDate, 7, 2) & "/" & Left(dtmInstallDate, 4) _ 14 & " " & Mid (dtmInstallDate, 9, 2) & ":" & _ 15 Mid(dtmInstallDate, 11, 2) & ":" & Mid(dtmInstallDate, _ 13, 2)) End Function
Ein Standarddatum in ein WMI-Datum konvertieren Um ein WMI-Datum zu erstellen, sind zwei Schritte erforderlich. Erstens müssen Sie die Differenz in Minuten zu GMT feststellen, und zweitens müssen Sie das Datum zum einem UTC-Wert konvertieren. Feststellen der Differenz zu GMT Glücklicherweise ist es ganz einfach, diese Differenz über WMI abzufragen. Die Klasse Win32_TimeZone stellt hierzu eine Eigenschaft mit dem Namen Bias zur Verfügung. Script 6.20 zeigt, wie diese Eigenschaft abgefragt werden kann. Script 6.20: Abfragen der Differenz zu GMT 1strComputer = "." 2Set objSWbemServices = GetObject("winmgmts:" _ 3 & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") 4Set colTimeZone = objSWbemServices.ExecQuery _ 5 ("SELECT * FROM Win32_TimeZone") 6For Each objTimeZone in colTimeZone 7 Wscript.Echo "Differenz: "& objTimeZone.Bias 8Next
Wenn Sie das Script unter CScript ausführen, erhalten Sie auf einem deutschen Computer die folgende Ausgabe: Seite 381 von 394
Differenz: -480
Ein Datum in einen UTC-Wert konvertieren Das Zusammenbauen eines UTC-Datums ist eigentlich ganz einfach. Sie verketten einfach die entsprechenden Zahlen zu einem langen String. Es gibt jedoch eine Schwierigkeit: Monat und Tag müssen zweistellig sein. Am 10.12 ist dies kein Problem - was machen wir jedoch am 2.6? Sie fügen dem Wert einfach eine führende Null hinzu - so wird aus 2.6 ganz einfach 02.06. Hierzu verwenden Sie die VBScript-Funktion Len, um die Länge des Wertes zu prüfen (die Zahl der Ziffern): If Len(dtmMonth) = 1 Then dtmMonth = "0" & dtmMonth End If
Script 6.21 konvertiert das aktuelle Datum in ein UTC-Datum. Wenn es den Zeitwert zum Datum hinzufügt, verwendet es die Funktion CStr. Diese stellt sicher, dass der Wert als String und nicht als Zahl behandelt wird. Script 6.21: Konvertieren des aktuellen Datums in ein UTC-Datum 1 strComputer = "." 2 Set objSWbemServices = GetObject("winmgmts:" _ 3 & "{impersonationLevel=impersonate}!\\" & strComputer & 4 "\root\cimv2") 5 Set colTimeZone = objSWbemServices.ExecQuery _ 6 ("SELECT * FROM Win32_TimeZone") 7 For Each objTimeZone in colTimeZone 8 strBias = objTimeZone.Bias 9 Next 10 11dtmCurrentDate = Date 12dtmTargetDate = Year(dtmCurrentDate) 13 14dtmMonth = Month(dtmCurrentDate) 15If Len(dtmMonth) = 1 Then 16 dtmMonth = "0" & dtmMonth 17End If 18 19dtmTargetDate = dtmTargetDate & dtmMonth 20 21dtmDay = Day(dtmCurrentDate) 22If Len(dtmDay) = 1 Then 23 dtmDay = "0" & dtmDay 24End If 25 26dtmTargetDate = dtmTargetDate & dtmDay & "000000.000000" dtmTargetDate = dtmTargetDate & Cstr(strBias)
Script 6.22 demonstriert eine praktische Verwendung der Konvertierung. Es ruft alle Ordner ab, die nach einem bestimmten Datum erstellt wurden (in diesem Fall der 18.10.2003): Script 6.22 Retrieving Folders Based on Creation Date 1 2 3 4 5
strComputer = "." Set objSWbemServices = GetObject("winmgmts:" _ & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") Set colTimeZone = objSWbemServices.ExecQuery _
Seite 382 von 394
("SELECT * FROM Win32_TimeZone") 6 7 For Each objTimeZone in colTimeZone 8 strBias = objTimeZone.Bias 9 Next 10 11dtmCurrentDate = "18/10/2003" 12dtmTargetDate = Year(dtmCurrentDate) 13 14dtmMonth = Month(dtmCurrentDate) 15If Len(dtmMonth) = 1 Then 16 dtmMonth = "0" & dtmMonth 17End If 18 19dtmTargetDate = dtmTargetDate & dtmMonth 20 21dtmDay = Day(dtmCurrentDate) 22If Len(dtmDay) = 1 Then 23 dtmDay = "0" & dtmDay 24End If 25 26dtmTargetDate = dtmTargetDate & dtmDay & "000000.000000" 27dtmTargetDate = dtmTargetDate & Cstr(strBias) 28 29Set colFolders = objSWbemServices.ExecQuery _ ("SELECT * FROM Win32_Directory WHERE CreationDate < '" & _ 30 dtmtargetDate & "'") 31 32For Each objFolder in colFolders Wscript.Echo objFolder.Name 33 Next
Scripte auf Basis von WMI-Vorlagen erstellen In den folgenden Abschnitten finden Sie grundlegende Scriptvorlagen, mit denen Sie die folgenden Aktionen durchführen können: • • • • •
Abfragen und Anzeigen der Eigenschaften von verwalteten Ressourcen. Ändern der Eigenschaften von verwalteten Ressourcen. Methoden einer WMI-Klasse aufrufen. Eine neue Instanz einer verwalteten Ressource erstellen. Vorhandene Instanzen einer verwalteten Ressource löschen.
Anmerkung: Alle Scriptvorlagen werden gegen den lokalen Computer ausgeführt ("."). Um das Script gegen einen Remotecomputer auszuführen, ändern Sie einfach den Wert der Variable strComputer: strComputer = "atl-dc-01"
Abfragen und Anzeigen der einzelnen Eigenschaften verwalteter Ressourcen Script 6.23 gibt Informationen über die installierten Dienste zurück. Es kann jedoch sehr schnell so angepasst werden, dass es Informationen über jede andere verwaltete Ressource zurückgibt. Seite 383 von 394
Script 6.23: Vorlage zum Abfragen und Anzeigen von Ressourceneigenschaften 1 strComputer = "." 2 strNamespace = "\root\cimv2" 3 strClassName = "Win32_Service" 4 5 Set objSWbemServices = GetObject("winmgmts:" _ 6 & "{impersonationLevel=impersonate}!\\" & strComputer & strNamespace) 7 8 Set colInstances = objSWbemServices.ExecQuery _ ("SELECT * FROM" & strClassName) 9 10For Each objInstance in colInstances 11 Wscript.Echo "Caption " & objInstance.Caption Wscript.Echo "Description " & objInstance.Description 12 13Next
Um die Vorlage mit anderen WMI-Klassen zu verwenden: • • •
Setzen Sie die Variable strClassName auf die entsprechende WMI-Klasse. Setzen Sie - wenn nötig - die Variable strNamespace auf den entsprechenden WMINamespace. Ersetzt die Zeilen in der For-Each-Schleife durch die entsprechenden Eigenschaften
Abfragen und Anzeigen aller Eigenschaften einer verwalteten Ressource Script 6.24 fragt alle Eigenschaften und die Werte einer Klasse ab. Script 6.24: Vorlage zum Abfragen und Anzeigen aller Eigenschaften einer Ressource 1 strComputer = "." 2 strNamespace = "\root\cimv2" 3 strClassName = "Win32_Process" 4 5 6 Set objSWbemServices = _ GetObject("winmgmts:{impersonationLevel=impersonate}!\\" &_ 7 8 strComputer & strNamespace) 9 10Set colInstances = objSWbemServices.ExecQuery("SELECT * FROM " &_ strClassName) 11 12 13Wscript.Echo "Eigenschaften der Klasse " & strClassName 14Wscript.Echo "=================================================" 15 16iCount = 0 17For Each objInstance in colInstances iCount = iCount + 1 18 19 Set colProperties = objInstance.Properties_ 20 Wscript.Echo vbCrLf 21 Wscript.Echo "******************" 22 Wscript.Echo "INSTANZ NUMMER: " & iCount 23 Wscript.Echo "******************" 24 Wscript.Echo vbCrLf 25 26 For Each objProperty in colProperties 27 Wscript.Echo objProperty.Name & " : " & objProperty.Value 28 Next 29
Seite 384 von 394
30 Wscript.Sleep(2000) 31Next
Um diese Vorlage mit anderen WMI-Klassen zu verwenden: • •
Setzen Sie den Wert von strClassName auf die entsprechende WMI-Klasse. Wenn notwendig, ändern Sie den Wert von strNamespace.
Schreiben von Ressourceneigenschaften Script 6.25 fragt alle Instanzen der Klasse Win32_OSRecoveryConfiguration ab. Es ändert die Werte von einigen Eigenschaften und wendet diese dann über die Methode Put_ an. Anmerkung: Diese Vorlage funktioniert nur für Eigenschaften, die schreibbar sind. Bei allen anderen Eigenschaften führt es zu einem Fehler. Script 6.25: Vorlage zum Schreiben von Ressourceneigenschaften 1 strComputer = "." 2 strClassName = "Win32_OSRecoveryConfiguration" 3 strNamespace = "\root\cimv2" 4 5 Set objSWbemServices = GetObject("winmgmts:" _ 6 & "{impersonationLevel=impersonate}!\\" & strComputer & strNamespace) 7 Set colInstances = objSWbemServices.ExecQuery _ 8 ("SELECT * FROM " & strClassName) 9 For Each objInstance in colInstances 10 objInstance.DebugInfoType = 1 11 objInstance.DebugFilePath = "c:\scripts\memory.dmp" 12 objInstance.OverWriteExistingDebugFile = False 13 objInstance.Put_ 14Next
Um die Vorlage mit anderen WMI-Klassen zu verwenden und andere Eigenschaften zu ändern: • • •
Setzen Sie den Wert von strClassName auf die entsprechende WMI-Klasse. Wenn notwendig, ändern Sie den Wert von strNamespace. Ersetzt die Zeilen in der For-Each-Schleife durch die entsprechenden Eigenschaften
Aufrufen von Methoden Script 6.26 kann als Vorlage für ein Script verwendet werden, das WMI-Methoden aufruft. Dieses Script ruft die Methode StopService der Klasse Win32_Service auf, um den Dienst Alerter anzuhalten. Script 6.26: Vorlage zum Aufrufen von Methoden 1 2 3 4 5
strComputer = "." strNamespace = "\root\cimv2" strClassName = "Win32_Service" strKeyName = "Name" strKeyValue = "Alerter"
Seite 385 von 394
6 7 Set objSWbemServices = GetObject("winmgmts:" &_ 8 "{impersonationLevel=impersonate}!\\" & strComputer & strNamespace) 9 Set colInstances = objSWbemServices.ExecQuery _ 10 ("SELECT * FROM " & strClassName & " WHERE " & strKeyName & " = '" &_ 11 strKeyValue & "'") 12For Each objInstance in colInstances objInstance.StopService() 13 14Next
Um die Vorlage mit anderen WMI-Klassen zu verwenden und andere Eigenschaften zu ändern: • • • • •
Setzen Sie den Wert von strClassName auf die entsprechende WMI-Klasse. Wenn notwendig, ändern Sie den Wert von strNamespace. Setzen Sie den Wert von strKeyName auf den Namen der Eigenschaft, die in der WHEREBedingung verwendet wird. Setzen Sie den Wert von strKeyValue auf den entsprechenden Wert für diese Eigenschaft. Ersetzt Sie die Zeilen in der For-Each-Schleife durch die entsprechenden Methodenaufrufe
Erstellen von Ressourcen Script 6.27 verwendet die Methode Create, um einen neuen freigegebenen Ordner zu erstellen. Script 6.27: Vorlage zum Erstellen von Ressourcen 1 strComputer = "." 2 strNamespace = "\root\cimv2" 3 strClassName = "Win32_Share" 4 5 Set objSWbemServices = GetObject("winmgmts:" _ 6 & "{impersonationLevel=impersonate}!\\" & strComputer & strNamespace) 7 8 Set objNewShare = objSWbemServices.Get(strClassName) 9 Set objInParams = _ 10objNewShare.Methods_("Create").InParameters.SpawnInstance_() 11 12objInParams.Properties_.Item("Description") = "New Share Description" 13objInParams.Properties_.Item("Name") = "New Share Name" 14objInParams.Properties_.Item("Path") = "C:\scripts\shared" 15objInParams.Properties_.Item("Type") = 0 16 objNewShare.ExecMethod_ "Create", objInParams
Um die Vorlage mit anderen WMI-Klassen zu verwenden und andere Eigenschaften zu ändern: • • •
Setzen Sie den Wert von strClassName auf die entsprechende WMI-Klasse. Wenn notwendig, ändern Sie den Wert von strNamespace. Ersetzt die Zeilen in der For-Each-Schleife durch die erforderlichen neuen Zeilen.
Seite 386 von 394
Löschen von Ressourcen Script 6.28 löscht den freigegebenen Ordner mit dem Namen "New Share Name." Script 6.28: Vorlage zum Löschen von Ressourcen 1 strComputer = "." 2 strNamespace = "\root\cimv2" 3 strClassName = "Win32_Share" 4 strKeyName = "Name" 5 strKeyValue = "New Share Name" 6 7 Set objSWbemServices = GetObject("winmgmts:" _ 8 & "{impersonationLevel=impersonate}!\\" & strComputer & strNamespace) 9 10Set objInstance = objSWbemServices.Get(strClassName & "." &_ 11 strKeyName & "='" &_ 12 strKeyValue & "'") 13objInstance.Delete
Um die Vorlage mit anderen WMI-Klassen zu verwenden und andere Eigenschaften zu ändern: • • • • •
Setzen Sie den Wert von strClassName auf die entsprechende WMI-Klasse. Wenn notwendig, ändern Sie den Wert von strNamespace. Ersetzt Sie die Zeilen in der For-Each-Schleife durch die erforderlichen neuen Zeilen. Setzten Sie den Wert von strKeyName auf die Schlüsseleigenschaft der Klasse. Setzen Sie den Wert von strKeyValue auf den entsprechenden Wert für diese Eigenschaft.
Überwachen von Ressourcen über WMI-Ereignisbenachrichtigungen Über WMI-Ereignisbenachrichtigungen können Sie den Status einer verwalteten Ressource überwachen und auf ein Problem reagieren. Statt zum Beispiel zu warten bis der freie Festplattenplatz aufgebraucht ist, können Sie eine WMI-Ereignisbenachrichtigung einrichten, die Sie per E-Mail benachrichtigt, wenn der freie Speicherplatz unter einen bestimmten Wert fällt. Scripte, die verwaltete Ressourcen überwachen, sehen genauso aus wie andere WMI-Scripte. Script 6.29 überwacht zum Beispiel die Prozesse auf einem Computer und zeigt eine Nachricht an, wenn ein Prozess mit dem Namen Notepad.exe gestartet wurde. Wenn Sie das Script unter CScript ausführen, sehen Sie nur, dass das Script gestartet wird und nicht beendet wird. Das Script wartet einfach auf das entsprechende Ereignis. Wenn Sie dann Notepad starten, zeigt das Script die folgende Fehlermeldung an: Notepad.exe wurde soeben gestartet.
Script 6.29: Überwachen des Prozesses Notepad.exe 1 strComputer = "." 2 Set objSWbemServices = GetObject("winmgmts:" &_ 3 "{impersonationLevel=impersonate}!" &_ 4 "\\" & strComputer & "\root\cimv2")
Seite 387 von 394
5 6 Set objEventSource = objSWbemServices.ExecNotificationQuery( _ 7 "SELECT * FROM __InstanceCreationEvent " &_ "WITHIN 10 " &_ 8 "WHERE TargetInstance " &_ 9 10 "ISA 'Win32_Process' " &_ 11 "AND TargetInstance.Name = 'notepad.exe'") 12 13Set objEventObject = objEventSource.NextEvent() 14Wscript.Echo "Notepad.exe wurde soeben gestartet."
In den Zeilen 1 bis 4 baut das Script eine Verbindung zum Namespace root\cimv2 auf. In den Zeilen 6 bis 11 wird eine Benachrichtigungsabfrage über die Methode ExecNotificationQuery ausgeführt. Der WQL-Abfragestring wird in den Zeilen 7 bis 11 erstellt. Der Abfragestring enthält die neuen Schlüsselwörter WITHIN und ISA. Diese werden später besprochen. In Zeile 13 wird die Methode NextEvent verwendet, um das Script anzuhalten und auf das nächste durch die Abfrage beschriebene Ereignis zu warten. In Zeile 14 wir eine Nachricht angezeigt, wenn das Ereignis aufgetreten ist. Script 6.30 überwachen die Dienste auf einem Computer und zeigt eine Nachricht an, wenn Sie der Status des Dienstes Alerter ändert. Starten Sie das Script und führen Sie die Befehle net start alerter oder net stop alerter aus. Sie erhalten die folgende Ausgabe: Status des Dienstes alerter hat sich geändert.
Script 6.30: Überwachen des Dienstes Alerter 1 strComputer = "." 2 Set objSWbemServices = GetObject("winmgmts:" &_ 3 "{impersonationLevel=impersonate}!" &_ 4 "\\" & strComputer & "\root\cimv2") 5 6 Set objEventSource = objSWbemServices.ExecNotificationQuery( _ 7 "SELECT * FROM __InstanceModificationEvent " &_ 8 "WITHIN 10 " &_ 9 "WHERE TargetInstance " &_ 10 "ISA 'Win32_Service' " &_ 11 "AND TargetInstance.Name = 'alerter'") 12 13Set objEventObject = objEventSource.NextEvent() 14Wscript.Echo "Status des Dienstes alerter hat sich geändert."
Vergleichen Sie das Script mit Script 6.29. Die Elemente, die sich unterscheiden, sind fett formatiert. Es gibt vier Unterschiede: • • • •
Der überwachte Ereignistyp: __InstanceCreationEvent wurde in __InstanceModificationEvent geändert. Die WMI-Klasse der zu überwachenden Ressource: Win32_Process wurde in Win32_Service geändert. Der Wert der Eigenschaft Name: notepad.exe wurde in alerter geändert. Die angezeigte Nachricht. Seite 388 von 394
Die drei Schritte eines Überwachungsscripts Die Überwachung von Ressourcen erfolgt über drei Schritte: 1.Aufbau einer Verbindung zu einem WMI-Namespace auf einem Computer. strComputer = "." Set objSWbemServices = GetObject("winmgmts:" &_ "{impersonationLevel=impersonate}!" &_ "\\" & strComputer & "\root\cimv2") 2.Ausführen einer Benachrichtigungsabfrage. Hierzu wird eine WQL-Abfrage mit den neuen Schlüsselwörtern WITHIN und ISA verwendet. Set objEventSource = objSWbemServices.ExecNotificationQuery( _ "SELECT * FROM __InstanceModificationEvent " &_ "WITHIN 10 " &_ "WHERE TargetInstance " &_ "ISA 'Win32_Service' " &_ "AND TargetInstance.Name = 'alerter'") 3.Empfangen des Ereignisses und Durchführung der erforderlichen Aktionen. Im dritten Schritt wird die Methode NextEvent aufgerufen. Das Script hält an und wartet auf das Auftreten des Ereignisses. Dann verarbeitet des die weiteren Zeilen: Set objEventObject = objEventSource.NextEvent() Wscript.Echo "Status des Dienst Alerter hat sich geändert." Es gibt einige wichtige Unterschiede zwischen Standard-WMI-Abfragen und Ereignisbenachrichtigungsabfragen. Ereignisbenachrichtigungsabfragen verwenden Ereignisklassen. Statt Instanzen von verwalteten Ressourcen abzufragen, verwenden Ereignisbenachrichtigungsabfragen Instanzen einer Ereignisklasse. Die Klasse __InstanceModificationEvent stellt zum Beispiel das Ereignis dar, das auftritt, wenn eine Instanz verändert wird - __InstanceDeletionEvent und __InstanceModificationEvent treten bei Löschen und Ändern von Instanzen auf. Jede dieser drei Klassen ist von der mehr allgemeinen Klasse __InstanceOperationEvent abgeleitet. Ereignisbenachrichtigungsabfragen verwenden das Schlüsselwort WITHIN Da die Klasse Win32_Service nicht über einen WMI-Eventprovider verfügt, muss das Schlüsselwort WITHIN verwendet werden, damit der WMI-Abfragemechanismus alle 10 Sekunden ausgeführt wird. Theoretisch kann es passieren, dass Ereignisse verpasst werden zum Beispiel dann, wenn die Abfrage nur alle 30 Sekunden verwendet wird. Ereignisbenachrichtigungsabfragen verwenden das Objekt TargetInstance Es ist ziemlich unwahrscheinlich, dass Sie sich bei jeder erstellten Instanz einer WMI-Klasse benachrichtigen lassen möchten. Stattdessen sind Sie wahrscheinlich an einer bestimmten Klasse interessiert. Mit TargetInstance sind Sie in der Lage, diese Instanz anzugeben. TargetInstance ist ein Objekt, das als Reaktion auf ein Ereignis erstellt wird. Es hat dieselben Eigenschaften und Werte wie das Objekt, dass das Ereignis ausgelöst hat. Außerdem gibt es ein WMI-Objekt mit dem Namen PreviousInstance. Dieses Objekt enthält die Eigenschaften Seite 389 von 394
und Werte, die vor dem Ereignis vorhanden waren. Über diese beiden Objekte können Sie feststellen, was durch das Ereignis geändert wurde. Ereignisbenachrichtigungsabfragen verwenden das Schlüsselwort ISA Über dieses Schlüsselwort können Sie prüfen, ob eine bestimmte Instanz zu einer bestimmten Klasse gehört. Es entspricht im Groben dem Gleichheitszeichen.
Wie die Ereignisbenachrichtigung arbeitet Wie für jede verwaltete Ressource gibt es auch für jedes Ereignis eine Klasse. Wenn ein Ereignis auftritt, wird eine Instanz der entsprechenden Klasse verwendet. Es gibt drei Haupttypen von WMI-Ereignisklassen, die alle von der Klasse __Event abgeleitet sind: Intrinsic, Extrinsic und Timer Events. Die Intrinsic-Klasse wird wiederum durch drei Klassen repräsentiert __NamespaceOperationEvent, __InstanceOperationEvent und __ClassOperationEvent. Die von __Event abgeleiteten Klassen müssen in jedem Namespace vorhanden sein, der Ressourcen enthält, die überwacht werden können. In Abbildung 6.5 sehen Sie die von __Event abgeleiteten Klassen im Namespace \root\default.
Seite 390 von 394
Abbildung 6.5: Hierarchie der Ereignisklassen Intrinsic-Ereignisse Mit Intrinsic-Ereignissen werden Ressourcen überwacht, die durch eine Klasse im CIMRepository repräsentiert werden. Sie können außerdem zu Überwachung von Änderungen an einem Namespace oder einer Klasse verwendet werden. Ein Intrinsic-Ereignis wird durch eine Instanz einer Klasse repräsentiert, die von den Klassen __InstanceOperationEvent, __NamespaceOperationEvent oder __ClassOperationEvent abgeleitet ist. Alle Änderungen an Instanzen werden durch die Klasse __InstanceOperationEvent und den von dieser Klasse abgeleiteten Klassen __InstanceCreationEvent, __InstanceModificationEvent und __InstanceDeletionEvent repräsentiert. Eine Überwachung dieser Instanzen führen Sie über eine WQL-Benachrichtigungsabfrage durch. Die entsprechende WQL-Syntax sieht so aus: Seite 391 von 394
SELECT * FROM __InstanceOperationEventOrDerivedClass WITHIN PollingInterval WHERE TargetInstance ISA WMIClassName AND TargetInstance.WMIClassPropertyName = Value
Die von __InstanceOperationEvent abgeleitete Klasse, die Sie zur Überwachung registrieren, ist von dem zu überwachenden Ereignis abhängig. Erstellung einer Ressource: __InstanceCreationEvent Die Benachrichtigungsabfrage für die Erstellung einer Ressource sieht folgendermaßen aus: SELECT * FROM __InstanceCreationEvent WITHIN PollingInterval WHERE TargetInstance ISA 'Win32_Process' and TargetInstance.Name = 'notepad.exe'
Änderung einer Ressource: __InstanceModificationEvent Die Benachrichtigungsabfrage für die Änderung einer Ressource sieht folgendermaßen aus: SELECT * FROM __InstanceModificationEvent WITHIN PollingInterval WHERE TargetInstance ISA 'Win32_Service' and TargetInstance.Name = 'alerter'
Löschen einer Ressource: __InstanceDeletionEvent Die Benachrichtigungsabfrage für das Löschen einer Ressource sieht folgendermaßen aus: SELECT * FROM __InstanceDeletionEvent WHERE TargetInstance ISA 'Win32_Process' and TargetInstance.Name = 'notepad.exe'
Extrinsic-Ereignisse Mit diesen Ereignissen überwachen Sie eine Ressource, die nicht durch eine Klasse im CIMRepository repräsentiert ist. Mit Extrinsic-Ereignissen können Sie zum Beispiel überwachen, ob Änderungen an der Registrierung vorgenommen wurden. Scripte, die Extrinsic-Ereignisse verwenden, gehen genau wie Scripte mit IntrinsicEreignissen vor. Die entsprechenden Klassen werden jedoch von der Klasse __ExtrinsicEvent abgeleitet.
Erweiterte Überwachung Die Überwachung über ein WMI-Script unterliegt einigen Einschränkungen: • • • •
Instanzen können nur über eine einzelne Eigenschaft überwacht werden. Es kann nur ein Ereignis überwacht werden. Es kann nur ein Ereignistyp überwacht werden: Erstellen, Ändern oder Löschen. Es kann nur ein Ressourcentyp überwacht werden.
Die folgenden Beispielscripte zeigen, wie Sie diese Beschränkungen umgehen können. Überwachen einer bestimmten Ressource Sie können die Ressource genauer angeben, wenn Sie die WHERE-Bedingung erweitern. Script 6.31 definiert, dass die Eigenschaft DeviceID den Wert CPU0 haben muss, und dass die Eigenschaft LoadPercentage einen Wert größer 90 haben muss. Script 6.31: Überwachen der ersten CPU auf eine Auslastung von mehr als 90 Prozent 1 Set objSWbemServices = _
Seite 392 von 394
2 GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") 3 4 strWQL = "SELECT * FROM __InstanceModificationEvent " &_ "WITHIN 5 " &_ 5 6 "WHERE TargetInstance ISA 'Win32_Processor' " &_ 7 "AND TargetInstance.DeviceID='CPU0' " &_ 8 "AND TargetInstance.LoadPercentage > 90" 9 10Set objEventSource = objSWbemServices.ExecNotificationQuery(strWQL) 11Set objEventObject = objEventSource.NextEvent() 12Wscript.Echo "Load Percentage on CPU0 exceeded 90%."
Mehr als ein Ereignis überwachen Script 6.29 und Script 6.30 werden nach dem Auftreten des Ereignisses beendet. Wenn die Scripte nach einem Ereignis auf weitere Ereignisse reagieren soll, sind hierzu nur wenige Änderungen erforderlich. Fassen Sie den Aufruf der Methode NextEvent und die Ausgabe einfach in eine Endlosschleife ein. Script 6.32: Fortwährende Überwachung der ersten CPU auf eine Auslastung von mehr als 90 Prozent 1 strComputer = "." 2 Set objSWbemServices = _ 3 GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 4 5 strWQL = "SELECT * FROM __InstanceModificationEvent " & _ 6 "WITHIN 5 " & _ 7 "WHERE TargetInstance ISA 'Win32_Processor' " & _ 8 "AND TargetInstance.DeviceID='CPU0' " & _ 9 "AND TargetInstance.LoadPercentage > 90" 10 11Set objSWbemEventSource = objSWbemServices.ExecNotificationQuery(strWQL) 12 13While True 14 Set objSWbemObject = objSWbemEventSource.NextEvent() WScript.Echo "Load Percentage on CPU0 exceeded 90%." 15 16Wend
Mehr als einen Ereignistyp überwachen Script 6.29 verarbeitet nur die Erstellungs-Ereignisse. Script 6.30 verarbeitet nur ÄnderungsEreignisse. Nehmen wir jedoch an, Sie möchten benachrichtigt werden wenn Notepad gestartet wird, wenn die Instanz von Notepad geändert wird und wenn Notepad gelöscht wird. Bis jetzt müssten Sie hierzu drei Scripte schreiben und starten. Die Klassen __InstanceCreationEvent, __InstanceModificationEvent und __InstanceDeletionEvent sind alle von der Klasse __InstanceOperationEvent abgeleitet. Wenn Sie also in der WQL-Benachrichtigungsabfrage __InstanceOperationEvent verwenden, dann werden Sie über alle Ereignisse informiert. Script 6.33: Überwachung mehrerer Ereignisse für Notepad.exe 1 strComputer = "." 2 Set objSWbemServices = GetObject("winmgmts:" &_ 3 "{impersonationLevel=impersonate}!" &_ 4 "\\" & strComputer & "\root\cimv2") 5
Seite 393 von 394
6 Set objEventSource = objSWbemServices.ExecNotificationQuery( _ 7 "SELECT * FROM __InstanceOperationEvent " &_ 8 "WITHIN 1 " &_ "WHERE TargetInstance " &_ 9 10 "ISA 'Win32_Process' " &_ 11 "AND TargetInstance.Name = 'notepad.exe'") 12 13Set objEventObject = objEventSource.NextEvent() 14Select Case objEventObject.Path_.Class 15 Case "__InstanceCreationEvent" 16 Wscript.Echo "Instanz von Notepad.exe gestartet." Case "__InstanceDeletionEvent" 17 18 Wscript.Echo "Instanz von Notepad.exe beendet." 19 Case "__InstanceModificationEvent" 20 Wscript.Echo "Instanz von Notepad.exe geändert." 21End Select
Mehrere Ressourcentypen überwachen Script 6.29 überwacht einen Prozess und Script 6.30 überwacht einen Dienst. Es scheint also so, als würde für jede Ressource ein eigenes Script benötigt. Dies ist jedoch nicht richtig - die beiden Ressourcen können auch in einem Script überwacht werden. Hierzu müssen Sie zwei Änderungen vornehmen: • •
Sie müssen die WQL-Abfrage durch logische ORs erweitern, um Benachrichtigungen von mehreren Ressourcen zu erhalten (Zeilen 12-14). Um festzustellen, wo ein Ereignis herkommt, müssen Sie die vom Ereignisobjekt zurückerhaltene Klasse überprüfen (Zeile 17-22).
Script 6.34: Überwachen eines Dienstes und eines Prozesses 1 strComputer = "." 2 Set objSWbemServices = _ 3 GetObject("winmgmts:\\" & strComputer & "\root\cimv2") 4 5 Set objSWbemEventSource = objSWbemServices.ExecNotificationQuery( _ 6 "SELECT * FROM __InstanceModificationEvent " & _ 7 "WITHIN 1 " & _ 8 "WHERE (TargetInstance " & _ 9 "ISA 'Win32_Process' " & _ 10 "AND TargetInstance.Name = 'notepad.exe') " & _ 11 "OR (TargetInstance " & _ 12 "ISA 'Win32_Service' " & _ 13 "AND TargetInstance.Name='w3svc')") 14 15Set objSWbemObject = objSWbemEventSource.NextEvent() 16Select Case objSWbemObject.TargetInstance.Path_.Class 17 Case "Win32_Process" 18 WScript.Echo "Instanz von notepad.exe geändert." 19 Case "Win32_Service" 20 WScript.Echo "Status von Dienst W3SVC geändert." 21End Select
Seite 394 von 394