Eine Sitzung mit der Klasse

Von The Microsoft Scripting Guys

Sesame Script

Willkommen zu Sesame Script, der Kolumne für Anfänger in der Skriptprogrammierung. In dieser Reihe werden erste grundlegende Kenntnisse zur Skriptprogrammierung vermittelt, durch die Sie viele Aufgaben der Systemadministration unter Windows automatisieren können. Durch die vorgestellten Konzepte der Skriptprogrammierung werden Sie in der Lage sein, Skripts zu lesen und zu verstehen und Ihre ersten eigenen Änderungen oder Erweiterungen in Skripts vorzunehmen. Sollte Ihnen ein spezieller Aspekt der Skriptprogrammierung Schwierigkeiten bereiten, lassen Sie es uns wissen (in englischer Sprache). Sehr wahrscheinlich sind Sie nicht der einzige, der vor einem bestimmten Problem steht. Dieser Artikel enthält Links zu englischsprachigen Seiten.

Lesen Sie auch die bisherigen Artikel im Sesame Script-Archiv.

Auf dieser Seite

Eine Sitzung mit der Klasse
Was ist eine Klasse?
Eigenschaften
Methoden
Methodenparameter
Objekte
Bindung
Freigeben von Objekten
Die wirkliche Welt
Happy Hour

Eine Sitzung mit der Klasse

Häufig beobachten die Scripting Guys, wie Leute an einem Computerprogrammierungskurs (oder sogar einem Skriptprogrammierungskurs) für Anfänger teilnehmen und vollständig verwirrt zurückkommen. Dabei handelte es sich um Personen, die täglich mit dem Computer arbeiten und äußerst intelligent zu sein schienen. Warum kamen sich also alle anschließend wie Idioten vor? Waren sie allesamt doch nicht ganz so intelligent? Obwohl wir diese Möglichkeit nicht vollständig ausschließen wollen, ist es doch wahrscheinlicher, dass sie sich etwas zu schnell in die Konzepte der Kurse und Einheiten gestürzt haben. Machen Sie sich jedoch keine Sorgen. Wir werden uns hier Zeit lassen und unser Bestes geben, Sie durch dieses Thema zu leiten. Und vielleicht – nur vielleicht – fühlen Sie sich anschließend nicht ganz so verwirrt wie all die anderen armen Leute.

Was ist eine Klasse?

Eine Klasse entspricht etwa einem Bauplan für ein Haus oder einer Blaupause. Auf dem Bauplan für ein Haus kann man erkennen, dass das Haus eine Küche, drei Schlafzimmer, ein Badezimmer, usw. hat. Er zeigt sogar, dass die Küche einen Ofen und eine Spüle und das Schlafzimmer einen Kleiderschrank hat. Wir sehen jedoch nicht, in welche Richtung die Wasserhähne an der Spüle gedreht werden, wie viel Wasser aus ihnen kommt, und was im Kleiderschrank aufbewahrt werden sollte. Hier ist ein Beispielplan für eine Küche:

Küchenplan

Eine Klasse ist insofern vergleichbar, als sie ein Framework bietet, mit dem Sie arbeiten können. Wie also würde eine Küchenklasse aussehen?

Küchenklasse

Natürlich können Sie sich auf dem Küchenplan kein Ei braten, Sie müssen zunächst die Küche bauen und einen Ofen installieren. Genauso wenig können Sie mit der Küchenklasse irgendetwas anfangen. Sie können sie lediglich verwenden, um ein Küchenobjekt zu erstellen. Zu den Objekten kommen wir jedoch erst später.

Eigenschaften

Ein ganzes Haus – und sogar eine Küche – ist recht groß und kompliziert. Daher wollen wir uns etwas Einfacherem zuwenden und lediglich einen Kühlschrank betrachten. Zunächst werden wir die Charakteristika eines Kühlschranks untersuchen. Da ist zunächst einmal die Farbe – Sie möchten sicher keinen einfachen weißen Kühlschrank in Ihrer schönen hellgrünen und violetten Küche haben. Außerdem benötigen Sie Fächer für die Nahrungsmittel. Wie viele? Befindet sich in der Tür ein Eisspender?

Was haben wir bisher ermittelt:

Refrigerator
    Color
    Shelves
    IceDispenser

Überraschung! Sie haben gerade die Eigenschaften der Kühlschrankklasse definiert. Noch mal:

Refrigerator
    string Color
    int Shelves
    boolean IceDispenser

"Color" muss der Name einer Farbe sein, deshalb haben wir dies als Zeichenfolge definiert. "Shelves" gibt die Anzahl der Fächer im Kühlschrank an, daher haben wir einen Integer-Wert angegeben. "IceDispenser" ist lediglich ein Ja/Nein-Wert – entweder gibt es einen oder nicht – daher haben wir einen Booleschen Wert eingegeben.

Erweitert: Obwohl wir den Eigenschaften hier Datentypen zugewiesen haben (Zeichenfolge, Integer, Boolescher Wert), verwendet VBScript eigentlich keine derartigen Typen. VBScript verwendet nur einen Datentyp: Variant. Was ist ein Variant-Typ? Mit einem Variant-Wert wird in VBScript ein "beliebiger Typ" angegeben. In den SDKs auf MSDN werden Sie jedoch feststellen, dass bestimmte Datentypen definiert sind. Diese Datentypen werden von anderen Sprachen (bspw. C++) benötigt. In VBScript ist die Definition im SDK nur deshalb von Bedeutung, weil Sie so erfahren, welchen Datentyp Sie erwarten können. Mit den Datentypen beschäftigen wir uns möglicherweise in einem zukünftigen Artikel.

Weitere Informationen

Datentypen unter VBScript – Microsoft Windows 2000 – Scripting-Handbuch

Elementary Data Types (in englischer Sprache) – Visual Basic Language Reference

Methoden

Wir haben einige Charakteristika (oder Eigenschaften) unseres Kühlschranks identifiziert. Nun müssen wir uns Gedanken darüber machen, was wir mit unserem Kühlschrank anfangen möchten. Am offensichtlichsten ist es, dass wir darin Dinge kühlen möchten. Dafür müssen wir eine Temperatur einstellen. Wenn wir uns für einen Eisspender entschieden haben, müssen wir nun festlegen, ob wir Würfel oder gestoßenes Eis möchten. Siehe da! Jetzt verfügen wir über Methoden:

Refrigerator
        SetTemperature
        SelectIce

Methodenparameter

Wir können nun die Methode SetTemperature verwenden, um die Temperatur unseres Kühlschranks einzustellen. Wir können dem Kühlschrank jedoch nicht einfach sagen: "Stell die Temperatur ein!". Wir müssen ihm sagen, welche Temperatur wir möchten. Hierfür müssen wir mit der Methode einen Parameter verwenden. In diesem Fall ist der Parameter der Wert, auf den wir die Temperatur einstellen möchten:

SetTemperature(39)

Für das Eis geben wir der Methode lediglich einen Zeichenfolgenparameter, entweder "crushed" (gestoßen) oder "cubed" (gewürfelt):

SelectIce("crushed")

Diesen Vorgang nennt man das Übergeben eines Parameters an die Methode. Wir übergeben einen Parameter, um der Methode bestimmte Informationen zur Verfügung zu stellen, mit der diese arbeiten kann. Wir können einen Parameter auch mithilfe einer Variablen übergeben (wie in einer früheren Kolumne besprochen):

intTemperature = 39
SetTemperature(intTemperature)

Woher wissen wir jedoch, dass wir der Methode SetTemperature beim Aufrufen eine Zahl und der Methode SelectIce eine der Zeichenfolgen "crushed" oder "cubed" übergeben müssen? Wir sehen im entsprechenden SDK (Software Development Kit) nach. Sie finden auf MSDN SDK-Dokumentationen für die meisten Skriptprogrammierungstechnologien, die Sie verwenden werden.

Erweitert: Eigenschaften und Methoden

So wie wir die Eigenschaften und Methoden hier in unserer idealen Welt des Kühlschrankwesens darstellen, gibt es einen deutlichen Unterschied zwischen den beiden Elementen: Eigenschaften beschreiben etwas, während die Methoden eine Aktion durchführen. Das klingt sinnvoll, nicht wahr? Warum haben wir dann jedoch eine Methode für SetTemperature, anstatt einfach eine Eigenschaft Temperature zu verwenden und dieser eine Zahl zuzuweisen, die der Temperatur entspricht? Warum benötigen wir eine Methode?

Wir wären überrascht, wenn Sie darauf eine gute Antwort hätten, wir haben nämlich keine. Lassen Sie uns zum besseren Verständnis einen Blick auf einige Beispiele aus dem wirklichen Leben nehmen.

Die WMI-Klasse Win32_OperatingSystem verfügt über einige Eigenschaften und vier Methoden. Bei den Eigenschaften handelt es sich um Dinge wie LastBootUpTime, SerialNumber und Version. Diese Features sind dem Betriebssystem zugeordnet, was mit unserer Definition von Eigenschaften absolut übereinstimmt. Selbst die Methoden erscheinen sinnvoll: Reboot, SetDateTime, Shutdown und Win32Shutdown. Hierbei handelt es sich sämtlich um Aktionen, um Dinge, die am Betriebssystem vorgenommen werden müssen. So weit so gut, oder?

Betrachten Sie nun die WMI-Klasse Win32_Directory. Diese Klasse verfügt über viele Eigenschaften, die man beim Arbeiten mit Verzeichnissen erwartet, z. B. FileName, Extension und Hidden. Betrachten Sie nun die Methoden. Einen Augenblick – da gibt es die Methode Rename. Es gibt jedoch auch die Eigenschaft Name. Warum benötigen wir anscheinend die Methode Rename, anstatt lediglich die Eigenschaft Name festzulegen?

Als ob das noch nicht verwirrend genug wäre, findet man mitunter eine Klasse mit einer Methode Rename aber ohne jegliche Eigenschaft Name. Warum? Wir wissen es nicht. Hier hat der Entwickler, der die Klasse erstellt hat, schlicht und einfach so gut wie möglich geraten, ob etwas ein Merkmal oder eine Aktion sein soll. Für uns Außenstehende ist das mitunter schwer verständlich, daran lässt sich jedoch wenig ändern.

Weitere Informationen

WMI-SDK

ADSI-SDK

Objekte

So sieht unsere Klasse nun aus:

Refrigerator
    string Color
    int Shelves
    boolean IceDispenser

    SetTemperature(int Temp)
    SelectIce(string IceType)

Da es sich um eine Kühlschrankklasse handelt, wollen wir nun die Farbe des Geräts festlegen:

Refrigerator.Color = "green"

Nun haben wir einen grünen Kühlschrank, richtig? Falsch! Denken Sie daran, dass es sich bei unserer Klasse lediglich um die Blaupause eines Kühlschranks handelt – Sie können eine Blaupause nicht grün machen. (Das wäre dann eine Grünpause und einfach nicht richtig.) Zunächst müssen wir ein tatsächliches Objekt, also in diesem Fall einen Kühlschrank erstellen:

Set objFridge = CreateObject("Refrigerator")

Objekte werden stets mit der Anweisung Set erstellt. Manchmal verwenden wir CreateObject, manchmal GetObject, manchmal andere Dinge, jedoch immer die Anweisung Set. Warum machen wir das nicht einfach?

objFridge = CreateObject("Refrigerator")

Weil Objekte so nicht funktionieren. Es bedarf größerer Anstrengungen als nur einem Gleichheitszeichen, um aus einer Klasse ein Objekt zu erstellen: Sie benötigen Set.

Erweitert: Natürlich gibt es da noch andere Punkte. Dies hat mit Speicheradressen zu tun. Wir verschieben nicht nur einen Wert in eine Variable, wir verweisen auch auf einen Speicherort im Speicher. Daher wird Ihnen auch der Begriff Objektverweis häufig begegnen. Die Variable (z. B. objFridge) beinhaltet kein wirkliches Objekt, sondern einen Verweis auf ein im Speicher abgelegtes Objekt. Ganz ehrlich, Sie können ein Objekt ohne die Anweisung Set erstellen, Sie können jedoch den Objektverweis keiner Variable zuweisen und diesen Objektverweis nirgendwo anders einsetzen, wenn in der Zuweisungsanweisung nicht Set verwendet wurde.

Jetzt wo wir über ein Objekt verfügen, können wir andere Dinge unternehmen, z. B. die Temperatur einstellen, die Farbe ändern und Abfragen, ob ein Eisspender vorhanden ist oder nicht:

Set objFridge = CreateObject("Refrigerator")
objFridge.SetTemperature(43) ' Degrees Fahrenheit
objFridge.Color = "green"
Wscript.Echo objFridge.IceDispenser

Sie mögen sich Fragen, warum wir all dies durchgehen mussten. Warum können wir nicht einfach die Klasse verwenden, ohne ein Objekt zu erstellen? Es handelt sich eben nicht um einen realen, physischen Kühlschrank, sondern es spielt sich letztlich alles auf einem Computer ab. Was passiert nun, wenn wir uns über ein Restaurant mit zwei Kühlschränken Gedanken machen? Wie können wir die jeweiligen Features unterscheiden? Mithilfe von Objekten ist dies einfach: Erstellen Sie basierend auf der Kühlschrankklasse zwei Objekte. Nehmen wir an, Kühlschrank 1 soll kälter als Kühlschrank 2 sein. Wir verwenden einfach die Kühlschrankklasse, um zwei Objekte zu erstellen:

Set objFridge1 = CreateObject("Refrigerator")
Set objFridge2 = CreateObject("Refrigerator")

objFridge1.SetTemperature(39)
objFridge2.SetTemperature(43)

Ganz einfach.

Weitere Informationen

Der COM-Prozess – Microsoft Windows 2000 – Scripting-Handbuch

Eine Objektreferenz erstellen – Microsoft Windows 2000 – Scripting-Handbuch

Bindung

Wie geht es Ihnen? Bleiben Sie am Ball? Brauchen Sie eine Pause und einen Schluck Wasser? Kein Problem, wir warten.

Wieder an Bord? Nun wird es etwas komplizierter, also bereiten Sie sich darauf vor.

Wir können nicht über Objekte reden, ohne über die Bindung zu sprechen. In vielen Artikeln im Script Center finden Sie häufig solche Sätze: "Wir beginnen damit, das Benutzerkonto in Active Directory zu binden" oder: "Wir beginnen damit, eine Verbindung zum WMI-Dienst herzustellen". Okay, im zweiten Beispiel wurde von "Verbinden" anstelle von "Bindung" gesprochen, es handelt sich jedoch um dieselbe Sache. (Nein, wir sind nicht einfach inkonsistent, es geschieht zu Ihrem Besten. Wir möchten Sie lediglich mit den verschiedenen Terminologievariationen vertraut machen. Wirklich.) Ihnen mag auch der Begriff "Instanziieren" begegnen. Es handelt sich letztlich um ein und dieselbe Sache.

Was bedeutet es nun, etwas zu binden (oder zu verbinden oder zu instanziieren)? Im Kern bedeuten diese Begriffe lediglich, dass ein Objekt erstellt wird. Sie binden an Active Directory oder WMI oder ADO oder ein beliebiges anderes Objekt, indem Sie je nach Objekttyp und dessen Herkunft entweder CreateObject oder GetObject aufrufen. Wir werden hier nicht ausführlich darauf eingehen, wann welche der beiden Anweisungen verwendet wird. Am Besten fangen Sie damit an, nach einem Beispiel zu suchen, das das gewünschte Objekt verwendet, und es einfach zu stehlen. (Darum geht es letztlich bei der Script Repository. Nehmen Sie sich einfach, was immer Sie möchten.)

Weitere Informationen

Bindung – Microsoft Windows 2000 – Scripting-Handbuch

Mit Objekten verbinden – Microsoft Windows 2000 – Scripting-Handbuch

Freigeben von Objekten

Viele geraten beim Freigeben von Objekten etwas durcheinander. Ein Objekt wird folgendermaßen freigegeben:

Set objFridge = Nothing

Wenn Sie ein Objekt auf Nothing setzen, können Sie es in Ihrem Skript nicht mehr weiter verwenden. Viele meinen, man müsse dies zum Abschluss des Skripts mit allen Objekten durchführen. Wir verraten Ihnen ein kleines Geheimnis: Das ist nicht erforderlich. Dabei handelt es sich nur um zusätzliche Tipperei, und die Scripting Guys sind stets gegen zusätzliche Tipperei.

Warum denken so viele Leute, dass dies notwendig sei? Geschichte. Hier ist die Story:

Beim Erstellen eines Objekts reserviert Ihr Computer für das Objekt etwas Platz im Speicher. Wenn Sie also objFridge erstellen, gibt es irgendwo auf Ihrem Computer einen Ort, der sagt: "Hey, ich bin jetzt objFridge!". In vielen Programmiersprachen (z. B. C und C++) werden Programme beendet, und anschließend ist dieser Teil des Speichers immer noch irgendwo da draußen auf dem Computer und sagt: "Ich bin objFridge.". Wenn Sie das Programm erneut ausführen, sagt nun ein anderer Teil des Speichers: "Hier bin ich, ich bin objFridge!". Nach mehrfachem Ausführen des Programms haben Sie die verschiedensten Speicherplätze, die alle behaupten, sie seien objFridge, jedoch nichts Sinnvolles tun, nur herumsitzen, sich wichtig vorkommen und sich einbilden, gebraucht zu werden. Da diese Speicherplätze davon ausgehen, wichtig zu sein, können Sie für nichts anderes verwendet werden. Wenn Sie das Programm neu starten, können Sie all die bereits vorhandenen objFridges nicht verwenden. Sie denken alle, sie seien beschäftigt, daher muss das Programm jedes Mal ein neues erstellen.

Sie können sich vermutlich vorstellen, was mit der Leistung Ihres Computers geschieht, nachdem Sie ein paar Tausend dieser objFridges erstellt haben. Sie verbrauchen Ihren gesamten Speicher, sodass dieser von niemand anderem mehr verwendet werden kann. Aus diesem Grund räumen Sie am Ende des Programms stets sämtliche Objekte auf: Sie setzen Sie auf Nothing und geben den Speicher für die Verwendung durch andere Benutzer frei.

Nun haben wir also unseren Speicher vermenschlicht.

Warum aber behaupten wir, dass dieses Aufräumen nicht nötig sei? Da VBScript (anders als andere Sprachen) hinter sich aufräumt. Nach Abschluss des Skripts wird der gesamte verwendete Speicher automatisch freigegeben. Wenn Sie also Ihr Objekt am Ende auf Nothing setzen, fügen Sie lediglich einen Schritt ein, den VBScript ohnehin durchführt. Warum sollten Sie also das alles tippen?

Wenn Sie jedoch ein äußerst langes und kompliziertes Skript erstellen, das über einen großen Zeitraum viele Verarbeitungen oder Ausführungen durchläuft, kann es sinnvoll sein, einige der nicht mehr verwendeten Objekte freizugeben, damit Sie den Speicher für etwas anderes verwenden können. Bei kurzen Skripts, die lediglich einige wenige Aufgaben erledigen, brauchen Sie sich darüber keine Gedanken zu machen.

Ein weiterer Punkt muss im Zusammenhang mit der Objektfreigabe noch erwähnt werden. Wie bereits erwähnt, kann ein auf Nothing gesetztes Objekt im weiteren Verlauf des Skripts nicht mehr verwendet werden. Wenn das Objekt jedoch eine Instanz einer Anwendung erstellt hat, wird diese Anwendung weiter ausgeführt. Selbst das Beenden des Skripts hilft hier nicht weiter. Kein Speicher verfolgt sie nach, doch die Anwendung wird nach wie vor ausgeführt und verwendet ihrerseits Speicher. So erstellt beispielsweise folgende Codezeile eine Instanz von Microsoft Excel:

Set objExcel = CreateObject("Excel.Application")

Wenn Sie diese Codezeile in Ihrem Skript ausführen, wird Excel gestartet. Die folgende Maßnahme beendet jedoch Excel nicht:

Set objExcel = Nothing

Wenn Sie objExcel auf Nothing setzen, bedeutet dies, dass Sie in Ihrem Skript nicht mehr auf diese Instanz von Excel verweisen können, Excel wird jedoch weiter ausgeführt. Für diesen Objekttyp müssen Sie Excel entweder mithilfe der Eigenschaft Visible sichtbar machen, damit der Benutzer es manuell beenden kann, oder Sie müssen Excel über das Skript schließen:

Sichtbar machen von Excel:

objExcel.Visible = True

Schließen von Excel:

objExcel.Quit

Zusammenfassend bedeutet dies, dass auf Nothing gesetzte Objekte ausgeführte Anwendungsobjekte nicht beenden. Auch wird nichts bewirkt, was VBScript nicht ohnehin umsetzt, es sei denn, Sie haben ein über einen großen Zeitraum ausgeführtes, speicherintensives Skript. (So, jetzt dürfen Sie durchatmen.)

Weitere Informationen

When Are You Required To Set Objects To Nothing? (in englischer Sprache) – Eric Lippert’s Blog

Objekte aus dem Speicher entfernen – Microsoft Windows 2000 – Scripting-Handbuch

Die wirkliche Welt

Lassen wir die Kühlschränke und Baupläne, betrachten wir stattdessen ein tatsächliches Skript. Werfen wir einen Blick auf die Klasse FileSystemObject. Die Klasse FileSystemObject definiert verschiedene Methoden und Eigenschaften, die das Erstellen eines Objekts ermöglichen, das auf das Dateisystem Ihres Computers zugreift. (Beachten Sie, dass dies nur für lokale, nicht jedoch für Remotecomputer möglich ist. Darüber wollen wir heute jedoch nicht sprechen, machen wir also weiter.)

Auf MSDN stellen Sie in der Dokumentation (https://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/jsobjfilesystem.asp, in englischer Sprache) fest, dass die Klasse FileSystemObject mehrere Methoden und eine Eigenschaft definiert. Hier ist ein Skript, das das Objekt FileSystemObject erstellt und anschließend überprüft, ob ein bestimmter Ordner vorhanden ist:

Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FolderExists("C:\Scripts") Then
    Wscript.Echo "Folder exists."
Else
    Wscript.Echo "Folder does not exist."
End If

Wie Sie sehen, erstellen wir zunächst aus der Klasse FileSystemObject ein FileSystemObject-Objekt. Dieses Objekt nennen wir objFSO. Bei objFSO handelt es sich also um das eigentliche Objekt, mit dem wir arbeiten können, oder, etwas technischer: objFSO ist ein Objektverweis zu der Instanz der Klasse FileSystemObject, mit der wir arbeiten (verstehen Sie nun, wo Instanziieren herkommt?).

Hinweis: Häufig beziehen wir uns lediglich auf "das FileSystemObject". Das liegt daran, dass "das FileSystemObject-Objekt" redundant und etwas unsinnig klingt. Das ist jedenfalls unsere Meinung.

Anschließend rufen wir eine der für sämtliche aus der Klasse FileSystemObject erstellten Objekte verfügbaren Methoden auf, in diesem Fall die Methode FolderExists. Wir übergeben dieser Methode einen Parameter, den Pfad zu dem Ordner, dessen Vorhandensein wir überprüfen möchten:

objFSO.FolderExists("C:\Scripts")

Zu den Methoden sollte gesagt werden, dass viele nicht nur über Parameter verfügen können, sondern auch Werte zurückgeben können. Wir hätten Folgendes umsetzen können:

IsAFolder = objFSO.FolderExists("C:\Scripts")

Dadurch würden wir in der Anweisung If die Variable IsAFolder verwenden:

If IsAFolder Then

Wir haben jedoch eine Verknüpfung verwendet und den Methodenaufruf innerhalb der Anweisung If platziert. Bei diesem Verknüpfungstyp handelt es sich um eine Standardpraxis beim Verwenden einer Methode, die einen Booleschen (wahr/falsch) Wert zurückgibt (wie bei der Methode FolderExists der Fall).

Anschließend geben wir eine Anweisung aus, die angibt, ob die Methode FolderExists den Ordner gefunden hat oder nicht.

Weitere Informationen

Mehre Aktionen mit If Then Else durchführen – Microsoft Windows 2000-Scripting – Handbuch

If…Then…Else Statement (in englischer Sprache) – VBScript Language Reference

FolderExists Method (in englischer Sprache) – Scripting Runtime Library

FileSystemObject Basics (in englischer Sprache) – Scripting Runtime Library

Happy Hour

Das war's. Gehen Sie raus, und feiern Sie. Sie gehören nicht mehr länger zu den armen, verwirrten Wesen, deren Hirne bei der Erwähnung von Klassen und Objekten zu rotieren beginnen. Das hoffen wir zumindest. Wenn doch, nun… dann gratulieren wir Ihnen wenigstens dazu, bis zum Ende des Artikels ausgehalten zu haben. Gehen Sie raus und feiern Sie trotzdem.