minpic01.jpg

Dr. Tobias Weltner

Zaubern mit Windows

Mehr Komfort beim Automatisieren: Scripting Host 2.0 und VBScript 5.0

So nützlich der Windows Scripting Host (WSH) in der ersten Fassung schon ist - wer etwas tiefer einsteigt, stößt doch schnell an die Grenzen der Makroprogrammierung. Beinahe unbemerkt hat Microsoft die Software jetzt generalüberholt: WSH 2.0 und VBScript 5.0 bringen neuen Schwung in die Skript-Entwicklung.

Unterthema: Listing 1
Unterthema: Listing 2
Unterthema: Listing 3
Unterthema: Listing 4
Unterthema: Scripting-Bonbons
Unterthema: Listing 5
Unterthema: Listing 6
Unterthema: Listing 7
Unterthema: Listing 8
Unterthema: Listing 9
Unterthema: Listing 10
Unterthema: Listing 11
Unterthema: Listing 12
Unterthema: Listing 13
Unterthema: Listing 14
Unterthema: Listing 15
Unterthema: Listing 16
Unterthema: Listing 17
Unterthema: Listing 18
Unterthema: Listing 19
Unterthema: Listing 20
Unterthema: Listing 21

So mancher Skript-Programmierer, ob Hobbyist oder Profi, braucht sich die Haare beim Automatisieren von Windows nicht länger zu raufen. Bisher lief das eine oder andere Skript einfach nicht, obwohl es korrekt aussah - dass der Scripting Host 1.0 einige unschöne Fehler enthält, bemerkt man leider nicht auf Anhieb. Manche Anwendungen ließen sich damit sogar überhaupt nicht realisieren. Man konnte zum Beispiel keine Dateien per Maus auf das Icon eines Skriptes ziehen, um es von diesem bearbeiten zu lassen (Drag & Drop), kam an bestimmte Systemkomponenten nicht heran und konnte schon erst recht keine `fremden´ Programme fernsteuern, wenn deren Entwickler nicht explizit entsprechende Mechanismen vorgesehen hatten. Die neue Fassung 2.0 des Scripting Host beseitigt diese drei und einige weitere Mankos und bringt eine ganze Reihe nützlicher neuer Funktionen mit. Unsere abgedruckten Beispielskripte helfen beim Umstieg.

Obwohl der Scripting Host 2.0 für Windows 2000 entwickelt wurde, läuft er erfreulicherweise auch auf älteren 32-Bit-Versionen von Windows - das gilt für 95, 98 und NT gleichermaßen. Das Update gibt es kostenlos bei Microsoft [1]. Zum Aktualisieren muss man zunächst die Skriptsprache VBScript auf den Stand von Version 5.0 bringen [2, 3], das System anschließend unbedingt neu starten und erst dann den WSH 2.0 installieren [4]. Auch wenn das Installationsprogramm ihn nicht erfordert, ist ein weiterer Neustart erforderlich. Sonst werden die neuen Drag&Drop-Fähigkeiten nicht registriert.

Bei unseren Versuchen scheiterte allerdings die Einrichtung auf `nackten´ Windows-95- und -NT-Systemen zunächst. Wie sich herausstellte, ist der Scripting Host auf moderne DCOM-Schnittstellen angewiesen, die erst bei Windows 98 und 2000 eingebaut sind. Abhilfe schafft die Installation des Internet Explorer 4: Der bringt das Betriebssystem so ganz nebenbei auf den neuesten DCOM-Standard. Windows 98 sollte hingegen auf Anhieb mit dem neuen WSH zurecht kommen.

Fernbedienung

Bisher konnte man andere Programme überhaupt nur fernsteuern, wenn diese über ein geeignetes Interface namens IDispatch() verfügten, in das sich ein Skript mit Hilfe des CreateObject()-Aufrufs einklinken konnte. Dieses Interface musste außerdem gut dokumentiert sein. Da beides allerdings bei den meisten Programmen nicht der Fall war, erstickten viele Versuche, diese fernzusteuern, schon im Keim.

Dieses Limit ist gesprengt: Ab sofort lassen sich auch ältere und undokumentierte Programme fernsteuern - ob die das wollen oder nicht. SendKeys simuliert einfache Tastendrücke und gaukelt dem Programm sozusagen einen eifrig tippenden Anwender vor.

Freilich vermögen auch einfache Makrorecorder Tastendrücke aufzunehmen und abzuspielen, um Programme fernzusteuern. Indem man diese Möglichkeit in Skripten einbaut, vergrößert man deren Aktionsradius aber doch immens.

Das Skript TEXT.VBS zeigt den Umgang mit SendKeys. Es springt vollautomatisch in eine beliebige Zeile eines Textverarbeitungsprogramms und wird damit übrigens auch für Skriptentwickler zum praktischen Helferlein: Jedes Mal, wenn der WSH Fehler einen Fehler bei der Ausführung eines Skriptes meldet, kann man direkt in die fehlerhafte Zeilennummer springen. Bisher musste man die Zeilennummer mühselig auszählen und den Cursor von Hand an die passende Stelle versetzen.

Das Skript möchte zunächst den Namen des Fensters wissen, das es fernsteuern soll - und zwar exakt so, wie er in der Fenstertitelleiste erscheint (zum Beispiel `Testskript.VBS - Editor´). AppActivate() schaltet daraufhin das entsprechende Fenster in den Vordergrund, und SendKeys() erledigt die Tipparbeit. Es springt zunächst an den Dokumentanfang und dann in die gewünschte Zeile, indem es entsprechend oft die Cursor-Down-Taste `drückt´.

Doch SendKeys vermag nicht nur einzelne Tastendrücke zu senden. Stellt man einem Zeichen ein `+´, `^´ oder `%´ voran, simuliert es ganze Tastenkombinationen mit Hilfe der Umschalt-, Steuerung- oder Alt-Taste. Sonderzeichen übergibt man in geschweiften Klammern: {HOME} drückt die Pos1-Taste. Und sogar mehrfache Tastendrücke sind möglich, zum Beispiel gibt {h 20} zwanzig Mal ein `h´ aus.

Makroprogramm selbst gebaut

Selbst Programm-Menüs lassen sich fernsteuern. Dabei empfiehlt sich allerdings die konsequente Verwendung von Kleinbuchstaben. WinWord öffnet nämlich über `%D´ (Alt-D) nicht etwa sein Datei-Menü, sondern fügt das aktuelle Datum ein, weil es die Anweisung als `Alt-Umschalt-d´ missinterpretiert. `%d´ dagegen funktioniert problemlos.

Welche Windows-Tasten sich mit welchen Schlüssen simulieren lassen, listet die WSH-2.0-Dokumentation [5] unter dem Stichwort SendKeys auf.

Noch komfortabler wird das abgedruckte Skript TEXT.VBS, wenn man es im Startmenü, wahlweise direkt oder in einer der Programmgruppen, platziert und mit einer Tastenkombination versieht. Dazu zieht man sein Icon auf das Startmenü, klickt mit der rechten Maustaste darauf und gibt die gewünschte Kombination ein, etwa Strg-Alt-T. In diesem Fall braucht das Skript natürlich nicht erst umständlich nach dem Namen des Fensters zu fragen - es steht ja bereits im Vordergrund. Wer das Skript also regelmäßig einzusetzen gedenkt, braucht lediglich die Zeilen Markierung Anfang-Ende zu löschen.

Schließlich bedarf es nur weniger Änderungen im Skript, um ein individuelles Makro für beliebige Programme zu schreiben.

Internetverbindungen automatisieren

Doch nicht allein Programme, selbst Systemdialoge lassen sich via SendKeys fernsteuern. DFÜ.VBS beispielsweise startet durch simple Eingaben eine DFÜ- oder Internetverbindung, und DRUCKER.VBS schaltet den Standarddrucker um. Beide Skripte bedürfen womöglich des individuellen Feintunings, weil die Wartezeiten bis zum Erscheinen der gewünschten Dialogfenster von System zu System unterschiedlich ausfallen. Achten Sie außerdem darauf, dass die Webansicht abgeschaltet ist, oder passen Sie die Tastendrücke an die Webansicht an.

Bei beiden Skripten verdient besonders die SendKeys-Anweisung `+{F10}´ Aufmerksamkeit: Diese versteckte Tastenkombination klappt ferngesteuert das Kontextmenü eines markierten Objektes auf, das anschließend mit weiteren SendKeys-Anweisungen bedient wird. Eine ähnliche Tastenkombination eröffnet den Skripten alle Funktionen des Startmenüs: `^{ESC}´.

SendKeys kann freilich auch beliebige Dialogfenster ausfüllen und für eigene Zwecke einspannen. DATEI.VBS beispielsweise ruft mit Hilfe des Shell.Application-Objekts das Suchen-Fenster auf und startet eine Recherche nach allen Dateien, die seit mindestens einem Monat nicht mehr gespeichert wurden. Sowohl das Shell.Application-Objekt als auch die Suchmethode `Letzter Zugriff´ des Suchen-Fensters gibt es allerdings erst bei Windows 98.

Mit Dateien arbeiten

Sogar der Windows-Explorer lässt sich mit Gewinn fernsteuern. Die drei Prozeduren Open, View und Mark im Skript EXPLORER.VBS öffnen ein Explorerfenster, schalten auf die gewünschte Darstellungsform um (Mode 1 bis 4 für `Große Symbole´, `Kleine Symbole´, `Liste´ und `Details´) und markieren eine Datei (`msdos.sys´) - jeweils über SendKeys. Somit kann sich jeder Anwender ein Skript auf den Desktop legen oder ins Startmenü packen und mit einer Tastenkombination versehen, um auf Knopfdruck seine individuellen Lieblingseinstellungen zu aktivieren. Windows ist in dieser Hinsicht ja leider etwas vergesslich ...

Spätestens bei den letzten Beispielen wird allerdings auch die erste Limitation von SendKeys deutlich. Es ist darauf angewiesen, dass das richtige Fenster im Vordergrund liegt. Verwendet der Explorer beispielsweise die Webansicht, dann scheitern die Beispielskripte zunächst, weil nun andere Tastendrücke erforderlich sind als bei normalen Explorer-Fenstern.

SendKeys-Limits

Schwerstes Manko des SendKeys-Befehls ist allerdings seine Betriebsblindheit: Der Befehl gibt stur die Tastendrucke aus, die ihm aufgetragen werden, kontrolliert aber nicht, an welches Fenster die Tastendrücke eigentlich gehen. Gesendet wird immer an das jeweilige Vordergrundfenster, auch wenn sich das in der Zwischenzeit geändert hat.

Genau das kann bei längeren Skriptaufgaben nervig bis gefährlich werden - was das ansonsten recht praktische WINORDNER.VBS demonstriert. Das Skript gibt eine Liste mit allen Dateien des Windows-Verzeichnisses in ein Fenster des Texteditors Notepad aus. Dieser Vorgang dauert einige Sekunden. Klickt der Anwender während dieser Zeit versehentlich in ein anderes Fenster, dann landet der restliche Text beim falschen Empfängerprogramm, was je nach Art des neuen Vordergrundfensters schwere Nebenwirkungen nach sich ziehen kann - denn es nimmt ja an, dass der Anwender die entsprechenden Tasten drückt. Abhilfe: Mit den Tasten `Strg-Alt-Entf´ die Taskliste aufrufen und dem Task WScript per `Task beenden´ blitzschnell die Sicherung herausdrehen.

minpic02.jpg

Welche DLL steht in welcher Version auf der Platte? Das Skript DATEIINFO.VBS weiß es.

Der einzig vernünftige Ausweg aus diesem Dilemma ist ausgerechnet bei Windows 98 und 2000 verstellt. Der Scripting Host bringt zwar zusätzlich zu SendKeys den AppActivate()-Befehl mit, der Fenster in den Vordergrund schalten soll. Dazu füttert man ihn mit dem exakten Namen des gewünschten Fensters, wie er in der Titelleiste erscheint. Auf diese Weise könnte man vor jeder SendKeys()-Anweisung sicherstellen, dass das richtige Fenster den Eingabefocus erhält. Dummerweise funktioniert AppActivate() aber bei neueren Windows-Versionen nicht mehr zuverlässig. Unter Umständen schaltet es das Fenster nicht in den Vordergrund, sondern sorgt nur dafür, dass dessen Schaltfläche in der Taskleiste hektisch blinkt. Hintergrund: Nur wenn der Aufrufer von AppActivate gerade selbst über den Eingabefocus verfügt, schaltet der Befehl fremde Fenster wirklich in den Vordergrund. Das ist bei Skripten aber in der Regel nur beim ersten AppActivate()-Aufruf der Fall.

Wirklich gefahrlos einsetzen lässt sich SendKeys() also nur mit entsprechenden Befehlserweiterungen, die sicherstellen, dass die Funktion zum richtigen Fenster spricht. Andernfalls sollte das Skript abbrechen.

Frischer Wind im Dateisystem

Das FileSystemObject, das zentrale Scripting-Tor zum gesamten Dateisystem, wurde als Teil des WSH 2.0 ebenfalls generalüberholt. Dabei behob Microsoft auch fatale Bugs. So liefert das File- und Folder-Objekt über Count endlich die korrekte Anzahl der Dateien und Ordner:

set fs = CreateObject
          ("Scripting.FileSystemObject")
set testordner = fs.GetFolder("C:\")
MsgBox "Ordner " & testordner & " enthält " _
& testordner.subfolders.Count & _
& " Unterordner und " & testordner.files.Count _ 
& " Dateien."
Im alten Scripting Host verursachte diese Funktion regelmäßig einen `unbekannten Fehler´. Doch das FileSystemObject hat auch wertvolle neue Funktionen hinzubekommen, die allerdings in der offiziellen Microsoft-Dokumentation gar nicht erwähnt werden.

Datei, wie alt bist du?

Für Wartungs- und Installationsskripte ist es lebenswichtig, die Versionen bestimmter Dateien feststellen zu können. Nur so lassen sich automatisiert veraltete Dateien austauschen, ohne dabei versehentlich neuere Versionen zu überschreiben. Skripten waren solche Versionsinformationen bislang jedoch nicht zugänglich.

Dieses Manko behebt die neue Funktion GetFileVersion(). Allerdings liefert die Funktion einen Fehler, wenn gar keine Versionsinformationen in der untersuchten Datei eingetragen sind. Deshalb sollte sie in separate Prozeduren ausgelagert werden, die das Fehlerhandling mittels on error resume next selbst übernehmen.

Das Beispiel VERSIONEN.VBS sucht alle in Windows installierten DLL-Dateien samt ihrer Versionsnummern zusammen und schreibt sie in eine alphabetisch sortierte HTML-Tabelle, die schließlich der jeweils installierte Web-Browser anzeigt. Eine solche Liste kann beim Troubleshooting wertvolle Dienste leisten, wenn man sie etwa ausdruckt und mit der Liste eines anderen Systems vergleicht.

Drag & Drop

Eine der leisesten und dabei mächtigsten Erweiterungen des WSH 2.0 ist seine neue Drag&Drop-Fähigkeit. Ab sofort reagiert ein Skript ganz automatisch, wenn ein Anwender Dateien auf sein Icon zieht - das klappte bislang nur mit richtigen Programmen.

Die Namen der fallen gelassenen Objekte verarbeitet das Skript wie ganz normale Kommandozeilenparameter über WScript.Arguments. DATEIINFO.VBS meldet Details über Dateien und Ordner, die man auf dem Skript-Icon ablädt. Dabei stellt das Skript die Informationen in einem InputBox-Fenster dar und trägt den kompletten Pfadnamen der Datei oder des Ordners als Vorgabe ins Eingabefeld ein. Das ist natürlich nur eine Finte, denn Eingaben erwartet das Skript in der abgedruckten Fassung gar nicht. Mit `Strg-C´ lässt sich der Pfadname immerhin in die Zwischenablage verfrachten und für andere Dinge weiterverwenden. Wer mag, kann das Skript freilich etwa um eine Funktion zum Umbenennen ergänzen.

Im derzeitigen Betastadium funktioniert Drag & Drop allerdings noch nicht bei Windows 2000.

Komfortable Fehlersuche

Mit dem Internet Explorer Script Debugger existiert ein ausgezeichnetes und noch dazu kostenloses Debugging-Tool für eigene Skripte (siehe Kasten auf Seite 296). Während der WSH 1.0 diesen bei Skriptfehlern automatisch zuschaltete, scheint der WSH 2.0 den Debugger allerdings nicht mehr zu unterstützen.

Laut der neuen Scripting-Philosophie Microsofts soll der WSH nämlich künftig nicht mehr gleich die Innereien eines Skriptes offen legen, nur weil ein Fehler auftrat. Das soll jetzt nur noch auf expliziten Wunsch des Entwicklers passieren. Dazu dienen die neuen Kommandozeilenparameter //X (im Debugger starten) und //D (bei Fehlern zum Debugger wechseln).

Einfacher ist die Skript-Hebebühne in Form von DEBUGSKRIPT.VBS. Lassen Sie eine Skriptdatei auf dem Skripticon fallen, dann wird der Debugger gestartet - jetzt kann man das Skript mit der F8-Taste im Einzelschrittmodus begutachten. Voraussetzung ist freilich, dass der Debugger überhaupt installiert ist [10].

Send to anywhere

Die neuen Drag&Drop-Fähigkeiten funktionieren nicht nur im klassischen Sinne. Dank dieser neuen Funktion fühlen sich Skripte ab sofort auch im `Senden an´-Menü wohl, sodass man sie dort als Befehlserweiterung einsetzen kann. Wie leicht Sie einen `Irgendwohinsenden´-Befehl implementieren, zeigt SENDEN.VBS.

Das Skript integriert man ins `Senden an´-Menü, indem man im Startmenü unter `Ausführen´ eingibt: `SENDTO´. Anschließend zieht man das Skript mit der rechten Maustaste ins SendTo-Fenster und wählt `Verknüpfung(en) hier erstellen´. Da es sich nur um einen Dateiverweis handelt, können Sie diesen auch beliebig umbenennen und mit einem schöneren Icon versehen (Rechtsklick, Eigenschaften, Anderes Symbol). Schon steht die neue Funktion bereit.

Die Kopierfunktion ist freilich nur ein Beispiel dafür, was man mit dem Senden-Menü so alles anstellen kann. Wer mag, kann das Skript so erweitern, dass es Dateien beispielsweise auch an andere Rechner im Netzwerk versendet.

Dialoge selbst gemacht

Leider gibt es im Scripting Host kein permanent sichtbares Ausgabefenster, wo ein Skript Lebenszeichen von sich geben könnte. Zu diesem Zweck kann man aber prima ein anderes Programm `missbrauchen´, indem man dieses vom Skript aus startet. Diese Technik ist nicht neu, lässt sich aber erst beim WSH 2.0 voll ausreizen. Mehr noch: Ab sofort kann der Scripting Host auch umgekehrt Zugriff auf seine eigenen Prozeduren und Funktionen gewähren, und damit fremde Programme zum Eingabemedium umfunktionieren. Skriptentwickler können so jedes nur erdenkliche Dialogfenster selbst nachrüsten.

FORMULAR.VBS kidnappt eine Instanz des Internet Explorer und stellt darin ein HTML-Formular dar, das aus der Vorlagendatei FORMULAR.HTML stammt. Die Schaltflächen des Formulars werden über den neuen GetRef()-Befehl mit Prozeduren des Skripts verknüpft, sodass das Skript über das Formular gesteuert werden kann.

Dank GetRef() verhält sich Ihr Skript wie ein in die HTML-Vorlage eingebettetes Skript und kann mit allen Ereignissen der HTML-Steuerelemente verknüpft werden. Die Ereignisse des Internet Explorer selbst wie beispielsweise onQuit leitet der WScript.CreateObject-Befehl ans Skript weiter.

minpic03.jpg

Hilfreich bei der Fehlersuche: Der Internet Explorer Script Debugger entwanzt auch WSH-Skripte.

Nachdem der Internet Explorer das Eingabefenster anzeigt, muss das Skript angehalten werden, um auf die Eingabe zu warten. Weil es dafür keine spezielle Funktion gibt, verwendet das Skript einfach eine do...loop-Endlosschleife. Sie bricht nur ab, wenn ende auf true gesetzt wird. Im Hauptskript geschieht dies niemals, aber sobald das IE-Fenster geschlossen wird, schaltet die zuständige Event-Prozedur ie_onQuit die Variable auf true und bricht damit die Warteschleife des Hauptskripts ab.

Unverzichtbar innerhalb der Warteschleife ist der neue Sleep-Befehl des WSH 2.0. Er gibt die Kontrolle für verschmerzbare 300 Millisekunden ans System ab, damit es andere Ereignisse zügig und ruckelfrei bearbeiten kann.

Nebenbei stellt sich die Frage, warum man nicht direkt Skripte per <SCRIPT>-Tag in HTML-Dokumenten einbetten sollte, um sich den Umweg über GetRef() zu sparen. Dies ist tatsächlich möglich, aber nicht ratsam. Alle CreateObject-Befehle eines eingebetteten Skriptes werden nämlich argwöhnisch beäugt und je nach den Sicherheitseinstellungen des Internet Explorer sogar gänzlich ignoriert. Eingebettete Skripte haben damit wenig Handlungsspielraum, während Ihnen die hier vorgestellte Lösung keine Grenzen auferlegt.

In DOS einklinken

Skripte brauchen Kontakt zur Außenwelt, um nützliche Dinge tun zu können. Bisher war das FileSystemObject Monopolist und diente Skripten als Tor zum Dateisystem. Mit dem folgenden kleinen Skript kann man etwa eine Textdatei wie MSDOS.SYS öffnen und darstellen:

dateiname = "C:\MSDOS.SYS"
set fs = CreateObject
          ("Scripting.FileSystemObject")
if fs.FileExists(dateiname) then
      set datei = fs.OpenTextFile(dateiname)
      MsgBox datei.ReadAll
      datei.close
else
      MsgBox dateiname & " existiert nicht!"
end if
Der WSH 2.0 führt jetzt die Gleichberechtigung von DOS-Ein- und Ausgaben mit Dateien ein. Damit kann ein Skript auch Texteingaben verarbeiten und Meldungen ins DOS-Fenster zurückschreiben. Freilich funktioniert diese neue Funktion nur dann, wenn das Skript tatsächlich in einem DOS-Fenster ausgeführt wird, deshalb fristet sie bislang ein Schattendasein. Zu Unrecht: Mit Hilfe des Ein- und Ausgabestroms von DOS lassen sich nämlich eigene Kommandozeilen-Utilities schreiben und sogar VBScript interaktiv ausführen.

DOSINPUT.VBS ist ein simples Beispiel. Um es auszuprobieren, öffnen Sie ein DOS-Fenster und rufen darin das Skript auf: `CSCRIPT DOSINPUT.VBS´. Es definiert mit StdIn und StdOut den Ein- und Ausgabestrom und kann jetzt interaktive Texteingaben lesen und die Resultate ins DOS-Fenster zurückschreiben. Durch Eingabe von `exit´ wird das Skript beendet.

VBScript interaktiv

KOMMANDO.VBS demonstriert mit dieser Technik einen einfachen Kommandozeilen-Interpreter. Es akzeptiert beliebige VBScript-Befehle und führt sie aus. Der Befehl `??´ gibt Ergebnisse zeilenweise im DOS-Fenster aus, während `???´ einzelne Zeichen in den Ausgabestrom schreibt.

Dieses Skript eignet sich ausgezeichnet, um VBScript zu erforschen oder besonders vertrackte Skript-Algorithmen interaktiv zu testen. Mit dem Doppelpunkt kann man mehrere Befehlszeilen voneinander abgrenzen. Um beispielsweise alle ASCII-Codes aufzulisten, genügt eine Zeile:

for x=32 to 255:?? x & vbTab & chr(x):next
Das Skript gestattet sogar wie in alten Tagen kleinere BASIC-Programme zu erstellen. Dazu stellt man den Befehlen Zeilennummern voran. Mit `run´ führt man ein Progrämmchen aus, `list´ zeigt es an. `delete´ löscht das gesamte Listing, während `clear´ das DOS-Fenster leert. (Vorsicht: solange das Skript auf Eingaben wartet, ist das Windows-Fensterhandling stark verlangsamt.)

Möglich wird dieses verblüffende Skript durch den undokumentierten ExecuteGlobal-Befehl, der von VBScript 5.0 stammt: Er führt VBScript-Anweisungen aus, die aus Textvariablen stammen.

Der Eingabestrom muss natürlich nicht von Menschenhand stammen. Besonders interessant ist das so genannte Piping: Die Eingabedaten stammen dabei von einem anderen Skript oder DOS-Befehl und werden nur weitergereicht. Das |-Zeichen verknüpft die einzelnen Arbeitsstationen respektive DOS-Befehle oder Skripte. FILTER.VBS demonstriert die Technik: Es akzeptiert die Ausgabe eines beliebigen DOS-Befehls, wandelt die deutschen Sonderzeichen vom ASCII- in den Windows-typischen ANSI-Code, speichert das Ergebnis in einer temporären Datei und füttert anschließend den Windows-Editor damit. Die folgende Zeile genügt, um ein komplettes Ordnerlisting in den Windows-Editor zu laden, von wo es dann ausgedruckt werden kann:

dir c:\ | cscript.exe filter.vbs

WS-Dateien und XML-Tags

Neben den bekannten Dateiextensionen .VBS (für VBScript) und .JS (für JavaScript) bringt der WSH 2.0 einen ganz neuen Dateityp mit: .WS (Windows Script). Solche Dateien verwenden XML-Tags und bieten damit bisher nicht gekannte Steuerungsmöglichkeiten.

Im einfachsten Fall `verpackt´ eine WS-Datei eine reguläre Skriptdatei nur in XML-Tags. Erster Vorteil sind zusätzliche Steuerparameter, mit denen zum Beispiel angegeben wird, ob das Skript im Fehlerfall den Script Debugger aufrufen soll oder nicht. FEHLER.WS provoziert eine Fehlermeldung, die aber entgegen den Voreinstellungen nicht in einem Dialog, sondern im Debugger angezeigt wird - sofern dieser installiert ist.

Sollen Skripte auf ein fremdes Objekt zugreifen, muss der Entwickler alle Details über Funktionen und Konstanten dieses Objektes kennen. Beim WSH 2.0 ist es nicht mehr ganz so schwierig, an solche Informationen heranzukommen, denn der kann die Konstanten eines Objektes aus seiner Type Library lesen. Die werden über <REFERENCE> zugänglich gemacht. Das Tag erwartet die ClassID der zuständigen Type Library, die es lesen und auswerten soll. TYPE.WS gibt die Class-ID der Type Library des Shell.Application-Objekts an. Dieses Objekt existiert auf Ihrem Rechner allerdings nur, wenn das IE4 Desktop Update oder Windows 98/2000 installiert ist.

Anschließend verwendet das Skript den Open-Befehl des Objekts und öffnet mit der Konstanten ssfDrives das Arbeitsplatz-Fenster. Diese Konstante entspringt der Type Library. Wird sie beispielsweise durch ssfBitBucket ersetzt, dann öffnet dieselbe Funktion den Papierkorb.

Die Type-Library-Informationen helfen aber leider nicht dabei, die undokumentierten Funktionen oder Konstanten eines Objektes aufzudecken. Hierfür sind nach wie vor Spionagetools nötig (siehe auch c't 10/99, S. 96).

minpic04.jpg

Eine variable Eingabemaske für ein Skript: eine HTML-Datei hilft aus.

TYPE.WS deutet bereits an: .WS-Dateien können endlich mehrere Skriptsprachen mischen, denn das <SCRIPT>-Tag definiert die Skriptsprache für jeden Skriptblock explizit. Werden mehrere Skriptsprachen gleichzeitig eingesetzt, dann bringt das zwar einen höheren Overhead mit sich, weil nun für jede Sprache ein eigenes Modul im Speicher gehalten werden muss.

Skriptsprachen mischen

Dieser Mehraufwand kann sich aber durchaus lohnen, denn so kann man die besonderen Vorzüge bestimmter Sprachen kombinieren oder einfach nur bereits vorhandene Routinen in neuen Projekten weiternutzen. Dies wird umso interessanter, als der WSH neben VBScript und JavaScript um viele weitere Skriptsprachen erweitert werden kann, etwa um REXX oder PERL.

Skript-Recycling

Noch wesentlich wertvoller ist die Möglichkeit, Skripte zu importieren. Damit kann man Routinefunktionen in Skriptbibliotheken definieren und von Fall zu Fall in neuen Projekten wiederverwenden. Die eigentlichen Skripte bleiben auf diese Weise klein und übersichtlich. Obendrein kommt eine Wartung oder ein Feintuning der Skriptbibliotheken gleich allen Skripten zugute, welche die Bibliotheken verwenden. Zur Veranschaulichung nutzt das Skript INCLUDE.WS die Funktion ShowFiles aus BIBLIOTHEK1.VBS.

Das Wiederverwenden herkömmlicher Skripte ist zwar bereits praktisch, aber eine weitere Neuerung in VBScript 5.0 macht es erst richtig effektiv: Die neue Version der Sprache kann Klassen definieren. Solche Objekte mit selbstgemachten Eigenschaften und Methoden verhalten sich nach außen wie eine `Black Box´: Ein Skript braucht sich nicht darum zu kümmern, wie es im Innern einer Klasse zugeht.

minpic05.jpg

BASIC-Interpreter selbst gemacht - mit dem interaktiven VBScript kein Problem.

minpic06.jpg

Einmal DOS und zurück: Ergebnisse aus der Kommandozeile in Skripten weiternutzen.

KLASSEN.WS importiert eine Klasse namens Attributes aus BIBLIOTHEK2.VBS. Diese rudimentäre Klasse könnte der Anfang für ausgefeilte Datei-Systemfunktionen sein, ist hier aus Platzgründen aber nicht ausformuliert. In der Sparversion bietet die Klasse zwei Eigenschaften: `Object´ und `System´. Mit Ersterem initialisieren Sie die Klasse. `Object´ kann ein File- oder Folder-Objekt sein. Anschließend liefert die Eigenschaft `System´ den Zustand des System-Dateiattributes und kann das System-Attribut auch verändern.

KLASSEN.WS demonstriert das normalerweise vertrackte Dateiattribut-Handling und verwendet MSDOS.SYS als Versuchskaninchen. Zuerst meldet es den aktuellen Zustand des System-Attributes und verändert das Attribut anschließend. Zum Schluss wird der Ausgangszustand wiederhergestellt. Wenn Sie dem Skript nicht glauben, dann suchen Sie MSDOS.SYS im Explorer heraus und rufen nach jeder Meldung selbst das Eigenschaften-Fenster zur Kontrolle auf.

Klassen sind ein extrem mächtiges Werkzeug, um effiziente Skriptbibliotheken zu erstellen. Beachten Sie aber, dass Klassen anders als bei Visual Basic nicht in einer einzigen Zeile mit Dim VarName as New Klasse definiert werden können. Dim und New gehören bei VBScript in zwei separate Zeilen.

Jetzt gehts los ...

Soweit die Einführung in die neuen Möglichkeiten von Scripting Host 2.0 und VBScript 5.0. Wer tiefer in die Materie einsteigen will, findet ausführliche Dokumentation zum Herunterladen bei [5], [6] und [7]. Wie sich auch die letzten Scripting-Hürden über selbstentwickelte Befehlserweiterungen sprengen und die gesamten Windows-API-Funktionen verwenden lassen, verrät zum Beispiel [12]. (se)

Literatur und URLs
[1] Microsoft Scripting-Homepage: http://msdn.microsoft.com/scripting

[2] Informationen zu Script-Engines: www.microsoft.com/msdownload/vbscript/scripting.asp

[3] VBScript 5.0 zum Download: www.microsoft.com/scripting/downloads/ws/x86/ste50de.exe

[4] WSH 2.0 zum Download: http://msdn.microsoft.com/scripting/windowshost/beta/x86/wsh20en.exe

[5] WSH-2.0-Dokumentation: http://msdn.microsoft.com/scripting/windowshost/beta/wshdoc.exe

[6] VBScript-Dokumentation: http://msdn.microsoft.com/scripting/vbscript/download/vbsdown.htm#DOC

[7] JavaScript-Dokumentation: http://msdn.microsoft.com/scripting/jscript/download/jsdown.htm#DOC

[8] HTMLHelp-Update: http://msdn.microsoft.com/workshop/author/htmlhelp/download.asp

[10] Script Debugger: http://msdn.microsoft.com/scripting/debugger/dbdown.htm

[11] Script Encoder: http://msdn.microsoft.com/scripting/vbscript/download/x86/sce10de.exe

[12] Franzis´-Verlag: Scripting Host, ISBN 3-7723-6484-5

Kasten 1


Listing 1

DFÜ.VBS stellt eine Internet-Verbindung her.

' DFÜ.VBS
' Startet eine DFÜ-Verbindung
' (C) Tobias Weltner, c't 21/99

set wshshell = CreateObject("WScript.Shell")

if MsgBox("Baue Internetverbindung auf!", vbOkCancel)=vbCancel then 
     WScript.Quit
end if

' Arbeitsplatz öffnen
wshshell.Run "explorer.exe /select,c:\"
WScript.Sleep 1000
' DFÜ-Netzwerk öffnen:
wshshell.SendKeys("DFÜ{ENTER}")
WScript.Sleep 1000
' Verbindung aussuchen und starten
' HIER BITTE NAME DER VERBINDUNG ANSTELLE
' VON xlink EINTRAGEN:
wshshell.SendKeys("xlink{ENTER}")
WScript.Sleep 1000
wshshell.SendKeys("{ENTER}")
Kasten 2

Listing 2

DRUCKER.VBS schaltet den Standard-Drucker um.

' DRUCKER.VBS
' legt den Standarddrucker fest
' dazu muss die Webansicht des
' Druckerordners abgeschaltet sein
' (C) Tobias Weltner, c't 21/99

set wshshell = CreateObject("WScript.Shell")

wshshell.run "control.exe main.cpl,Drucker,0"
WScript.Sleep 1000

' Drucker aussuchen:
' HIER NAME DES DRUCKERS EINTRAGEN:
wshshell.SendKeys("HP Laserjet")
WScript.Sleep 200

' Kontextmenü aufklappen
wshshell.SendKeys("+{F10}")
WScript.Sleep 200

' Standarddrucker aussuchen:
wshshell.SendKeys("{DOWN 3}{ENTER}")
WScript.Sleep 200

' Fenster schließen
wshshell.SendKeys("%{F4}")
Kasten 3

Listing 3

TEXT.VBS - das Textprogramm von Geisterhand bedient.

' TEXT.VBS
' markiert eine beliebige Zeile in einem
' Textverarbeitungsprogramm
' (C) Tobias Weltner, c't 21/99

set wshshell = CreateObject("WScript.Shell")

' Markierung Anfang ---
fenster = InputBox("Wie heißt das Textverarbeitungs" _
          & "-Fenster, das Sie fernsteuern wollen?")
if fenster="" then WScript.Quit
' Markierung Ende ---

zeile = inputBox("In welche Zeile wollen Sie springen?")
if not isNumeric(zeile) then
     MsgBox "Sie haben keine Zahl angegeben!"
     WScript.Quit
else
     zeile = Fix(zeile)
     if zeile<1 then
          MsgBox "Sie haben keine gültige" _
          & " Zeilennummer angegeben!"
          WScript.Quit
     end if
end if

' Markierung Anfang ---
' Fenster aktivieren
wshshell.AppActivate(fenster)
' Markierung Ende ---

WScript.Sleep(100)
'An den Anfang springen
wshshell.SendKeys "^{HOME}"

' Zeilen zählen
wshshell.SendKeys "{DOWN " & zeile-1 & "}"

' Zeile markieren
wshshell.SendKeys "{HOME}"
wshshell.SendKeys "+{END}"
Kasten 4

Scripting-Bonbons

Bei kniffligen oder längeren Skripten kann es schon mal schwierig werden, Fehlern auf die Spur zu kommen. In diesem Fall hilft der Internet Explorer Script Debugger, den es kostenlos bei Microsoft gibt [10]. Ist der Debugger installiert, kann man - wie im Artikel gezeigt - Skripte in den Debugger laden und dann schrittweise ausführen. Im Befehlsfenster, über das Ansicht-Menü des Debuggers zu erreichen, lassen sich nach jedem Schritt die aktuellen Variablen erfragen. `?´ gibt dabei Meldungen aus.

For your eyes only

Einen ganz anderen Weg geht der Script Encoder, der ebenfalls kostenlos zu haben ist [11]. Mit seiner Hilfe lassen sich Skripte kodieren, damit die Skriptanweisungen darin geheim bleiben. Der Script Encoder schützt Skripte zwar - wohl aus Geschwindigkeitsgründen - nicht gerade wie Fort Knox, ist aber trotzdem ein effizientes Tool. `Normale´ Anwender jedenfalls werden so schnell keine Möglichkeit finden, an das Innenleben der kodierten Skripte heranzukommen.

Der Script Encoder selbst ist ein DOS-Kommandozeilen-Utility namens SCRENC.EXE. Weil es etwas unbequem zu bedienen ist, können Sie auch unser Skript ENCODE.VBS verwenden. Es nimmt per Drag & Drop VBS-Dateien entgegen, kodiert diese automatisch und legt sie als VBE-Dateien im selben Ordner ab.

Das komplette Kodieren von VBS-Skripten kann aber unerwünscht sein, weil vielleicht am Anfang der Datei Hinweise oder eine Copyright-Notiz lesbar bleiben sollen. Im folgenden Beispiel bleiben alle Skriptzeilen bis einschließlich `**Start Encode**´ erhalten.

´ Vor dem Kodieren:
´ **Start Encode**
kennwort = InputBox("Bitte geben Sie Ihr Kennwort ein!")
if kennwort = "strenggeheim" then
	MsgBox "Sie sind einer von den Guten.", vbInformation
else
	MsgBox "Alarm!", vbCritical
end if
´ Vor dem Kodieren:
´**Start Encode**#@~^0QAAAA==@#@&3nxSWDDP{~q 
w;Y~WacrAkDO+,o+(nUPUk P(t.~n+UxSGDDPnr "Jb@#@&kWP0nxAKDDP´,JkOD xL tnrs
JPD4+@#@&i\do~WXPE?rn,/rx9~+bxn.,\WU~9+UPV;D+UcJBP\(qWWM:CYbWU@#@&
+Vkn@#@&dHkL$WXPrbsl.heJSP78ZMkOr1lV@#@& x[PbW@#@&fD8AAA==^#~@ 
Das Ganze hat allerdings einen Haken. Kodierte Skripte lassen sich nur ausführen, wenn entweder der Script Encoder installiert und die vbe-Endung mit der Script-Engine VBScript.Encode verknüpft ist, oder wenn kodierte Skripte explizit diese Script-Engine angeben, zum Beispiel im Rahmen einer .WS-Datei. Das demonstriert das Listing KODIERT.WS.

Listing KODIERT.WS

ENCODE.VBS hilft beim Kodieren von Skripten: So behalten Sie Ihre Programmiergeheimnisse für sich.

' ENCODE.VBS
' Verschlüsselt VBS-Skripte, die auf dieses Skript gezogen
' werden. Dazu muss Script Encoder installiert sein
' (C) Tobias Weltner, c't 21/99

set wshshell = CreateObject("WScript.Shell")
set args = WScript.Arguments

' HIER DEN PFAD ZU IHRER SCRENC.EXE EINTRAGEN!
encoder = "C:\Program Files\Microsoft Script Encoder\SCRENC.EXE"

if args.Count>0 then
     for each argument in args
          path = left(argument, InstrRev(argument, "\"))
          name = mid(argument, InstrRev(argument, "\")+1)
          ext = lcase(mid(argument, InstrRev(argument, ".")+1))
          if not ext="vbs" then
               MsgBox "Es können nur VBS-Skripte kodiert werden!"
          else
               antwort = MsgBox("Soll " & name & _
                    " verschlüsselt werden?", _
                    vbYesNo + vbQuestion)
               if antwort = vbYes then
                    encname = Replace(lcase(name), _
                         ".vbs", ".vbe")
                    befehl = """" & encoder & """ """ _
                         & argument & """ """ & path _
                         & encname & """"
                    resultat = WSHSHell.Run(befehl,0,true)
                    wshshell.Popup name & " wurde codiert!", 2
               end if
          end if
     next
else
     MsgBox "Dies ist ein Drag&Drop-Ziel!" _
          & "Bitte VBS-Dateien darauf fallen lassen."
end if
Kasten 5

Listing 4

EXPLORER.VBS steuert die Windows-Oberfläche fern.

' EXPLORER.VBS
' markiert eine Datei im Explorer und
' schaltet die Ansicht um.
' Webansicht muss abgeschaltet sein
' (C) Tobias Weltner, c't 21/99

set wshshell = CreateObject("WScript.Shell")

Open "C:\"
View 4
Mark "msdos.sys"

sub Open(path)
     wshshell.run chr(34) & path & chr(34)
     WScript.Sleep 500
end sub

sub Mark(name)
     wshshell.SendKeys(name)
     WScript.Sleep 500
end sub

sub View(mode)
     wshshell.SendKeys("%A")
     WScript.Sleep 100
     select case mode
          case 1: wshshell.SendKeys("G")
          case 2: wshshell.SendKeys("K")
          case 3: wshshell.SendKeys("L")
          case 4: wshshell.SendKeys("D")
     end select
     WScript.Sleep 500
end sub
Kasten 6

Listing 5

DATEI.VBS stöbert mit Windows-Bordmitteln nach alten Dateien.

' DATEI.VBS
' sucht Dateien, die seit mindestens einem Monat
' nicht mehr benutzt wurden.
' Erfordert das IE4 Desktop Upgrade oder Windows 98
' (C) Tobias Weltner, c't 21/99

MsgBox "Suche Dateien, die mehr als 1 Monat unbenutzt sind!"

set shell = CreateObject("Shell.Application")
set wshshell = CreateObject("WScript.Shell")

shell.FindFiles
WScript.Sleep 1000
wshshell.SendKeys "*.TXT *.BMP *.JPG *.HTM *.HTML{TAB}{TAB}C:"
wshshell.SendKeys "{TAB 3}{RIGHT}"
wshshell.SendKeys "%F{TAB}{DOWN 2}"
wshshell.SendKeys "%W{TAB}1{RIGHT}1{RIGHT}1980"
wshshell.SendKeys "{TAB}{RIGHT}{DOWN}%S"
Kasten 7

Listing 6

WINORDNER.VBS - jetzt bloß keine Taste drücken!

' WINORDNER.VBS
' listet den Inhalt des Windows-Ordners
' im Editor auf.
' Achtung: während der Ausgabe des Ordnerinhaltes
' nicht das Vordergrundfenster wechseln!
' (C) Tobias Weltner, c't 21/99

set wshshell = CreateObject("WScript.Shell")
set fs = CreateObject("Scripting.FileSystemObject")

windir = wshshell.ExpandEnvironmentStrings("%WINDIR%")
MsgBox "Gebe Windows-Ordner aus! Während dieser Operation "_
     & "NICHT DAS VORDERGRUNDFENSTER wechseln!"


' Editor starten:
wshshell.Run "NOTEPAD.EXE"
wscript.Sleep 2000

' Windows-Verzeichnis ausgeben:
set folder = fs.GetFolder(windir)

' zuerst die Unterordner:
for each subfolder in folder.subfolders
     ausgabe = "[" & ucase(subfolder.name) & "]{ENTER}"
     send ausgabe
next

' dann die Dateien:
for each file in folder.files
     ausgabe = lcase(file.name) & "{ENTER}"
     send ausgabe
next

wshshell.Popup "Fertig!", 3



sub send(zeichen)
     wshshell.SendKeys zeichen
     WScript.Sleep 100
end sub
Kasten 8

Listing 7

Beim Troubleshooting hilfreich: VERSIONEN.VBS

' VERSIONEN.VBS
' listet alle DLLs im Systemordner mit
' ihrer internen Versionsnummer auf
' (C) Tobias Weltner, c't 21/99

set wshshell = CreateObject("WScript.Shell")
set fs = CreateObject("Scripting.FileSystemObject")

' Diese Liste nimmt Versionsinfos auf:
redim liste(3000,1)
counter=0

wshshell.Popup "Prüfe DLL-Versionen. Bitte Geduld!", 1

' Windows-Ordner ermitteln:
windir = wshshell.ExpandEnvironmentStrings("%WINDIR%")

' Protokolldatei öffnen:
logbuchname = "C:\DLL.HTM"
set logbuch = fs.CreateTextFile(logbuchname, true)
logbuch.WriteLine "<html><style>td {font:10pt Arial;" _
          & " background:""#CCCCCC""}</style><body><table>"
logbuch.WriteLine "<tr><td><b>DLL-Name</b></td>"_
          & "<td><b>Version</b></td></tr>"
' Systemordner öffnen:
set system = fs.GetFolder(windir & "\system")

' alle Dateien darin kontrollieren
for each file in system.files
     ext = lcase(fs.GetExtensionName(file.name))
     if ext="dll" then
          ' es ist eine DLL, also Version bestimmen:
          ver = GetVer(file.path)
          ' notieren:
          liste(counter,0) = lcase(file.name)
          liste(counter,1) = ver
          counter = counter + 1
     end if
next

SortResult 0
WriteResult

' Protokoll schließen und anzeigen:
logbuch.WriteLine "</table></body></html>"
logbuch.Close
wshshell.run logbuchname

function GetVer(pfad)
     on error resume next
     GetVer = CStr(fs.getFileVersion(pfad))
     if not err.Number=0 then
          GetVer = "??"
          err.clear
     end if
end function

sub WriteResult
     for x=0 to counter-1
          logbuch.WriteLine "<tr><td>" _
          & liste(x,0) & "</td><td>" _
          & liste(x,1) & "</td></tr>"
     next
end sub

sub sortresult(modus)
     if modus<0 or modus>1 then
          MsgBox "Sortiermodus muss 0 oder 1 sein!"
          exit sub
     end if
     for x=0 to counter-1
          for y=x+1 to counter-1
               if liste(x,modus)>liste(y,modus) then
                    temp1 = liste(x,0)
                    temp2 = liste(x,1)
                    liste(x,0) = liste(y,0)
                    liste(x,1) = liste(y,1)
                    liste(y,0) = temp1
                    liste(y,1) = temp2
               end if
          next
     next
end sub
Kasten 9

Listing 8

DATEIINFO.VBS zeigt mehr Details zu einer Datei.

' DATEIINFO.VBS
' meldet Detailinfos über Dateien und Ordner,
' die auf dem Skripticon abgeladen werden
' (C) Tobias Weltner, c't 21/99

set fs = CreateObject("Scripting.FileSystemObject")

' Argumente lesen
set args = WScript.Arguments
if args.Count=0 then
     MsgBox "Dieses Skript ist ein Drag&Drop-Ziel." _
     & " Ziehen Sie Dateien/Ordner auf das Skript-Icon!"
     WScript.Quit
else
     for x=0 to args.count-1
          GetInfo args(x)
     next
end if

sub GetInfo(path)
     ' Datei oder Ordner?
     if Instr(path, ".")>0 then
          ' von Datei ausgehen
          if fs.FileExists(path) then
               folder=false
               set file = fs.GetFile(path)
               muell = InputBox(GetDetails(file),_
                "Datei-Info", file.path)
          else
               ' doch ein Ordner?
               folder=true
          end if
     else
          ' ein Ordner:
          folder = true
     end if
     
     if folder then
          if fs.FolderExists(path) then
               set folder = fs.GetFolder(path)
               muell = InputBox(GetDetails(folder),_
               "Info", folder.path)          
          else
               MsgBox """" & path & """ nicht gefunden!"
          end if
     end if
end sub

function GetDetails(obj)
     txt = "Name: " & obj.name & vbCr
     txt = txt & "Typ: " & obj.Type & vbCr
     txt = txt & "angelegt: " & _
          DateValue(obj.DateCreated) & vbCr
     txt = txt & "geändert: " & _
          DateValue(obj.DateLastModified) & vbCr
     txt = txt & "benutzt: " & _
          DateValue(obj.DateLastAccessed) & vbCr
     txt = txt & "DOS-Name: " & _
          obj.ShortName & vbCr
     GetDetails = txt & "Größe: " _
          & FormatNumber(obj.Size/1024,2) & " KB" & vbCr
end function
Kasten 10

Listing 9

DEBUGSKRIPT.VBS hilft beim Debugging von Skripten.

' DEBUGSKRIPT.VBS
' Drag&Drop-Anwendung:
' Öffnet alle Skripte im Debugger, die auf dieses
' Skripticon gezogen werden.
' Dazu muss der IE Script Debugger installiert sein
' (C) Tobias Weltner, c't 21/99

set fs = CreateObject("Scripting.FileSystemObject")
set args = WScript.Arguments
if args.Count=0 then
     MsgBox "Dieses Skript ist ein Drag&Drop-Ziel. " _
          & "Ziehen Sie Dateien/Ordner auf das Skript-Icon!"
     WScript.Quit
end if

set wshshell = CreateObject("WScript.Shell")
' ist Datei eine Skriptdatei?
if fs.FileExists(args(0)) then
     set datei = fs.GetFile(args(0))
     ext = lcase(fs.GetExtensionName(args(0)))
     if ext="vbs" or ext="js" or ext="ws" then
          wshshell.run "wscript.exe //X """ & args(0) & """"
     else
          MsgBox args(0) & " ist keine Skriptdatei!"
     end if
else
     MsgBox args(0) & " ist keine Datei!"
end if
Kasten 11

Listing 10

SENDEN.VBS kopiert Dateien an ein Ziel Ihrer Wahl.

' SENDEN.VBS
' Drag&Drop-Anwendung:
' Versendet Dateien an ein beliebiges Ziel,
' die auf diesem Skripticon abgelegt werden
' (C) Tobias Weltner, c't 21/99

set fs = CreateObject("Scripting.FileSystemObject")
set args = WScript.Arguments

if args.Count=0 then
     MsgBox "Dieses Skript ist ein Drag&Drop-Ziel. " _
          & "Ziehen Sie Dateien/Ordner auf das Skript-Icon!"
     WScript.Quit
end if

for x=0 to args.count-1
     if not fs.FileExists(args(x)) then
          MsgBox "Sie dürfen nur Dateien auswählen! " _
               & vbCr & """" & args(x) & """ ist keine Datei."
          WScript.Quit
     end if
next

ziel = InputBox("Wohin soll(en) die Datei(en) kopiert werden?")

if fs.FolderExists(ziel) then
     if not right(ziel,1)="\" then ziel = ziel + "\"
     for x = 0 to args.count-1
          fs.CopyFile args(x), ziel
     next
end if

MsgBox "Aktion erledigt."
Kasten 12

Listing 11

FORMULAR.VBS: Mit Hilfe des Internet Explorer Formulare in Skript-Oberflächen.

' FORMULAR.VBS
' verwendet ein IE-Fenster für Dialogfeld-Eingaben und verbindet
' IE-Schaltflächen über GetRef() mit Skript-Prozeduren
' (C) Tobias Weltner, c't 21/99

' Internet Explorer Objekt:
set ie = WScript.CreateObject("InternetExplorer.Application", "ie_")

' Pfad zum Skript:
path = left(WScript.ScriptFullName, _
          InstrRev(WScript.ScriptFullName, "\"))

' FORMULAR.HTML in den IE laden:
ie.navigate(path & "FORMULAR.HTML")
' warten, bis Seite geladen ist:
do
loop until ie.ReadyState=4

' Toolbars abschalten und sichtbar machen:
ie.Width = 300
ie.height = 150
ie.Toolbar = false
ie.Statusbar = false
ie.visible = true

' Referenz auf HTML-Dokument und OK-Button mit
' Prozedur "okbutton" verknüpfen:
set document = ie.document
document.all.okbutton.onClick = GetRef("okbutton")

' auf Buttonklick warten:
eingabe = ""
ende = false
eingabeok = false
do
     ' 300 Millisekunden warten, um Events 
     ' verarbeiten zu können:
     WScript.Sleep 300
loop until ende

if eingabeok then
     MsgBox "Sie haben eingegeben: " & eingabe
else
     MsgBox "Sie haben abgebrochen!"
end if

sub ie_onQuit
     ' wird ausgelöst, wenn das IE-Fenster
     ' geschlossen wird:
     ende = true
end sub

sub okbutton
     ' Button wurde angeklickt!
     ' Eingabe aus dem Textfeld lesen:
     eingabe = document.all.eingabe.Value
     eingabeok = true
     ' IE schließen:
     ie.Quit
end sub
Kasten 13

Listing 12

DOSINPUT.VBS spricht mit der Kommandozeile.

' DOSINPUT.VBS
' liest Eingaben aus dem DOS-Eingabestrom
' und gibt sie wieder aus
' Skript muss auf der DOS-Ebene mit CSCRIPT.EXE
' gestartet werden!
' (C) Tobias Weltner, c't 21/99

' Ein- und Ausgabe definieren
set eingabe = WScript.StdIn
set ausgabe = WScript.StdOut

ausgabe.WriteLine "Geben Sie 'exit' ein, um zu beenden!"

ok=true
do while not eingabe.atEndOfStream 
     zeile = eingabe.ReadLine
     if lcase(zeile)="exit" then 
          exit do     
     else
          ausgabe.WriteLine "Sie haben eingegeben: " & zeile
     end if
loop

ausgabe.WriteLine "PROGRAMM BEENDET."
Kasten 14

Listing 13

FORMULAR.HTML: Internet Explorer für Eingabefeld `missbraucht´.

<html>
<head><title>Eingabe</title></head>
<body>
<p>Bitte geben Sie etwas ein!
<input type="text" id=eingabe><br>
<input type="button" id=okbutton value="Abschicken!">
</body>
</html>
Kasten 15

Listing 14

KOMMANDO.VBS - Interpreter selbst gebaut

' KOMMANDO.VBS
' Interaktive VBScript-Umgebung
' Skript muss auf DOS-Ebene mit CSCRIPT.EXE
' gestartet werden und akzeptiert dann
' VBScript-Befehle sowie Listingfunktionen
' (C) Tobias Weltner, c't 21/99

' Ein- und Ausgabe definieren
set prveingabe = WScript.StdIn
set prvausgabe = WScript.StdOut

' Zeilennummern definieren
redim prvlisting(1000)

prvprompt = "VBScript> "

' Hinweise ausgeben:
prvausgabe.WriteLine "Geben Sie VBScript-Befehle ein!"
prvausgabe.WriteLine "Geben Sie 'exit' ein, um zu beenden!"
prvausgabe.Write prvprompt

do while not prveingabe.atEndOfStream 
     ' aktuelle Eingabe lesen:
     prvzeile = prveingabe.ReadLine

     ' Kommandos herausfischen:
     if lcase(prvzeile)="exit" then 
          exit do     
     elseif lcase(prvzeile)="clear" then
          for x=1 to 25
               prvausgabe.WriteLine
          next
     elseif lcase(prvzeile)="list" then
          ShowListing
     elseif lcase(prvzeile)="run" then
          ExecuteListing
     elseif lcase(prvzeile)="delete" then
          redim prvlisting(1000)
     else
          ' kein eigenes Kommando, also als
          ' neue Programmzeile interpretieren:
          ManageLine prvzeile
     end if
     ' Prompt ausgeben:
     prvausgabe.Write prvprompt
loop

prvausgabe.WriteLine "PROGRAMM BEENDET."

sub ShowListing
     ' Listing löschen:
     prvlist = ""

     ' alle nicht leeren Zeilen mit Zeilennummer
     ' ausgaben:
     for prvx=0 to UBound(prvlisting)
          if not prvlisting(prvx)="" then
               prvlist = prvlist & prvx & vbTab _
                    & prvlisting(prvx) & vbCrLf
          end if
     next
     prvausgabe.Write prvList
end sub

sub ExecuteListing
     ' Listing ausführen:
     for prvx=0 to UBound(prvlisting)
          ' alle nicht leeren Zeilen zusammenfassen und
          ' die speziellen Zeichen ?? und ??? durch die
          ' Ausgabebefehle ersetzen:
          if not prvlisting(prvx)="" then
               prvlist = prvlist & prvlisting(prvx) & vbCr
          end if
     next
     prvList = Replace(prvList, "???", "prvausgabe.Write ")
     prvList = Replace(prvList, "??", "prvausgabe.WriteLine ")

     ' ausführen:
     on error resume next
     ExecuteGlobal prvList
     if not err.Number=0 then
          prvausgabe.WriteLine "FEHLER IM LISTING: " _
               & Convert(err.description)
          err.clear
     end if
end sub

sub ManageLine(prvzeile)
     ' neue Zeile einfügen oder Zeile löschen:
     ' wo ist das erste Leerzeichen?
     prvpos = Instr(prvzeile, " ")
     if prvpos>0 then
          ' links davon ist Zeilennummer, rechts Code:
          prvnr = left(prvzeile, prvpos)
          prvcode = trim(mid(prvzeile, prvpos))
          if isNumeric(prvnr) then
               ' wirklich eine Zeilennummer?
               prvnr = Fix(prvnr)
               ' dann Code in Listing aufnehmen:
               prvlisting(prvnr) = prvcode
          else
               ' direkt ausführen
               ExecuteDirect
          end if
     else
          ' nur Zeilennummer, aber kein Code?
          ' dann Zeile löschen:
          if isNumeric(prvzeile) then
               prvlisting(Fix(prvzeile))=""
          else
               ' Zeile soll direkt ausgeführt werden:
               ExecuteDirect
          end if
     end if
end sub

sub ExecuteDirect
     prvzeile = Replace(prvzeile, "???", "prvausgabe.Write ")
     prvzeile = Replace(prvzeile, "??", "prvausgabe.WriteLine ")
     on error resume next
     ExecuteGlobal prvzeile
     if not err.Number=0 then
          prvausgabe.WriteLine "FEHLER: " _
               & Convert(err.description)
          err.clear
     end if
     on error goto 0
end sub          

function Convert(text)
     Convert = Replace(text, chr(228), chr(132))
     Convert = Replace(Convert, chr(252), chr(129))
     Convert = Replace(Convert, chr(196), chr(142))
     Convert = Replace(Convert, chr(220), chr(154))
     Convert = Replace(Convert, chr(214), chr(153))
     Convert = Replace(Convert, chr(246), chr(148))
     Convert = Replace(Convert, chr(223), chr(225))
end function
Kasten 16

Listing 15

FILTER.VBS - Piping fürs Konvertieren `missbraucht´.

' FILTER.VBS
' liest Input von DOS-Befehlen und
' wandelt die Zeichen in den ANSI-Code
' Anschließend wird das Ergebnis gespeichert
' und kann über Windows-Programme gelesen werden
' (C) Tobias Weltner, c't 21/99

' Filter für Verzeichnis-Listings

' Ein- und Ausgabe definieren
set eingabe = WScript.StdIn
set ausgabe = WScript.StdOut
set fs = CreateObject("Scripting.FileSystemObject")
set wshshell = CreateObject("WScript.Shell")

ausgabe.WriteLine "konvertiert Eingabe in Windows-ANSI " _
     & "und speichert Ergebnis in Datei"

' temporäre Ausgabedatei anlegen
tempname = "C:\" & fs.GetTempName
set tempfile = fs.CreateTextFile(tempname, true)

do while not eingabe.atEndOfStream 
     zeile = Convert(eingabe.ReadLine)
     tempfile.WriteLine zeile
loop

' Ausgabedatei schließen und öffnen:
tempfile.close
result = Wshshell.Run("NOTEPAD " & tempname,,true)

' Ausgabedatei löschen
fs.DeleteFile tempname, true


function Convert(text)
     Convert = Replace(text, chr(132), chr(228))
     Convert = Replace(Convert, chr(129), chr(252))
     Convert = Replace(Convert, chr(142), chr(196))
     Convert = Replace(Convert, chr(154), chr(220))
     Convert = Replace(Convert, chr(153), chr(214))
     Convert = Replace(Convert, chr(148), chr(246))
     Convert = Replace(Convert, chr(225), chr(223))
end function
Kasten 17

Listing 16

TYPE.WS benutzt die Class Library.

<!--
TYPE.WS
TypeLibrary-Informationen verwenden:
Skript referenziert die Shell.Application-TypeLibrary
und kann anschließend die darin definierten Konstanten
einsetzen.
' (C) Tobias Weltner, c't 21/99
-->
<Job>
<Reference guid={EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}/>
<Script language="VBScript">
     Set shell = CreateObject("Shell.Application")
     shell.Open ssfDRIVES
</Script>
</Job>
Kasten 18

Listing 17

Skripte wiederverwenden: INCLUDE.WS zieht Routinen von ...

<!--
INCLUDE.WS
Dieses Skript liest zuerst bibliothek1.vbs
ein und verwendet dann daraus Funktionen
(C) Tobias Weltner, c't 21/99
-->

<Job>
<script language="VBScript" src="bibliothek1.vbs" />
<Script language="VBScript">
     ShowFiles "C:\"
</Script>
</Job>
Kasten 19

Listing 18

... BIBLIOTHEK1.VBS heran.

' BIBLIOTHEK1.VBS
set fs = CreateObject("Scripting.FileSystemObject")

sub ShowFiles(path)
     set folder = fs.GetFolder(path)
     for each file in folder.files
          msg = msg & file.name & vbCr
     next
     MsgBox msg
end sub
Kasten 20

Listing 19

FEHLER.WS demonstriert den Umgang mit dem Debugger.

<!-- 
FEHLER.WS
demonstriert, wie XML-Tags
in .WS-Dateien Skripte einbetten.
Skript provoziert einen Fehler und
ruft wegen debug=true den Skript-
Debugger auf, sofern installiert
' (C) Tobias Weltner, c't 21/99
-->

<job>
<?job debug=true ?>
<script language="VBScript">
MsgBox "XML-verpacktes VBScript: provoziert gleich einen Fehler"
MsgBo "hier ist ein Fehler!"
</script>
</job>
Kasten 21

Listing 20

KLASSEN.WS - Klassen in einer Skriptsprache?

<!--
KLASSEN.WS
Skript verwendet eine in bibliothek2.vbs definierte
Klasse mit Eigenschaften und Methoden.
Die Klasse Attributes erlaubt es, das System-Attribut
von Dateien zu setzen oder zu löschen.
' (C) Tobias Weltner, c't 21/99
-->
<Job>
<script language="VBScript" src="bibliothek2.vbs" />
<Script language="VBScript">
set fs = CreateObject("Scripting.FileSystemObject")
     dim helper
     set helper = New Attributes
     set datei = fs.GetFile("C:\MSDOS.SYS")
     helper.Object = datei
     status = helper.System
     MsgBox "Status des System-Attributes: " & status
     helper.System = true
     MsgBox "Status des System-Attributes: " & helper.System
     helper.System = false
     MsgBox "Status des System-Attributes: " & helper.System
     helper.System = status
</Script>
</Job>
Kasten 22

Listing 21

BIBLIOTHEK2.VBS hilft der Klasse mit einer Routine aus.

' Bibliothek 2
Private fsobj 

Class Attributes
     ' Initialisieren
     public property Let Object(obj)
          set fsobj = obj
     end property

     public property Get Object()
          set Object = fsobj
     end property

     ' System-Attribut
     public property Let System(mode)
          if mode then
               fsobj.Attributes = fsobj.Attributes and not 216 or 4
          else
               fsobj.Attributes = fsobj.Attributes and not 216 and not 4
          end if
     end property

     public property Get System
          if (fsobj.Attributes and 4)=0 then
               System = false
          else
               System = true
          end if
     end property
end class



set fs = CreateObject("Scripting.FileSystemObject")

sub ShowFiles(path)
     set folder = fs.GetFolder(path)
     for each file in folder.files
          msg = msg & file.name & vbCr
     next
     MsgBox msg
end sub

Zu diesem Artikel existieren Programmbeispiele
2199_294.zip

2199_294.txt