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

Hilfreiche Statische Methoden

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 

 

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)
[…]

 

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