Zum Hauptinhalt springen

Com-Objekte

Das COM (Component Objects Model) ist eine ziemlich alte Programmierschnittstelle, die Microsoft bereits in den 90er-Jahren eingeführt hat. COM sollte vom .NET-Framework abgelöst werden, ist aber so tief in Windows verwurzelt, dass es immer noch in vielen Anwendungen wie z.B. Microsoft Office steckt. Inzwischen hat es ein Revival erlebt, denn eine modernisierte Variante von COM steckt im neuen App-Konzept, das Microsoft mit Windows 8 eingeführt hat.

COM stellt unter Visual Basic Script die Objekte bereit, die wir in Powershell normalerweise vom .NET-Framework bekommen. Da Powershell aber universell einsetzbar ist, kann man auch von hier aus auf COM-Objekte und deren Eigenschaften und Methoden zugreifen. Dafür muss man nichts weiter tun, als New-Object mit Hilfe des Parameters -ComObject zu sagen, dass man ein COM-Objekt haben möchte.

$update = New-object –comobject "Microsoft.Update.Autoupdate"
$Update.results
$update.DetectNow()

Danach kann man mit den COM-Objekten genauso arbeiten, wie man es in Powershell von Objekten gewohnt ist. Mit Get-Member können Sie die COM-Objekte auch untersuchen.

$update = New-object -comobject "Microsoft.Update.Autoupdate"
$update | get-member

Tatsächlich braucht man COM kaum noch. Das obige Beispiel können Sie verwenden, um den Windows-Updatedienst zu steuern. Allerdings gibt es in der Powershell-Gallery das sehr gute Modul PSWindowsUpdate, dass die ganze Funktionalität bereits in Powershell-Cmdlets verpackt hat. Im Hintergrund ruft das Modul genau die besagte COM-Schnittstelle auf, aber Sie müssen sich damit nicht mehr herumärgern. Ein anderer Anwendungszweck ist Microsoft Office, dass sich nach wie vor über COM verscripten lässt. Allerdings gibt es auch hier einfachere Möglichkeiten, wie z.B. die Module ImportExcel, das entgegen dem Namen nicht nur Excel-Dateien importieren sondern auch echte Worksheets exportieren kann, oder das Modul PSWriteWord zum Erstellen von Word-Dateien. Vorteil beider Varianten ist, dass man kein installiertes Office auf dem Rechner braucht, was für COM Pflicht ist. Falls Sie aber doch ein Word-Dokument mit Powershell erstellen möchten, hier ein sehr kurzer Beispielcode.

$Word = New-Object -ComObject Word.Application 
$Word.Visible = $True # Word in den Vordergrund holen
$Document = $Word.Documents.Add() # Ein neues Dokument erstellen
$Selection = $Word.Selection # Aktuelles Fenster selektieren
$Selection.TypeText("Hello") # Den Text "Hello" einfügen
$Selection.TypeParagraph() # Einen neuen Absatz einfügen 
$Document.SaveAs("$env:TEMP\demo.docx")  # Das Dokument speichern

Statische Eigenschaften und Methoden

Objektmethoden können nur mit den Objekten verwendet werden, zui denen sie gehören. Nehmen Sie als Beispiel einen String. Die Stringmethode Split() teilt einen String in Substrings auf.

$Env:PSModulePath.Split(';')

> C:\Users\Admin\Documents\WindowsPowerShell\Modules 
> C:\Program Files\WindowsPowerShell\Modules 
> C:\Windows\system32\WindowsPowerShell\v1.0\Modules

Split() verarbeitet den Inhalt des Strings, der in der Umgebungsvariablen $Env:PSModulePath gespeichert ist, kann aber ausschließlich auf die String-Instanz von $Env:PSModulePath angewendet werden – es ist nicht möglich, mit $Env:PSModulePath.Split() einen anderen String zu bearbeiten als den, der in $Env:PSModulePath gespeichert ist.

Oder nehmen Sie einen Datumstyp:

$Datum = Get-Date
$Datum.AddHours(10)

Die Methode AddHours() des Objekts $Datum kann immer nur Stunden zu dem DateTime addieren, dass in $Datum gespeichert ist.

Es gibt aber auch .NET-Methoden, die beliebige Objekte verarbeiten können. Da in der Objektorientierten Programmierung alles in Klassen verpackt werden muss, werden solche universellen Methoden in Klassen gespeichert und als statische Methoden bezeichnet. Anders gesagt sind statische Methoden Programme, die in Objektortientierten Sprachen wie c# aber immer in einer Klasse gespeichert werden. Zusätzlich zu statischen Methoden gibt es auch statische Eigenschaften. Statische Eigenschaften werden oft dazu verwendet, um Konstante Werte zur Verfügung zu stellen.

Einige sehr praktische Beispiele befinden sich in der [System.Math]-Klasse:

[System.math]::Pi               # die Zahl Pi
> 3,14159265358979
[System.math]::Round(10.22456)  # Runden
> 10
[System.math]::SQRT(441)        # Quadratwurzel (Square-root)
> 21

Die Math-Klasse stellt eine ganze Reihe von mathematischen Methoden und zwei Konstanten (Pi und die Eulersche Zahl e) (im Powershell 7 zusätzlich tau) zur Verfügung. Im Gegensatz zu Objekten kann man die Methoden verwenden, um beliebige Werte zu berechnen. Um Powershell zu sagen, dass man auf Methoden in der Klasse zugreifen möchte, nicht auf die Methoden der Klasse, wird die etwas eigenwillige Syntax :: verwendet. 

Der Zugriff auf statische Eigenschaften und Methoden findet mit zwei Doppelpunkten statt, weil jede einzelne Klassen ebenfalls als Objekte repräsentiert ist. Mit einem Punkt greift man auf die Meta-Informationen der jeweiligen Klasse zu. Wenn Sie z.B. wissen wollen, in welcher Datei der Code für die Math-Klasse versteckt ist, können Sie die Eigenschaft Assembly von [System.math] abfragen:

[System.math].assembly

> GAC   Version     Location
> ---   -------     --------
> True  v4.0.30319  C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscorl...

Um alle statischen Methoden eines Typs anzuzeigen, können Sie Get-Member mit dem Parameter static verwenden:

[System.math] | get-member -Static

>   TypeName: System.Math

> Name   MemberType   Definition
> ----   ----------   ----------
> Abs    Method       static sbyte Abs(sbyte value), static int16 Abs(int16 value), static... 
> Acos   Method       static double Acos(double d)
> Asin   Method       static double Asin(double d)
> Atan   Method       static double Atan(double d)
[…]

 

Hilfreiche statische Methoden

Hier noch ein paar Beispiele für hilfreiche statische Methoden

Umgebungsvariablen global setzen mit System.Environment

Wenn Sie in Powershell Umgebungsvariablen setzen wollen, können Sie das über das Powershell-Laufwerk ENV: machen. Alle Änderungen, die an diesem Laufwerk durchgeführt werden, beziehen sich aber nur auf den Powershell-Prozess und sind nach dem Schließen der Powershell wieder verschwunden. Mit der statischen Methode SetEnvironmentVariable() aus der System.Environment-Klasse können die Umgebungsvariablen aber auch Systemweit gesetzt werden:

[System.Environment]::SetEnvironmentVariable('Test','Computervariable','Machine') 
[System.Environment]::SetEnvironmentVariable('Demo','Uservariable','User')

Der erste Aufruf erstellt eine Umgebungsvariable "Test" mit dem Inhalt "Computervariable" auf Computerebene, also für alle Benutzer, während der zweite Aufruf eine Benutzervariable mit dem Namen "Demo" und dem Inhalt "Uservariable" erzeugt.

Da sich statische Methoden bis auf die beiden Doppelpunkte beim Aufruf wie Methoden verhalten, kön-nen Sie sich die Überladungen durch Aufrufen der Methode ohne die runden Klammern anzeigen lassen:

[system.Environment]::SetEnvironmentvariable

> OverloadDefinitions
-----------------------
> static void SetEnvironmentVariable(string variable, string value) 
> static void SetEnvironmentVariable(string variable, string value, System.EnvironmentVariableTarget target)

Die Klasse System.Environment hat aber noch mehr spannende Eigenschaften und Methoden:

[system.Environment] | get-member -Static

>  TypeName: System.Environment

> Name                       MemberType Definition
> ----                       ---------- ----------
> ExpandEnvironmentVariables Method     static string expandEnvironmentVariables(string name)
> GetEnvironmentVariables    Method     static System.Collections.IDictionary
> GetLogicalDrives           Method     static string[] GetLogicalDrives()
> SetEnvironmentVariable     Method     static void SetEnvironmentVariable(string variable, strin...
> CommandLine                Property   static string CommandLine {get;}
> CurrentDirectory           Property   static string CurrentDirectory {get;set;}
> CurrentManagedThreadId     Property   static int CurrentManagedThreadId {get;}
[...]

 (Die Ausgabe wurde zusammengefasst)

Daten konvertieren mit System.Convert

Die Convert-Klasse stellt eine Reihe von Methoden zur Verfügung, um Datentypen zu konvertieren.

[System.Convert]::ToSTring(42562,16)   # Konvertiert ins Hex-System
> a642
[System.Convert]::ToSTring(42562,2)    # Konvertiert ins Binär-System
> 1010011001000010
[System.Convert]::ToSTring(42562,8)    # Konvertiert ins Oktal-System
> 123102 

Das erste Beispiel konvertiert die Zahl 42562 ins Hexadezimal (16er)-Zahlensystem, das zweite Beispiel konvertiert ins Binär-System (2er), und das dritte Beispiel konvertiert ins Oktal-System (8er).

Textbasierte Übertragungsprotkolle wieSMTP, das Mail-Protokoll, können keine Binärdaten übertragen, weil manche der Bitfolgen in Binärdaten in Textdaten als Steuerzeichen interpretiert werden. Um zu verstehen, warum das problematisch ist, brauchen Sie nur eine ausführe Datei im Notepad aufzumachen. Das Problem ist letzlich, dass man Binärdaten nicht als Text darstellen und hinterher wieder fehlerfrei in Binärdaten zurück konvertierten kann. Um dieses Problem zu lösen, hat man das Base64-Format entwickelt, bei dem die Bits einer Binärdatei in ausschließlich darstellbare Zeichen kodiert werden. Die darstellbaren Zeichen können dann problemlos übertragen und beim Empfänger zurück kodiert werden. Für die Konvertierung in Base64 gibt es ebenfalls eine Methode in der Convert-Klasse. 

$img = $env:windir\Web\Wallpaper\Windows\img0.jpg

$Bin=[System.Convert]::ToBase64String((Get-Content -Path $Img -Raw -Encoding Byte))

Mit der Methode FromBase64String() wandeln Sie den String wieder in Binärcode um, und mit Set-Content können Sie die Binärdaten wieder als Datei speichern:

$Binary = [Convert]::FromBase64String( $Bin )
Set-Content -Path c.\image.jpg -Value $Binary -Encoding Byte
[System.Convert]::ToSTring(42562,16)   # Konvertiert ins Hex-System
a642
[System.Convert]::ToSTring(42562,2)    # Konvertiert ins Binär-System
1010011001000010
[System.Convert]::ToSTring(42562,8)    # Konvertiert ins Oktal-System
123102 

 

Typen und Assemblys laden

Eine ganze Reihe von Klassen werden von Powershell beim Starten automatisch geladen.Manche müssen aber erst geladen werden, bevor man sie verwenden kann, da Powershell nur häufig verwendete Typen bereitstellt. Die einfache Ein-Button-GUI z.B. läuft nicht, wenn sie direkt aus der Powershell aufgerufen wird. Das liegt daran, dass die Windows-Forms-Erweiterungen von Powershell nicht automatisch geladen werden – das erledigt erst die ISE, die die Forms-Bibliothek selbst verwendet. Sie können in Ihren Skripten Typen mit dem Cmdlet Add-Type aber selbst nachladen. Für das Forms-Skript fügen Sie am in der ersten Zeile folgende Code-Zeile hinzu:

Add-Type -AssemblyName System.Windows.Forms

Eine Assembly ist eine Datei, die .net Code enthält, der beim Kompilieren im .net Compiler nicht automatisch zu einem ausführen Programm gemacht wird. Sie werden als Bibliotheken bei Bedarf nachgeladen und kommen Form von DLLs (Dynamic Link Library). Viele Klassen können aus dem GAC (Global Assembly Cache) direkt nachgeladen werden. Manchmal bekommen Sie aber auch Assemblys in Form von .dll-Dateien, die Sie direkt aus dem Dateisystem nachladen müssen. Dann geben Sie mit dem Parameter -Path von Add-Type den Pfad zur Datei an, die geladen werden soll. Hier am Beispiel WinScp, einer .NET-Bibliothek zum Übertragen von Dateien per FTP oder SFTP:

Add-Type -Path 'C:\Assemblies\WinSCPnet.dll'

Der Fehlercode 0x80131515 beim Laden ist eine Sicherheitsfunktion, die verhindert, dass DLLs direkt aus einer Freigabe oder aus dem Internet geladen werden. Sollte das Laden bei Ihnen fehlschlagen, stellen Sie sicher, dass die Datei nicht als "nicht vertrauenswürdig" markiert ist.

Image 110

Sie können das "Mark of the Web" auch mit Powershell entfernen:

Unblock-File -Path C:\Assemblies\WinSCPnet.dll

Wenn Sie tiefer in das Thema Assemblies einsteigen möchten, lesen Sie Assemblies in .NET.

Konstruktoren

Das Datetime-Objekt, das im obigen Beispiel erzeugt wurde, hatte als Startwert den 01 Januar des Jahres 01. Man kann ein neues Datetime-Objekt aber auch mit einem bestimmten Datums- und Zeitwert erzeugen. Hierfür muss man das neue Objekt initialisieren. Typen stellen hierfür Konstruktoren (abgekürzt .ctor) zur Verfügung. Konstruktoren sind nichts anderes als Methoden, die Parameter verwenden, um die Eigenschaften für das neue Objekt zu konfigurieren. Da Konstruktoren ausschließlich dafür da sind, neue Objekte zu initialisieren, verfügen nur Typen über Konstruktoren – an Objekten werden Sie Konstruktoren vergeblich suchen.

Die Parameter für den Konstruktor können Sie beim Erstellen eines Objektes mit New-Object über den Parameter -Argumentlist übergeben. Denken Sie daran – ein Konstruktor ist eine Methode, es werden also nur die Argumente übergeben. Um ein Datumobjekt mit einem definierten Datum zu erstellen, gehen Sie so vor:

New-Object -TypeName DateTime -ArgumentList 2020,05,25
> Montag, 25. Mai 2020 00:00:00

In Beispiel wird das Jahr, der Monat und der Tag übergeben. Sie können das Datetime-Objekt aber auch mit Ticks (1 Tick = 1/10.000.000 Sekunde) erstellen:

New-Object -TypeName DateTime -ArgumentList 637259616000000000
> Montag, 25. Mai 2020 00:00:00

Da es sich beim Konstruktor letztlich nur um eine Methode handelt, können Sie die Argumente auch direkt mit dem Typ übergeben:

New-Object -TypeName DateTime(637259616000000000)

 Achten Sie darauf, dass viele Typen ausschließlich Konstruktoren mit Pflicht-Parametern haben. Wenn Sie Beispielsweise versuchen, ein Powershell-Credential-Objekt zu erzeugen, ohne Argumente für den Konstruktor zu übergeben, bekommen Sie eine Fehlermeldung:

New-Object -TypeName PSCredential
> New-Object : Kein Konstruktor gefunden. Es kann kein geeigneter Konstruktor für den Typ "PSCredential" gefunden werden.
>   In Zeile:1 Zeichen:1

Diese inzwischen gar nicht mehr so kryptische Fehlermeldung besagt, dass es keinen Konstruktor gibt, der ohne Argument ein Objekt erzeugen kann. Sobald Sie alle notwendigen Argumente zum Erzeugen eines Konstruktors übergeben, erzeugt Ihnen Powershell das PSCredential-Objekt:

$Cred = ConvertTo-SecureString -String "Password" -AsPlainText -Force New-Object -TypeName PSCredential -ArgumentList ('admin',$Cred)

Das PSCredential-Objekt befindet sich übrigens eigentlich im Namespace System.Management.Automation, der zu Powershell gehört und ebenfalls nicht angegeben werden muss.

Um herauszufinden, welche Konstruktoren existieren, stellt Ihnen jeder Typ eine Method GetConstructors() bereit.

[PSCredential].GetConstructors()

Jeder Konstruktor ist eine eigene Methode mit eigenen Parametern. Sie können die Parameter jedes Konstruktors mit folgendem Skript ausgeben. Die Darstellung der Konstruktoren ähnelt den Überladungen einer Methode:

[PSCredential].GetConstructors() | 
    ForEach-Object { ($_.GetParameters() |
    ForEach-object { $_.ToString()} ) -join ', ' }

> System.String userName, System.Security.SecureString password 
> System.Management.Automation.PSObject pso

Achten Sie darauf, dass der zweite Foreach-Object hier innerhalb des ersten Foreach-Object ausgeführt wird. Der erste Foreach-Object nimmt einen Konstruktor aus der Pipeline und ruft alle Parameter mit der Methode GetParameters() ab. Die Parameter werden dann in einer Pipeline innerhalb der ersten Pipeline weiterverarbeitet, die die Parameter für jedes Konstruktor in Strings konvertiert und mit einem ', ' zusammenfasst.

Am einfachsten können Sie sich die Konstruktoren anzeigen lassen, indem Sie auf die New()-Methode eines Typen zugreifen. New() erzeugt ein neues Objekt und stellt Überladungen (in Powershell würde man Parametersätze sagen) für jeden Konstruktor bereit. Wie bei Methoden üblich, gibt Powershell Ihnen alle Überladungen der Methode zurück.

[PSCredential]::new
> OverloadDefinitions
>     pscredential new(string userName, securestring password) 
>     pscredential new(psobject pso)
 
[PSCredential].GetConstructors() | ForEach-Object { ($_.GetParameters() |
ForEach-object { $_.ToString()} ) -join ', '
}

System.String userName, System.Security.SecureString password System.Management.Automation.PSObject pso

Objekte in Powershell erstellen

Powershell basiert auf dem .NET-Framework, genau wie C# und einige andere Programmiersprachen von Microsoft. Durch diese Verwandtschaft kann Powershell im Prinzip auf alle Funktionen des .NET-Frameworks zurückgreifen, was eine schier unerschöpfliche Menge an zusätzlichen Funktionalitäten zur Verfügung stellt, die man sehr einfach anzapfen kann.

Ein Framework besteht aus einer Reihe von Komponenten, die zu einem Produkt zusammengeschnürt sind. Im Falle des .NET-Framework sind diese Komponenten im Wesentlichen die CLR oder Common Language Runtime, die für die Ausführung von Programmen zuständig ist, und der für Powershell-Skripter wesentlich interessantere Teil, die Klassenbibliothek. Die Klassenbibliothek stellt eine riesige Menge an Klassen zur Verfügung, die im .NET-Framework direkt verwendet werden können.

Klassen, oder allgemeiner Typen (der Unterschied ist in Powershell nicht relevant), sind Vorlagen für Objekte. Sie können keine Objekte erstellen, ohne eine Vorlage dafür zu haben. Für gänzliche neue Ob-jekte müssen Sie also immer erst einen Typ erstellen, bevor Sie ein neues Objekt darauf aufbauen kön-nen. Mehr zur Objekten und wie Sie neue Objekte erstellen können finden Sie auch im Abschnitt Varia-blen und Objekte. Wenn Sie von einem Typ ein neues Objekt ableiten, spricht man auch von instanziie-ren, das neue Objekt ist eine neue Instanz.

Wie bereits im Abschnitt Variablen und Objekte besprochen, können Sie ein neues Objekt mit dem Cmdlet New-Object erzeugen. Sie müssen hierfür nur wissen wie die Vorlage Ihres Objekts heißt.

Die Vorlage des Objekts (der Typ-Name oder die Klasse) heißt in diesem Fall also system.Datetime und stellt alle Informationen bereit, um ein neues Datums-Objekt zu erzeugen. Die Erstellung eines Objekts aus einer Vorlage wird auch als instanzieren bezeichnet, das Objekt selbst als Instanz. 

Der Typ-Name setzt sich aus dem Namen des Typs und dem "Namespace" zusammen, also dem Bezeichner vor dem Typ-Namen. Im Falle des Datetime-Typs ist das system. Ein Namespace ist einfach ein "Ordner", in dem sich der Typ befindet. Namespaces haben nur einen Zweck – sicher zu stellen, dass neue erstellte Klassen immer eindeutige Namen haben. Namespaces werden z.B. auch im Domain Name System (DNS) oder im Dateisystem verwendet. Die Ordner, in denen sich eine Datei befindet, stellen den Namespace dar. Auf diese Weise kann es in Windows z.B. zwei Dateien Notepad.exe geben: C:\Windows\Notepad.exe und C:\Windows\System32\Notepad.exe.

Wenn ein Programmierer einen neuen Datentyp bzw. eine Klasse erstellen will, braucht er nur einen neuen Namespace ("Ordner") zu erstellen und kann dann sicher sein, dass alle Typ-Namen, die er hier ablegt, eindeutig sind. Um ein neues Objekt zu erstellen, muss man immer den vollqualifizierten Namen der Klasse angeben.Genau wie im Dateisystem gibt es aber auch Namespaces, die sich im "Pfad" befinden. Typen, die hier liegen, können direkt über Ihren Namen referenziert werden.

New-Object -TypeName system.Datetime
> Montag, 1. Januar 0001 00:00:00

New-Object -Typename Datetime

Datetime kann auch direkt referenziert werden, der System-Namespace (in dem sich alle Basis-Typen wie String, Datetime, Int usw. befinden) ist also "im Pfad". In einem Typ sind alle Eigenschaften, Methoden und Ereignisse definiert, die eine neue Instanz eines Objekts nach dem Instanziieren besitzt.

$Datum = New-Object -TypeName Datetime

Eigenschaften sind Werte, die direkt abgefragt werden können. Sie "beschreiben" das Objekt. Um eine Eigenschaft abzufragen, schreibt man hinter das Objekt einen Punkt und dann den Eigenschaft-Namen.

$Datum.Year
> 1

Methoden sind Programme, die zum Objekt gehören. Methoden lesen meist Eigenschaften des Objekts aus, verarbeiten diese und geben den verarbeiteten Wert aus, ohne das eigentliche Objekt zu verändern. Methoden-Parameter werden in runden Klammern hinter dem Methoden-Namen angegeben. Im Gegensatz zu Powershell haben diese aber eine feste Reihenfolge, man muss also bei der Übergabe der Argumente die Reihenfolge beibehalten. Eine Methode wird wie eine Eigenschaft aufgerufen, hat aber immer ein paar runde Klammern hinter dem Methodennamen, über die die Parameter übergeben werden können.

$Datum.AddYears(2019)
> Mittwoch, 1. Januar 2020 00:00:00

Events oder Ereignisse erlauben es, auf Änderungen des Objekts zu reagieren. Events sind sehr üblich bei grafischen Oberflächen, weil es mit ihrer Hilfe möglich ist, auf Interaktionen wie einen Maus-klick auf einen Button automatisch zu reagieren. Ohne ein Event müsste man eine Schleife bauen, die dauerhaft prüft, ob der Button gedrückt wurde. Mit einem Event ist es möglich, Code ausführen zu lassen, wenn das Click-Ereignis eintritt. Da Datums-Objekte über keine Events verfügen, hier ein kleines Skript zum Erstellen einer sehr simplen Windows-Forms GUI.

Add-Type -AssemblyName system.windows.forms
$Form                   = New-Object system.Windows.Forms.Form
$Form.ClientSize        = '248,92'
$Form.text              = "ShutdownGUI"
$btn_Shutdown           = New-Object system.Windows.Forms.Button
$btn_Shutdown.text      = 'Herunterfahren'
$btn_Shutdown.width     = 190
$btn_Shutdown.height    = 42
$btn_Shutdown.location  = New-Object System.Drawing.Point(26,26)
$btn_Shutdown.Font      = 'Microsoft Sans Serif,10'
$btn_Shutdown.Add_Click({ $btn_Shutdown.Text = 'Starten' })
$Form.controls.AddRange(@($btn_Shutdown))
$Form.ShowDialog()

Dieses Skript öffnet bei der Ausführung ein Fenster mit einem Button.

Image 096

Grafische Oberflächen bestehen ausschließlich aus Objekten. Windows Forms ist recht einfach zu benutzen. Sie benötigen ein Formular, in das grafische Elemente eingebettet werden. Die ersten vier Zeilen legen die Eigenschaften des Formular-Objekt fest und konfigurieren die Größe und den Text in der Kopfzeile.

In den folgenden Zeilen wird das Button-Objekt erstellt und konfiguriert. Die Eigenschaft Text beinhaltet dabei den Text, der auf dem Button angezeigt werden soll, und Location definiert, an welcher Position auf dem Formular der Button gezeichnet werden soll. Hierfür wird ein Drawing-Point verwendet.

Der Drawing-Point ist sehr einfaches Objekt, das nur x- und y-Koordinate eines Punktes speichert. Die Größe des Buttons wird über die Eigenschaften Width und Heigth angegeben. Wirklich interessant ist aber diese Zeile: 

$btn_Shutdown.Add_Click({ $btn_Shutdown.Text = 'Starten' })

Hier wird dem Button ein Click-Event hinzugefügt. Add_Click() ist eine Methode (also ein Programm), die beim Auftreten des Click-Events gestartet wird. Als Parameter wird ein Skriptblock übergen, der ausgeführt wird, wenn der Button geklickt wird. Der Skriptblock kann wie üblich beliebigen Powershell-Code enthalten. Im Beispiel wird sicherheitshalber der Text des Buttons angepasst, so dass nach dem Klicken 'Starten' auf dem Button steht – Sie können den Code aber auch einfach durch Stop-Computer ersetzen, und schon fährt Ihr Computer herunter.

Die letzten beiden Zeilen verknüpfen den Button mit dem Formular – ohne diese Zeile sind der Button und das Formular einfach zwei Objekte, die nichts miteinander zu tun haben. Anschließend wird die Methode $Form.Showdialog aufgerufen, die das Formular anzeigt. Ohne diese Zeile wird das Formular zwar im Hintergrund erzeugt, aber Windows zeigt es am Bildschirm nicht an.

Wenn Sie sich selbst eine grafische Oberfläche mit Windows Forms erstellen möchte – dieses kleine Beispiel wurde auf der Website PoshGui.Com erzeugt. Dort gibt es einen Online-Editor, der Ihnen den Code automatisch erzeugt. Eine etwas weitergehende Einführung gibt es außerdem bei Microsoft.learn.

 

 
$Form                   = New-Object system.Windows.Forms.Form
$Form.ClientSize        = '248,92'
$Form.text              = "ShutdownGUI"
$btn_Shutdown           = New-Object system.Windows.Forms.Button
$btn_Shutdown.text      = 'Herunterfahren'
$btn_Shutdown.width     = 190
$btn_Shutdown.height    = 42
$btn_Shutdown.location  = New-Object System.Drawing.Point(26,26)
$btn_Shutdown.Font      = 'Microsoft Sans Serif,10'
$btn_Shutdown.Add_Click({ $btn_Shutdown.Text = 'Starten' })
$Form.controls.AddRange(@($btn_Shutdown))
$Form.ShowDialog()