Unterthema: Handwerkszeug
Unterthema: Listing 1
Unterthema: Listing 2
Unterthema: Listing 3
Unterthema: Listing 4
Unterthema: Listing 5
Unterthema: Listing 6
Anders als etwa MacOS besitzt Windows keinen Ordner `Ausschaltobjekte´, in dem der Anwender Programme und Skripte seiner Wahl platzieren kann. Es gibt zwar Shareware, die in den Ausschaltprozess einzugreifen vermag (`Shutdown-König´, siehe c't 12/99, S. 134), doch deren Möglichkeiten sind recht eingeschränkt im Vergleich zu jenen, die man sich mit dem Scripting Host von Windows erschließt [2, 3].
Wir haben insgesamt fünf Skriptprogramme fürs Herunterfahren implementiert, die zusammengenommen komfortabler sind als der von Microsoft vorgesehene Befehl. Wer vor dem Ausschalten die Festplatte automatisch defragmentieren, reparieren oder auch wichtige Daten sichern lassen möchte, sollte also in Zukunft sein Windows nicht mehr mit dem bekannten `Beenden´-Aufruf schließen. Genau wie dieser schalten die Skripte den Computer nach dem Herunterfahren sogar aus - sofern er über ein ATX-Netzteil verfügt.
Experimentieren Sie mit den einzelnen Varianten des Skriptes und fügen Sie bei Bedarf eigene Funktionen hinzu. Die entsprechend gekennzeichneten Listings arbeiten nur unter Windows 98. Alle haben eines gemeinsam: In den Skriptkonstanten sind die Herunterfahr-Befehle gespeichert, die mit der Run-Methode des WSHShell-Objekts aufgerufen werden. Die Systemvariable %WINDIR%, die das Windows-Verzeichnis enthält, wird dabei zuvor über ExpandEnvironmentStrings durch den tatsächlichen Ordnernamen ersetzt.
Bemerkenswert ist die undokumentierte Datei SOFTBOOT.EXE, mit der man das System neu starten kann. Sie existiert allerdings nur, wenn der Internet Explorer 4 installiert ist. Unter Windows 9x findet sie sich dann im Verzeichnis `Windows\System´, bei Windows NT unter `WinNT\System32´.
Execute prüft zunächst in der Registry, ob für den jeweiligen Auftrag bereits ein Schlüssel eingetragen ist. Falls ja, liest es aus dem Schlüssel das Datum, an dem das Tool zum letzten Mal ausgeführt wurde. Anschließend ermittelt es via DateDiff die Zeitdifferenz zum aktuellen Datum und kann so entscheiden, ob es Zeit ist, diesen Wartungsbefehl auszuführen. Da Windows von Haus aus mit zweistelligen Jahreszahlen arbeitet, wird die Routine beim Jahrtausendwechsel stolpern. Das können Sie verhindern, indem Sie die `Ländereinstellungen´ der Systemsteuerung auf vierstellige Jahreszahlen umstellen.
Ist das Intervall überschritten, ruft die Run-Methode den Wartungsbefehl auf. Der zusätzliche Parameter true lässt das Skript so lange anhalten, bis die Wartung abgeschlossen ist, sodass sich die Tools nicht gegenseitig ins Gehege kommen. Schließlich vermerkt das Skript das aktuelle Ausführungsdatum im Registry-Schlüssel des Tools.
Damit das Skript bequem zu handhaben ist, gibt es einige Meldungen aus. Steht beispielsweise ein Wartungsprogramm zur Ausführung an, so meldet das Skript dies und gibt die Möglichkeit, die Wartung abzubrechen und sofort herunterzufahren. Die Wartung wird dann beim nächsten Herunterfahren automatisch neu angeboten.
Darüber hinaus fragt das Skript nach jedem Wartungsprogramm nach, ob Sie den gesamten Prozess abbrechen wollen. Kommen Sie beispielsweise nach einigen Stunden an den Rechner und stellen fest, dass dieser noch mit Reparatur- oder Aufräumarbeiten beschäftigt ist, dann können Sie auf diese Weise das laufende Wartungsprogramm abbrechen und anschließend den Herunterfahr-Prozess abbrechen. Allerdings erscheint die Abfrage nur für vier Sekunden. So ist gewährleistet, dass das Skript im unbeaufsichtigten Modus nicht ewig wartet.
Das Skript speichert die Ausführungsdaten der einzelnen Wartungstools im Registry-Schlüssel HKEY_CURRENT_USER\Software\CTmagazin\. Das Skript CLEAR.VBS löscht diese Informationen auf Wunsch wieder - nützlich etwa, um das Skript ausgiebig zu testen. Es entfernt alle Eintragungen, sodass beim nächsten Herunterfahren wieder alle Wartungstools unabhängig vom eingestellten Wartungsintervall ausgeführt werden.
Durch die Eingabe von SCANDSKW.EXE /SAGESET:5 im Startmenü `Ausführen´ legt man alle Optionen des ScanDisk-Tools fest, die man später per /SAGERUN:5 aufrufen möchte. Wählen Sie als Prüfmethode `Standard´, und aktivieren Sie die Option `Fehler automatisch korrigieren´. Damit das Tool nach vollbrachter Arbeit keine störenden Meldungen anzeigt, die das Herunterfahren stoppen könnten, klickt man anschließend noch auf `Erweitert´ und wählt im Feld `Zusammenfassung anzeigen´ die Option `Nie´.
Rufen Sie als nächstes SCANDSKW.EXE /SAGESET:6 auf, schalten Sie auf `Intensiv´, aktivieren Sie je nach Wunsch weitere Optionen und schalten Sie wiederum `Fehler automatisch korrigieren´ ein. Der Festplatten-Optimierer Defrag lässt sich analog dazu über DEFRAG.EXE /SAGESET:5 konfigurieren. Welche Ziffern Sie hinter /SAGESET: respektive /SAGERUN: verwenden, spielt keine Rolle - nur die 1 sollten Sie vermeiden, denn die verwendet der Windows-98-Wartungsassistent.
Execute """C:\TEST\DOCLEAN.VBS""", 0, "befehlx"Wie oben beschrieben, erfordert Execute drei Parameter, nämlich den Befehlsaufruf, das Wartungsintervall (0 steht für `jedes Mal´) sowie einen unverwechselbaren Namen. Anstelle von `befehlx´ können Sie auch einen beliebigen anderen wählen. Da VBScript zwei Anführungszeichen in eins verwandelt, muss man hier drei angeben.
BackItUp "C:\BRIEFE", "D:\BACKUP", ";doc;", 1Nach getaner Arbeit gibt das Skript eine Erfolgsmeldung aus - aber nur für zehn Sekunden, wiederum, um andere Tätigkeiten nicht zu behindern.
Da das BACKITUP-Skript das gesamte Dateisystem durchsuchen kann, eignet es sich auch, um etwa überflüssige Dateien per Dateityp, Name oder letztem Dateizugriff zu sammeln und zu löschen. Das Umschreiben ist in wenigen Minuten erledigt, doch Vorsicht beim rekursiven Löschen - ein falscher Parameter kann zu verheerenden Ergebnissen führen!
In der Variablen typen legt man durch Semikola getrennt fest, mit welchen Dateitypen man tatsächlich regelmäßig arbeitet. Im abgedruckten Skript sind das beispielsweise die Textdokumente .doc und .txt sowie das Bildformat .bmp. Alle Verweise, die nicht auf einen dieser Dateitypen verweisen, werden aus dem Dokumente-Menü entfernt: Nach dem nächsten Systemstart liefert es endlich einen wirklich wertvollen Überblick über alle wichtigen Dateien, mit denen kürzlich gearbeitet wurde.
Wie schon in den letzten beiden Artikeln zum Scripting Host können wir hier aus Platzgründen nur funktionierende Beispiele ohne größeren Komfort bieten. Experimentieren Sie selbst, sichern Sie Ihre individuellen Dokumenttypen, speichern Sie Ihre Arbeitszeiten in einer Tabelle oder lassen Sie sich eine ganz neue Idee einfallen: Die Skripte bilden das ideale Grundgerüst dafür. (se)
[2] Dr. Tobias Weltner: Windows im Griff, Mit dem Scripting Host wird das System erst rund, c't 10/99, S. 96
[3] Dr. Tobias Weltner: Scandruckfaxkopierer, Einlesen, Ausgeben, Bearbeiten mit Windows-Bordmitteln, c't 14/99, S. 192
[4] http://wwwthep.physik.uni-mainz.de/~frink/newgina_pre09.zip
REM Doclean.VBS (c) c't/Tobias Weltner typen = ";doc;txt;bmp;" set fs = CreateObject("Scripting.FileSystemObject") Set WshShell = Wscript.CreateObject("Wscript.Shell") set ordner = fs.GetFolder(WshShell.SpecialFolders("Recent")) for each datei in ordner.files if lcase(fs.GetExtensionName(datei.path)) = "lnk" then set scut = WshShell.CreateShortcut(datei.path) ziel = scut.TargetPath extension = ";" & lcase(fs.GetExtensionName(ziel)) & ";" if Instr(lcase(typen), extension)=0 then datei.delete vbtrue end if end if nextKasten 3
REM Autoclose.VBS (c) c't/Tobias Weltner do loop until CloseAllWindows=0 function CloseAllWindows set Shell = CreateObject("Shell.Application") set shellwindows = Shell.Windows for each window in shellwindows set parent = window.Application parent.Quit next set shellwindows = Shell.Windows CloseAllWindows = shellwindows.Count end functionKasten 4
REM Herunterfahren.VBS (c) c't/Tobias Weltner ' Dieses Skript fährt den Rechner auf verschiedene Arten herunter const sd_ausschalten9598 = "rundll32.exe user,ExitWindows" const sd_kaltstart9598IE4 = "%windir%\system\softboot.exe /s:,60" const sd_kaltstartNTIE4 = "%windir%\system32\softboot.exe /s:,60" const sd_warmstart9598 = "rundll.exe user,ExitWindowsExec" const sd_anmelden98 = "rundll32.exe shell32.dll,SHExitWindowsEx 0" const sd_ausschalten98 = "rundll32.exe shell32.dll,SHExitWindowsEx 1" const sd_kaltstart98 = "rundll32.exe shell32.dll,SHExitWindowsEx 2" frage = "Wollen Sie Windows wirklich beenden?" header = "Beenden" antwort = MsgBox(frage, vbQuestion + vbYesNo, header) if antwort = vbYes then ShutDown sd_ausschalten9598 end if sub ShutDown(variante) set WSHShell = CreateObject("WScript.Shell") variante = WSHShell.ExpandEnvironmentStrings(variante) WSHShell.Run variante end subKasten 5
REM Herunterfahren2.VBS (c) c't/Tobias Weltner ' Dieses Skript fährt den Rechner auf verschiedene Arten herunter const sd_ausschalten9598 = "rundll32.exe user,ExitWindows" const sd_kaltstart9598IE4 = "%windir%\system\softboot.exe /s:,60" const sd_kaltstartNTIE4 = "%windir%\system32\softboot.exe /s:,60" const sd_warmstart9598 = "rundll.exe user,ExitWindowsExec" const sd_anmelden98 = "rundll32.exe shell32.dll,SHExitWindowsEx 0" const sd_ausschalten98 = "rundll32.exe shell32.dll,SHExitWindowsEx 1" const sd_kaltstart98 = "rundll32.exe shell32.dll,SHExitWindowsEx 2" public asked public wartung public interrupt public abbrechen frage = "Wollen Sie Windows wirklich beenden?" header = "Beenden" antwort = MsgBox(frage, vbQuestion + vbYesNo, header) if antwort = vbYes then ShutDown sd_ausschalten9598 end if sub ShutDown(variante) set WSHShell = CreateObject("WScript.Shell") variante = WSHShell.ExpandEnvironmentStrings(variante) Wartungsprogramme if not abbrechen=vbYes then WSHShell.Run variante end sub sub Wartungsprogramme Execute "SCANDSKW.EXE /SAGERUN:5", 5, "befehl1" Execute "SCANDSKW.EXE /SAGERUN:6", 30, "befehl2" Execute "DEFRAG.EXE /SAGERUN:5", 14, "befehl3" end sub sub Execute(befehl, wann, key) set WSHShell = CreateObject("WScript.Shell") regkey = "HKCU\Software\CTmagazin\" + key + "\datum\" if KeyExists(regkey) then lastExec = WSHShell.RegRead(regkey) timediff = DateDiff("d", lastExec, Date()) if timediff>=wann then doit = true else doit = false end if else doit = true end if if doit then if not asked then wartung = MsgBox("Es stehen einige Wartungen an. Durchführen?", vbQuestion + vbYesNo) asked = true end if if wartung = vbYes then WSHShell.Run befehl, 1, true WSHShell.RegWrite regkey, date() abbrechen = WSHShell.Popup("Herunterfahren abbrechen?", 4, "Unterbrechung", vbQuestion + vbYesNo) if abbrechen = vbYes then wartung = vbNo end if end if set WSHShell = Nothing end sub function KeyExists(key) on error resume next set WSHShell = CreateObject("WScript.Shell") test = WSHShell.RegRead(key) if err.number<>0 then err.clear KeyExists = false else KeyExists = true end if set WSHShell = Nothing end functionKasten 6
REM Clear.VBS (c) c't/Tobias Weltner set WSHShell = CreateObject("WScript.Shell") on error resume next WSHShell.RegDelete "HKCU\Software\CTmagazin\" if err.Number=0 then MsgBox "Registry-Einstellungen gelöscht", vbinformation else MsgBox "Keine Registry-Einstellungen vorhanden", vbInformation end ifKasten 7
REM BackItUp.VBS (c) c't/Tobias Weltner set fs = CreateObject("Scripting.FileSystemObject") set WSHShell = CreateObject("WScript.Shell") erneuert = 0 neukopiert = 0 uptodate = 0 unzutreffend = 0 neuordner = 0 problem = 0 ' --------------------------------------------------------------------- ' Syntax: BackItUp quelle, ziel, extensionen, modus ' quelle: Ordner mit den Originalen ' ziel: Ordner mit den Sicherheitskopien ' extensionen: Liste der Dateiextensionen, die gesichert werden sollen ' Extensionen durch Semikola trennen: ";vbs;doc;txt;" ' modus: 1 auch Unterordner sichern ' 2 alle Dateien sichern ' 4 immer neu kopieren BackItUp "C:\WINDOWS\DESKTOP\CT", "C:\BACKUP1", "", 1+2 ' --------------------------------------------------------------------- ' Statusreport für 10 Sekunden anzeigen: r = neukopiert & " Dateien neu kopiert" + vbCr r = r & erneuert & " Dateien aktualisiert" + vbCr r = r & uptodate & " Dateien befanden sich auf aktuellem Stand" + vbCr r = r & unzutreffend & " Dateien entsprachen nicht den Auswahlkriterien." + vbCr r = r & problem & " Dateien konnten aufgrund von Fehlern nicht kopiert werden." + vbCr r = r & neuordner & " neue Ordner angelegt." WSHShell.Popup r, 10, "Report", vbInformation sub BackItUp(original, ziel, extensionen, mode) ' "\" am Ende des Pfadnamens entfernen, falls vorhanden: if right(original,1)="\" then original=left(original, len(original)-1) if right(ziel,1)="\" then ziel=left(ziel, len(ziel)-1) ' Fehler abfangen: if not fs.FolderExists(original) then fehler = "Quell-Ordner """ & original & """ existiert nicht!" end if if not fs.FolderExists(ziel) then fehler = "Ziel-Ordner """ & ziel & """ existiert nicht!" end if if not fehler="" then MsgBox fehler, vbCritical WScript.Quit end if ' Quellordner sichern: set ordner = fs.GetFolder(original) BackupOrdner ordner, original, ziel, extensionen, mode end sub sub BackupOrdner(folderobj, original, sicherung, extensionen, mode) ' Dateien in diesem Ordner sichern BackupFiles folderobj, original, sicherung, extensionen, mode ' Unterordner in diesem Ordner sichern ' (rekursiver Aufruf) if (mode and 1) then for each unterordner in folderobj.subfolders BackupOrdner unterordner, original, sicherung, extensionen, mode next end if end sub sub BackupFiles(ordner, original, sicherung, extensionen, mode) ' alle Dateien in diesem Ordner auflisten for each datei in ordner.files ' Dateiextension der aktuellen Datei ermitteln: extension = ";" & lcase(fs.GetExtensionName(datei.Path)) & ";" ' bei mode=2 oder passender Extension sichern: if (mode and 2) or (Instr(lcase(extensionen), extension) >0) then ' Original wann zuletzt geändert? alteroriginal = datei.DateLastModified ' liegt die Originaldatei in einem Unterordner? ' Pfad der Datei bestimmen: dateipfad = datei.path ' Dateinamen vom Pfad entfernen: dateipfad = left(dateipfad, InstrRev(dateipfad, "\")) ' nur das Unterverzeichnis des Originale-Ordners übriglassen: dateipfad = mid(dateipfad, len(original)+1) ' Sicherungsordner + evtl. Unterordner: sicherungsordner = sicherung + dateipfad ' Zieldateiname: zieldatei = sicherungsordner & datei.Name ' Existiert bereits die Sicherungskopie? zieldateiexist = fs.FileExists(zieldatei) if zieldateiexist then ' von wann ist die Sicherungskopie? alterziel = fs.GetFile(zieldatei).DateLastModified else alterziel = CDate("1.1.80") flag = true end if ' Sicherungskopie ist veraltet oder mode=4: if (alteroriginal>alterziel) or (mode and 4) then ok = CopyIt(datei,sicherungsordner) if flag and ok then neukopiert = neukopiert + 1 elseif ok then erneuert = erneuert + 1 else problem = problem + 1 end if else uptodate = uptodate + 1 end if else unzutreffend = unzutreffend + 1 end if next end sub function CopyIt(filehandle, zielordner) ' kopiert die Datei on error resume next if not fs.FolderExists(zielordner) then fs.CreateFolder zielordner neuordner = neuordner + 1 end if ziel = zielordner & filehandle.name filehandle.copy ziel, true if err.number=0 then CopyIt = true else CopyIt = false err.clear end if end function