COM und .NET aus PowerShell aufrufen — Was Ihre Skripte erreichen können, erweitern

· · PowerShell, Windows, COM, .NET, Automatisierung, Skript, Nutzung vorhandener Ressourcen, Betriebliche Effizienz

„Ich habe nach einem Cmdlet gesucht, aber für das, was ich vorhabe, gibt es keines.“ – Nutzen Sie PowerShell eine Weile, stoßen Sie unweigerlich an diese Wand. Verknüpfungen in großer Zahl erstellen, den Inhalt eines ZIP inspizieren, ohne es zu entpacken, Fenster manipulieren, Excel-Dateien lesen und schreiben. Anfragen, die die Standard-Cmdlets nicht erreichen, kommen bei internen IT- und Betriebsmitarbeitern in kleinen und mittleren Unternehmen ständig auf.

Die Sache ist die: Die andere Seite dieser Wand ist PowerShells Heimspiel. Da PowerShell auf .NET aufbaut, können Sie Typen aus der .NET-Klassenbibliothek direkt aufrufen, ganz ohne zusätzliche Installation. Darüber hinaus lässt Add-Type Sie C#-Code oder Win32-APIs (P/Invoke) an Ort und Stelle einbetten, und New-Object -ComObject lässt Sie COM-Objekte wie WScript.Shell und Excel steuern. Im Grunde steht Ihnen fast der gesamte „Windows-Automatisierungs“-Werkzeugkasten zur Verfügung, der früher in VBScript oder VBA geschrieben wurde.

Diese Macht kommt allerdings mit der Pflicht, hinterher aufzuräumen. Insbesondere Excel-COM-Automatisierung ist der Nährboden für das klassische Problem „EXCEL.EXE läuft nach Skriptende immer noch“ – und Microsoft empfiehlt Office-Automatisierung in unbeaufsichtigten Läufen überhaupt nicht. Dieser Artikel legt die praktischen Muster und Fallstricke beim Aufrufen von .NET, bei der Nutzung von Add-Type und bei der Steuerung von COM aus PowerShell heraus dar und schließt damit, wie sich entscheiden lässt, „wie weit treibe ich das Skript, und wann mache ich ein C#-Werkzeug daraus?“

1. Die wichtigsten Punkte vorab

  • PowerShell sitzt auf .NET auf. Windows PowerShell 5.1 baut auf dem .NET Framework auf, PowerShell 7 auf .NET (ehemals .NET Core), und Sie können statische .NET-Methoden direkt aufrufen, etwa [System.IO.Path]::GetFileNameWithoutExtension().1
  • Erzeugen Sie Instanzen mit New-Object oder, ab PowerShell 5.0, mit [Type]::new(). Geben Sie [Type]::new (ohne Klammern) ein, erhalten Sie eine Liste der Konstruktoren, sodass Sie die Argumente beim Schreiben herausfinden können.2
  • Add-Type kompiliert C#-Quellcode an Ort und Stelle und fügt ihn der Sitzung hinzu. Übergeben Sie eine DllImport-Signatur, können Sie per P/Invoke Win32-APIs aufrufen. Der hinzugefügte Typ lässt sich nicht aus der Sitzung entfernen, und ein Typ mit demselben Namen kann nicht neu definiert werden.3
  • COM-Objekte werden mit New-Object -ComObject <ProgId> erzeugt. VBScripts CreateObject("Shell.Application") bildet sich direkt auf New-Object -ComObject Shell.Application ab, sodass Werkzeuge aus der WSH-Ära wie das Erstellen von Verknüpfungen mit WScript.Shell aus PowerShell heraus verfügbar sind.45
  • Die Lebensdauer von COM-Objekten wird durch Referenzzählung verwaltet, und auf .NET-Seite hält der Runtime Callable Wrapper (RCW) diese Referenzen. Solange eine Referenz bestehen bleibt, beenden sich Prozesse wie Excel nicht. Sie können explizit mit Marshal.ReleaseComObject freigeben, doch übermäßiger Einsatz lädt eine andere Art von Unfall ein, weshalb die offizielle Anleitung lautet: „nur, wenn es wirklich nötig ist“.67
  • Microsoft stellt unmissverständlich fest, dass Office-Automatisierung in unbeaufsichtigten Läufen „weder empfohlen noch unterstützt“ wird. Excel über COM aus einem Dienst, aus der Aufgabenplanung oder serverseitig zu steuern, ist eine nicht unterstützte Konfiguration, selbst wenn es scheinbar funktioniert. Ersetzen Sie unbeaufsichtigte Verarbeitung durch Open-XML-Bibliotheken oder CSV-Integration.8
  • Die verfügbaren Typen und Methoden unterscheiden sich zwischen 5.1 und 7. Es gibt Fälle wie den Unterschied bei der String.Split-Überladung, bei denen sich derselbe Code unterschiedlich verhält, und unter 5.1 müssen Sie GAC-Assemblys manchmal explizit mit Add-Type laden.13
  • Wenn sich direkte .NET-Aufrufe zu häufen beginnen, ist das die Grenze, ab der Sie erwägen sollten, daraus ein C#-Werkzeug zu machen. Sobald Add-Types Unfähigkeit, Typen neu zu definieren, oder Verteilungsprobleme zu stören beginnen, nähert sich das Skript seinen Grenzen.

2. PowerShell sitzt auf .NET auf ── [Type]::Method und New-Object

PowerShells Cmdlets sind „für bequeme Betriebsarbeit verpackte Teile der .NET-Klassenbibliothek“. Die unverpackte Funktionalität lässt sich ebenfalls direkt aufrufen – schreiben Sie einfach den Typnamen in eckige Klammern. Statische Methoden nutzen ::; Instanzmethoden und -eigenschaften nutzen ..

# .NET-statische Methoden direkt aufrufen. Kein Cmdlet nötig, keine zusätzliche Installation
[System.IO.Path]::GetFileNameWithoutExtension('C:\data\orders_20260718.csv')  # -> orders_20260718
[System.IO.Path]::Combine('C:\data', 'archive', '2026-07')                    # -> C:\data\archive\2026-07
[System.Math]::Round(123.456, 1)                                              # -> 123.5

# Brauchen Sie eine Instanz, erzeugen Sie sie mit New-Object oder [Type]::new()
$list = [System.Collections.Generic.List[string]]::new()
$list.Add('server01')

# ::new ohne Klammern eingegeben liefert die Liste der Konstruktoren (praktisch, um Argumente zu finden)
[System.IO.StreamWriter]::new

[Type]::new() ist eine mit PowerShell 5.0 hinzugekommene Syntax. Sie ist schneller als New-Object und lässt Sie, wie im obigen Beispiel, die Konstruktorüberladungen an Ort und Stelle prüfen.2 Es gibt allerdings einen Vorbehalt. Von Cmdlets wie Get-Item zurückgegebene Objekte haben mitunter zusätzliche, von PowerShell angehängte Eigenschaften (NoteProperty), sodass ihre Member nicht mit einem über ::new() direkt erzeugten Objekt desselben Typs übereinstimmen müssen.2 Wundern Sie sich, weil „die Eigenschaft, die da war, als ich es über das Cmdlet bekam, fehlt“, denken Sie an diesen Mechanismus.

Hier ein praktisches Rezept. Für ZIP-Arbeit gibt es Compress-Archive und Expand-Archive, doch die darunterliegende ZipArchive-API hat eine Dateigrößengrenze von 2 GB9, und feinere Vorgänge wie „nur den Inhalt auflisten, ohne zu entpacken“ haben überhaupt kein Cmdlet. Hier kommt .NETs ZipFile-Klasse ins Spiel.10

# Unter Windows PowerShell 5.1 müssen Sie die GAC-Assembly explizit laden
# (unter PowerShell 7 werden gebündelte Assemblys automatisch bei Bedarf geladen, diese Zeile ist dort optional)
Add-Type -AssemblyName System.IO.Compression.FileSystem

# Zuerst lesend — den Inhalt des ZIP inspizieren, ohne zu entpacken
$archive = [System.IO.Compression.ZipFile]::OpenRead('C:\deploy\release.zip')
try {
    $archive.Entries | Select-Object FullName, Length, LastWriteTime
}
finally {
    $archive.Dispose()  # sicherstellen, dass das Datei-Handle zurückgegeben wird
}

# Sieht der Inhalt gut aus, entpacken. Existiert am Ziel bereits eine Datei mit demselben Namen,
# wirft diese zweiargumentige Überladung mitten im Entpacken eine Ausnahme (sie überschreibt nicht).
# Vermeiden Sie es, direkt in einen bestehenden Ordner zu entpacken; entpacken Sie stattdessen
# jedes Mal in einen frischen Ordner und wechseln Sie dann um — das ist das sichere Muster
$dest = "C:\apps\web_$(Get-Date -Format yyyyMMddHHmmss)"
[System.IO.Compression.ZipFile]::ExtractToDirectory('C:\deploy\release.zip', $dest)

Auf dem .NET Framework erfordert die Nutzung der Klasse ZipFile einen Verweis auf die Assembly System.IO.Compression.FileSystem, und dasselbe gilt für PowerShell 5.1.10 Unter 7 lädt die gebündelte Assembly automatisch bei Bedarf, sodass es auch ohne Add-Type funktioniert, aber es explizit zu schreiben bedeutet, dass das Skript auf beiden funktioniert – daher ist Explizitheit für gemeinsam genutzte Skripte die sicherere Wahl.3

3. Eigene C#- und Win32-APIs (P/Invoke) mit Add-Type einbetten

Nach „Dinge, die es in .NET gibt“ kommen „Dinge, die es nicht gibt“. Übergeben Sie C#-Quellcode an Add-Type, wird er an Ort und Stelle kompiliert und der Typ Ihrer Sitzung hinzugefügt. Gehen Sie weiter und übergeben Sie eine DllImport-Signatur an -MemberDefinition, können Sie Win32-APIs über P/Invoke aufrufen. Das ist eine legitime Technik, die als Beispiel in der offiziellen Dokumentation erscheint.3

# Mich mit einem Dialog benachrichtigen, wenn ein von Hand gestarteter, lange laufender Job fertig ist
# (ruft MessageBoxW in user32.dll über P/Invoke auf)
$signature = @'
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBoxW(IntPtr hWnd, string text, string caption, uint type);
'@

$native = Add-Type -MemberDefinition $signature -Name 'NativeMethods' `
    -Namespace 'Win32' -PassThru

# Mit MB_ICONINFORMATION (0x40) anzeigen
[void]$native::MessageBoxW([IntPtr]::Zero, 'Die Sicherung ist abgeschlossen.', 'Lange laufender Job', 0x40)

Eine Warnung. Modale Dialoge sind strikt für interaktive Läufe gedacht, bei denen Sie vor dem Desktop sitzen. In einer nicht interaktiven Sitzung, etwa einem unbeaufsichtigten Lauf über die Aufgabenplanung oder einem Windows-Dienst, bleibt die Verarbeitung an einem Dialog hängen, den niemand schließen kann, und kehrt nie zurück. Verwenden Sie für unbeaufsichtigte Jobs Benachrichtigungsmechanismen, die nicht auf eine Antwort warten – Protokolldateien, das Ereignisprotokoll, E-Mail-Versand.

Add-Type hat drei betrieblich wichtige Einschränkungen.3

  • Der hinzugefügte Typ ist auf diese Sitzung beschränkt. Eine andere Sitzung, oder eine entfernte, braucht erneut Add-Type.
  • Ein Typ mit demselben Namen kann nicht neu definiert werden. Haben Sie eine Signatur falsch geschrieben und wollen sie korrigieren, ändern Sie den Namen oder starten eine neue Sitzung. Experimentieren Sie in einer Wegwerf-Konsole.
  • Unter PowerShell 7 wird die Kompilierung selbst übersprungen, wenn bereits ein Typ mit demselben Namen existiert. Verdächtigen Sie dies, wenn „der Code, den ich angeblich korrigiert habe, nicht greift“.

Noch etwas, das für alle P/Invoke-Arbeit gilt. Ist die Signatur falsch – Argumenttypen, Zeichensatz, Aufrufkonvention –, können Sie statt einer Ausnahme den gesamten Prozess zum Absturz bringen. Die Fallstricke beim Marshalling von Zeichenfolgen und Handles werden ausführlich in „Win32-APIs sicher aus C# aufrufen ── ein praktischer P/Invoke-Leitfaden (DllImport / LibraryImport / CsWin32)“ behandelt, das sich lohnt zu lesen, bevor Sie Win32-APIs über Add-Type ernsthaft einsetzen.

4. COM aufrufen ── New-Object -ComObject und der WSH-Werkzeugkasten

COM ist die Familie von Komponenten, die schon lange vor .NET mit Windows ausgeliefert wurde. Sie erzeugen sie mit New-Object -ComObject <ProgId>, und VBScripts Set objShell = CreateObject("Shell.Application") bildet sich direkt auf $objShell = New-Object -ComObject Shell.Application ab.4 Aus WSH (Windows Script Host) stammende Objekte wie WScript.Shell, WScript.Network und Scripting.FileSystemObject sind auf dieselbe Weise verfügbar.5 Zur grundlegenden Frage, was COM eigentlich ist, siehe „Was sind COM / ActiveX / OCX? - Unterschiede und Zusammenhänge erklärt“.

Ein repräsentatives praktisches Rezept ist das massenhafte Erstellen von Verknüpfungen. Für das Erstellen einer Verknüpfung (.lnk) gibt es kein Cmdlet, und die offizielle Dokumentation selbst schreibt, dass „manche Aufgaben, wie das Erstellen einer Verknüpfung, mit WSH-Klassen einfacher sind“, und gibt ein WScript.Shell-Beispiel.5

# Szenario: Verknüpfungen zu einem Werkzeug auf einem Freigabeordner an den öffentlichen Desktop jedes Rechners verteilen
$shortcuts = Import-Csv 'C:\deploy\shortcuts.csv'   # drei Spalten: Name, Target, Args
$wsh = New-Object -ComObject WScript.Shell

foreach ($item in $shortcuts) {
    $lnkPath = Join-Path 'C:\Users\Public\Desktop' "$($item.Name).lnk"
    $lnk = $wsh.CreateShortcut($lnkPath)   # eine vorhandene .lnk wird überschrieben
    $lnk.TargetPath = $item.Target
    $lnk.Arguments  = $item.Args
    $lnk.Save()
    Write-Host "Erstellt: $lnkPath"
}

Die Member eines COM-Objekts können Sie mit $wsh | Get-Member inspizieren.5 E-Mail-Entwürfe in Outlook, die Arbeit mit Spezialordnern über Shell.Application – das Anwendungsspektrum ist enorm. Doch ActiveX-Ausführbare wie Excel.Application (COM-Server, die in einem eigenen Prozess starten) bringen das im nächsten Abschnitt behandelte Aufräumproblem mit sich. Die offizielle Dokumentation weist zudem darauf hin, dass es von der jeweiligen Anwendung abhängt, ob sich der Prozess beendet, wenn Sie Ihre Referenz freigeben, und dass Sie das Beendungsverhalten testen sollten, bevor Sie sich darauf verlassen.5

5. Nach COM aufräumen ── zurückbleibendes EXCEL.EXE und „unbeaufsichtigtes Office wird nicht unterstützt“

5.1. Warum der Prozess am Leben bleibt

Die Lebensdauer von COM-Objekten wird durch Referenzzählung verwaltet. Berühren Sie COM von .NET aus (also von PowerShell aus), wird für jedes COM-Objekt ein Proxy namens RCW (Runtime Callable Wrapper) erzeugt, und solange der RCW lebt, wird die Referenz auf COM-Seite nicht freigegeben. Das Einsammeln des RCW wird der Garbage Collection überlassen.6 Mit anderen Worten: Selbst wenn Sie $excel.Quit() aufrufen, beendet sich EXCEL.EXE nicht, solange irgendwo noch eine Referenz besteht – einschließlich des RCW eines Zwischenobjekts, das Sie nie einer Variable zugewiesen haben, wie bei $excel.Workbooks.Open(...).

$excel = New-Object -ComObject Excel.Application
try {
    $excel.DisplayAlerts = $false
    $books = $excel.Workbooks              # auch Zwischenobjekte zuweisen, damit sie später freigegeben werden können
    $book  = $books.Open('C:\work\monthly-sales.xlsx')
    $sheet = $book.Worksheets.Item(1)
    $sheet.Cells.Item(1, 1).Value2 = "Aktualisiert: $(Get-Date -Format 'yyyy-MM-dd')"
    $book.Save()
}
finally {
    # Genau der Pfad, bei dem Close fehlschlägt, ist der, an dem EXCEL.EXE tendenziell überlebt,
    # daher verschachtelte finally-Blöcke nutzen, um Quit und Freigabe zu garantieren
    try {
        if ($book) { $book.Close($false) }
    }
    finally {
        # Auch wenn Quit fehlschlägt (Excel reagiert nicht, COM-Server getrennt ...), immer die RCWs freigeben
        try {
            if ($excel) { $excel.Quit() }
        }
        finally {
            # Die Referenzzähler der RCWs explizit herabsetzen (Kinder vor Eltern)
            foreach ($obj in @($sheet, $book, $books, $excel)) {
                if ($obj) { [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($obj) }
            }
            # Absicherung: den GC alle RCWs einsammeln lassen, die wir nicht in einer Variable erfasst haben
            [System.GC]::Collect()
            [System.GC]::WaitForPendingFinalizers()
            [System.GC]::Collect()
        }
    }
}

Marshal.ReleaseComObject setzt den Referenzzähler des RCW herab und gibt die Referenz auf COM-Seite frei, sobald er null erreicht. Die offizielle Dokumentation warnt jedoch eindringlich, dass der Zugriff auf einen bereits freigegebenen RCW Ausnahmen oder Zugriffsverletzungen verursacht, weshalb er „nur eingesetzt werden sollte, wenn es absolut nötig ist“.7 Die konkurrierenden Freigabemuster (die ReleaseComObject-Fraktion und die GC-Fraktion), die Zwei-Punkte-Regel-Falle und wie sich trotzdem überlebende Prozesse identifizieren lassen, werden ausführlich in „Warum EXCEL.EXE-Prozesse nach C#-Excel-COM-Automatisierung bestehen bleiben ── Muster zur Referenzfreigabe und die Ersatz-Entscheidung“ behandelt. Dieser Artikel dreht sich um C#, doch der RCW-Mechanismus ist in PowerShell identisch.

5.2. Noch besser: Office in unbeaufsichtigten Läufen gar nicht erst verwenden

Es gibt eine grundlegendere Entscheidung zu treffen. Microsoft stellt offiziell fest, dass es „die Automatisierung von Office durch unbeaufsichtigte, nicht interaktive Client-Anwendungen oder -Komponenten (einschließlich ASP, ASP.NET, DCOM und NT-Diensten) derzeit weder empfiehlt noch unterstützt“. Office ist auf interaktive Nutzung ausgelegt und kann sich in unbeaufsichtigten Umgebungen unvorhersehbar verhalten oder blockieren.8 Konkrete Probleme werden ebenfalls genannt: Verarbeitung, die an einem unerwarteten Dialog stehen bleibt, sowie das STA-basierte Design, das gleichzeitiger Ausführung nicht standhält.8

Eine Konfiguration, die Excel-COM aus einem nächtlichen Aufgabenplanungsjob oder aus einem Dienst heraus steuert, ist also von dem Moment an nicht unterstützt, in dem sie zu funktionieren beginnt. Für unbeaufsichtigte Verarbeitung lautet die Empfehlung, auf direktes Bearbeiten von Dateien mit Open XML oder CSV-Integration umzusteigen – irgendetwas, das die Office-Anwendung selbst nicht startet.8 Konkrete PowerShell-Alternativen sind in „Excel- und CSV-Arbeit mit PowerShell automatisieren ── praktische Rezepte für Aggregation, Abgleich und Berichtsausgabe“ zusammengestellt, das parallel zu diesem Artikel veröffentlicht wurde. Ziehen Sie die Grenze so, dass Excel-COM nur „zur Unterstützung der Arbeit eines Menschen, auf einem Desktop, an dem eine Person angemeldet ist“ eingesetzt wird.

6. Das nutzbare .NET unterscheidet sich zwischen 5.1 und 7

Windows PowerShell 5.1 baut auf der .NET-Framework-4.5-Linie auf, PowerShell 7 auf .NET (ehemals .NET Core).1 Solange Sie bei Cmdlets bleiben, gibt es kaum Gelegenheiten, den Unterschied zu bemerken, doch sobald Sie wie in diesem Artikel direkt .NET aufrufen, tritt er zutage.

Punkt Windows PowerShell 5.1 PowerShell 7
Grundlage .NET-Framework-4.5-Linie1 .NET (7.4 basiert auf .NET 8.0, je nach Version aktualisiert)1
Methodenüberladungen Weniger (z. B. hat String.Split 6)1 Mehr (die Dokumentation zeigt, dass dasselbe Split('pq') unterschiedliche Ergebnisse liefert)1
Assembly-Laden GAC-Assemblys müssen oft explizit mit Add-Type geladen werden3 Gebündelte Assemblys laden automatisch bei Bedarf3
Nicht mehr Verfügbares Einige reine Windows-Cmdlets wie *-EventLog wurden entfernt1

Sowohl „es funktionierte unter 5.1, aber nicht unter 7“ als auch der umgekehrte Fall können vorkommen. Jedes Skript, das beide unterstützen muss, ist in beiden Umgebungen zu testen. Das Gesamtbild für Migrationsentscheidungen wird in „Die Unterschiede zwischen Windows PowerShell 5.1 und PowerShell 7 ── ein praktischer Leitfaden zur Migration interner Skripte“ behandelt, das parallel zu diesem Artikel veröffentlicht wurde.

7. Praktische Faustregeln (Entscheidungstabelle)

Punkt Optionen Wie zu entscheiden ist
Kein Cmdlet existiert für das Gewünschte Aufgeben / Die .NET-Klasse direkt aufrufen Zuerst nach [Type]::Method suchen. System.IO, System.Text und System.IO.Compression füllen die meisten „fast schon da“-Lücken1
Eine Instanz erzeugen New-Object / [Type]::new() Unterstützen Sie nur 5.0 und höher, verwenden Sie ::new(). Sie erhalten die Konstruktorliste, und es ist schneller. COM nur mit New-Object -ComObject24
Sie brauchen eine Win32-API Von Hand erledigen stattdessen / P/Invoke über Add-Type Für eine Handvoll APIs reicht Add-Type völlig. Eine falsche Signatur bringt den ganzen Prozess zum Absturz, daher in einer Wegwerf-Sitzung prüfen3
Aus WSH stammende Vorgänge wie das Erstellen von Verknüpfungen COM (WScript.Shell) COM ist dort weiterhin aktuell, wo kein Cmdlet existiert. Auch das offizielle Beispiel nutzt diesen Ansatz5
Excel-Arbeit, die einen Menschen unterstützt COM + konsequentes Aufräumen Quit, ReleaseComObject und GC als Satz in finally. Auch Zwischenobjekte Variablen zuweisen76
Excel-/Office-Verarbeitung in einem unbeaufsichtigten Job COM / Eine Methode, die Office nicht startet Unbeaufsichtigte Office-Automatisierung wird nicht unterstützt. Durch Open XML oder CSV ersetzen8
Das Skript ist sich selbst entwachsen In PowerShell weitermachen / Ein C#-Werkzeug daraus machen Sobald der mit Add-Type eingebettete C#-Code auf hunderte Zeilen anwächst, die Nicht-Neudefinierbarkeits-Einschränkung die Entwicklung behindert oder Sie keine Kompilierung auf jedem Zielrechner wollen – das ist das Signal zum Wechsel

Diese letzte Zeile ist das abschließende Urteil dieses Artikels. Sobald der mit Add-Type eingebettete C#-Code aufzublähen beginnt, haben Sie „ein C#-Programm in einer PowerShell-Hülle“ vor sich. Es in ein C#-Projekt zu verschieben – mit Visual Studios Typprüfung und Debugger, NuGet-Paketen und Unit-Tests – macht sowohl Entwicklung als auch Wartung schneller. Und da es Wege gibt, aus C# heraus wieder auf Ihre PowerShell-Assets zurückzugreifen, bedeutet die Migration nicht, alles neu zu schreiben. „Wie man PowerShell aus C# (CSharp) heraus ausführt und die Ergebnisse als Objekte empfängt“ behandelt die Brückenmuster.

8. Zusammenfassung

  • PowerShell baut auf .NET auf, und Sie können die .NET-Klassenbibliothek direkt mit [Type]::StaticMethod, New-Object und [Type]::new() aufrufen. Gibt es kein Cmdlet, ist .NET der erste Ort, an dem Sie nachsehen sollten.
  • Add-Type kompiliert C#-Code an Ort und Stelle, und die Übergabe einer DllImport-Signatur lässt Sie über P/Invoke Win32-APIs aufrufen. Typen sind sitzungsgebunden, und ein Typ mit demselben Namen kann nicht neu definiert werden.
  • COM wird mit New-Object -ComObject gesteuert. Aus WSH stammende Werkzeuge wie das Erstellen von Verknüpfungen sind weiterhin aktuell.
  • Die Lebensdauer von COM wird durch Referenzzählung plus RCWs verwaltet, und zurückbleibende Referenzen lassen Prozesse wie EXCEL.EXE weiterlaufen. Schreiben Sie Aufräumcode, der Quit, ReleaseComObject und den GC in einem finally-Block abdeckt.
  • Microsoft stellt unmissverständlich fest, dass unbeaufsichtigte Office-Automatisierung weder empfohlen noch unterstützt wird. Ersetzen Sie nächtliche Jobs durch Methoden, die Office nie starten.
  • 5.1 und 7 sitzen auf unterschiedlichen .NET-Grundlagen mit unterschiedlichen verfügbaren Typen und Überladungen. Skripte, die beide unterstützen müssen, sind auf beiden zu testen.
  • Ein aufgeblähter Add-Type-Block ist das Signal, das Skript in ein C#-Werkzeug umzuwandeln. Es gibt etablierte Wege, um zwischen einem Skript und einer ausführbaren Datei zu vermitteln.

Verwandte Artikel

Verwandte Beratungsbereiche

Die KomuraSoft LLC übernimmt die Automatisierung interner Geschäftsprozesse mit PowerShell, das Design und die Überarbeitung von Skripten mit COM-Bezug (Excel-Integration, Legacy-Komponenten) sowie die Umwandlung von Skripten, die „sich selbst entwachsen sind“, in C#-Werkzeuge. Wir übernehmen auch Fehlersuchen wie zurückbleibende EXCEL.EXE-Prozesse.

  1. Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. Dazu, dass Windows PowerShell 5.1 auf .NET Framework 4.5 aufbaut und PowerShell 6.0 und höher auf .NET Core (heute .NET), zur Liste der .NET-Versionen, die jeder PowerShell-Version zugrunde liegen, zum Beispiel, bei dem Überladungsunterschiede von String.Split das Ergebnis identischen Codes verändern, sowie zu den in 7 entfernten Cmdlets.  2 3 4 5 6 7 8 9

  2. Microsoft Learn, about_Object_Creation. Zur statischen new()-Methode, die in PowerShell 5.0 allen .NET-Typen hinzugefügt wurde, dazu, dass die Eingabe von ::new die Konstruktorüberladungen auflistet, sowie dazu, dass über Cmdlets bezogene Objekte von PowerShell hinzugefügte NoteProperties besitzen, sodass ihre Member nicht mit über ::new() erzeugten Objekten übereinstimmen müssen.  2 3 4

  3. Microsoft Learn, Add-Type. Dazu, wie Add-Type C#-Quellcode kompiliert und Typen der Sitzung hinzufügt, zum offiziellen P/Invoke-Beispiel mit MemberDefinition (ShowWindowAsync in user32.dll), dazu, dass hinzugefügte Typen sitzungsgebunden sind und ein Typ mit demselben Namen nicht neu definiert werden kann, dazu, dass PowerShell 7 die Kompilierung überspringt, wenn bereits ein Typ mit demselben Namen existiert, sowie dazu, dass 5.1 Add-Type zum Laden von GAC-Assemblys benötigt, während 6 und höher sie automatisch laden.  2 3 4 5 6 7 8

  4. Microsoft Learn, New-Object. Dazu, wie New-Object Instanzen von .NET- oder COM-Objekten erzeugt, dazu, dass der Parameter -ComObject eine ProgId entgegennimmt, sowie dazu, dass VBScripts CreateObject(“Shell.Application”) New-Object -ComObject “Shell.Application” entspricht.  2 3

  5. Microsoft Learn, Creating .NET and COM objects (New-Object). Zum Erstellen von WSH-Objekten wie WScript.Shell, zur Aussage, dass das Erstellen von Verknüpfungen mit WSH-Klassen einfacher ist, samt einem ausgearbeiteten CreateShortcut-Beispiel, zur Anwendung von Get-Member auf COM-Objekte, dazu, dass das Beendungsverhalten von ActiveX-Ausführbaren anwendungsabhängig ist und vorab getestet werden muss, sowie dazu, dass New-Object .NETs RCW verwendet.  2 3 4 5 6

  6. Microsoft Learn, Runtime Callable Wrapper. Dazu, wie .NET COM-Objekte über einen Proxy namens RCW zugänglich macht, dazu, dass pro COM-Objekt genau ein RCW erzeugt wird, sowie dazu, dass der RCW seine Referenz auf das COM-Objekt freigibt, sobald er von der Garbage Collection eingesammelt wird.  2 3

  7. Microsoft Learn, Marshal.ReleaseComObject(Object) Method. Dazu, wie ReleaseComObject den Referenzzähler des RCW herabsetzt und das zugrunde liegende COM-Objekt bei null freigibt, dazu, dass die Nutzung eines RCW nach der Freigabe Ausnahmen oder Zugriffsverletzungen verursacht, zur offiziellen Warnung, es „nur einzusetzen, wenn es absolut nötig ist“, sowie zu seinem Verhältnis zu FinalReleaseComObject.  2 3

  8. Microsoft Learn, Considerations for unattended automation of Office in the Microsoft 365 for unattended RPA environment. Dazu, dass Microsoft die Automatisierung von Office durch unbeaufsichtigte, nicht interaktive Client-Anwendungen oder -Komponenten (einschließlich ASP, ASP.NET, DCOM und NT-Diensten) weder empfiehlt noch unterstützt, zur Möglichkeit instabilen Verhaltens und von Blockierungen in unbeaufsichtigten Umgebungen, zu konkreten Problemen wie dem Anhalten an Dialogen und aus STA stammenden Nebenläufigkeitseinschränkungen, sowie dazu, dass das direkte Bearbeiten von Open-XML-Dateiformaten die empfohlene Alternative ist.  2 3 4 5

  9. Microsoft Learn, Expand-Archive. Dazu, dass Expand-Archive die API System.IO.Compression.ZipArchive verwendet, sowie dazu, dass diese API eine maximale Dateigrößengrenze von 2 GB hat. 

  10. Microsoft Learn, ZipFile Class. Dazu, wie die Klasse ZipFile statische Methoden zum Erstellen, Entpacken und Öffnen von ZIP-Dateien bereitstellt, dazu, dass ihre Nutzung im .NET Framework einen Verweis auf die Assembly System.IO.Compression.FileSystem erfordert, sowie zu den Zwecken von CreateFromDirectory, ExtractToDirectory und OpenRead.  2

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Kann ich .NET-Klassen direkt aus PowerShell aufrufen?
Ja, das können Sie. PowerShell baut auf .NET auf, sodass Sie den Typnamen in eckige Klammern schreiben und eine statische Methode mit :: aufrufen, etwa [System.IO.Path]::GetFileNameWithoutExtension(). Brauchen Sie eine Instanz, erzeugen Sie sie mit New-Object oder mit [Type]::new(), verfügbar seit PowerShell 5.0. Selbst wo kein Cmdlet für das existiert, was Sie wollen – steht es in der .NET-Klassenbibliothek, können Sie es ohne zusätzliche Installation aus einem Skript heraus nutzen.
Sollte ich New-Object oder [Type]::new() verwenden?
Beide erzeugen das Objekt, aber [Type]::new() ohne Klammern eingegeben listet die Konstruktoren dieses Typs auf, was es praktisch macht, um beim Schreiben die Argumente herauszufinden. Es ist zudem leistungsfähiger. COM-Objekte lassen sich hingegen nur mit New-Object -ComObject erzeugen. Nutzen Sie auch New-Object, wenn Sie Umgebungen älter als PowerShell 5.0 unterstützen müssen.
Warum bleibt EXCEL.EXE laufen, nachdem ich Excel über COM aus PowerShell heraus gesteuert habe?
Die Lebensdauer von COM-Objekten wird durch Referenzzählung verwaltet, und auf .NET-Seite hält ein Runtime Callable Wrapper (RCW) diese Referenz. Schreiben Sie etwas wie $excel.Workbooks.Open(), wird auch für das Zwischenobjekt Workbooks unsichtbar ein RCW erzeugt, der eine Referenz hinterlässt, die Sie nie einer Variable zugewiesen haben. Rufen Sie Quit() auf, während noch Referenzen bestehen, beendet sich der Prozess nicht. Am Ende müssen Sie explizit aufräumen – entweder mit Marshal.ReleaseComObject freigeben oder die Variablen auf null setzen und GC.Collect() sowie WaitForPendingFinalizers() sie einsammeln lassen.
Ist es akzeptabel, Excel über COM auf einem Server oder in einem nächtlichen Batch-Job zu steuern?
Das wird nicht empfohlen. Microsoft stellt offiziell fest, dass die Automatisierung von Office-Anwendungen aus unbeaufsichtigten, nicht interaktiven Client-Anwendungen oder -Komponenten weder empfohlen noch unterstützt wird. Office ist auf interaktive Desktop-Nutzung ausgelegt und kann sich in unbeaufsichtigten Läufen unvorhersehbar verhalten oder blockieren. Für unbeaufsichtigte Verarbeitung ist der Standardweg, auf Methoden umzusteigen, die die Office-Anwendung selbst nie starten – Open-XML-Bibliotheken, CSV-Integration und Ähnliches.
Kann ich Win32-APIs mit Add-Type aufrufen?
Ja, das können Sie. Übergeben Sie eine C#-Signatur mit DllImport an -MemberDefinition von Add-Type, wird sie an Ort und Stelle kompiliert, sodass Sie Win32-APIs über P/Invoke aufrufen können. Die offizielle Dokumentation enthält sogar ein Beispiel, das ShowWindowAsync in user32.dll aufruft. Beachten Sie, dass der hinzugefügte Typ in der Sitzung bestehen bleibt und ein Typ mit demselben Namen nicht neu definiert werden kann – starten Sie beim Experimentieren daher jeweils eine neue Sitzung. Eine falsche Signatur kann den gesamten Prozess zum Absturz bringen, daher ist es am sichersten, in einer Wegwerf-Konsole zu prüfen.
Gibt es Unterschiede beim Aufrufen von .NET zwischen Windows PowerShell 5.1 und PowerShell 7?
Ja. 5.1 baut auf der .NET-Framework-4.5-Linie auf, während PowerShell 7 auf .NET (ehemals .NET Core) basiert, sodass sich die verfügbaren Typen und Methodenüberladungen unterscheiden. String.Split hat in 7 zum Beispiel mehr Überladungen, und die offizielle Dokumentation zeigt ein Beispiel, bei dem derselbe Code unterschiedliche Ergebnisse liefert. Unter 5.1 müssen Sie zudem häufig GAC-Assemblys mit Add-Type laden, während 7 gebündelte Assemblys automatisch bei Bedarf lädt. Testen Sie jedes Skript, das beide unterstützen muss, in beiden Umgebungen.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog