.NET-Typen in PowerShell-Klassen

Oct 23 2020

Ich versuche, mich mit den Best Practices der PowerShell-Klasse vertraut zu machen, und bin mit einer einfachen Klasse, die mit Ballontipps umgehen soll, in Verwirrung geraten.

Add-Type -assemblyName:System.Drawing
Add-Type -assemblyName:System.Windows.Forms

class PxMessage {
    static [PxMessage] $instance static $balloon
    static $defaultIcon static [PxMessage] GetInstance($processIcon) {
        if ([PxMessage]::instance -eq $null) { [PxMessage]::instance = [PxMessage]::new() #[PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new() [PxMessage]::balloon = New-Object Windows.Forms.NotifyIcon [PxMessage]::defaultIcon = $processIcon
        }

        return [PxMessage]::instance
    }

    [Void] SendMessage ([String]$title, [String]$message, [String]$messageIcon) { [PxMessage]::balloon.icon = [PxMessage]::defaultIcon [PxMessage]::balloon.balloonTipTitle = $title
        [PxMessage]::balloon.balloonTipText = $message [PxMessage]::balloon.balloonTipIcon = $messageIcon
        [PxMessage]::balloon.visible = $true [PxMessage]::balloon.ShowBalloonTip(0) [PxMessage]::balloon.Dispose } } $processIcon = [System.Drawing.Icon]::ExtractAssociatedIcon($(Get-Process -id:$PID | Select-Object -expandProperty:path))
$message = [PxMessage]::GetInstance($processIcon)

$message.SendMessage('Title', "$(Get-Date)", 'Info')

Ich habe zwei Fragen:

1: Warum funktioniert [PxMessage]::balloon = New-Object Windows.Forms.NotifyIcon, aber [PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()nicht ( Unable to find typeFehler)? Und deutet dies darauf hin, dass die Verwendung [Type]::new()noch nicht vollständig unterstützt wird und ich aus Gründen der Konsistenz besser überall New-Object verwenden sollte? Oder zumindest überall in meinen eigenen Klassen?

2: Ich möchte meine Eigenschaften und Parameter eingeben, erhalte jedoch auch Unable to find typeFehler, wenn ich die Eigenschaften $balloon& $defaultIconeingebe oder wenn ich den $processIconParameter in die GetInstanceMethode eingebe.

Natürlich kann ich Eigenschaften eingeben, auch wenn mein Typ definiert ist. Was ist also anders an den beiden [System.Drawing]& [System.Windows.Forms]und ist dies ein Fehler oder eine Funktion? Und gibt es andere Typen, die sich ähnlich verhalten?

Antworten

4 MathiasR.Jessen Oct 22 2020 at 22:44

Dies ist im Wesentlichen eine Rennbedingung!

Wenn PowerShell mit der Ausführung einer Skriptdatei beginnt, werden drei Phasen durchlaufen:

  • Parsing
  • Zusammenstellung
  • Ausführung

Das allererste, was in der Kompilierungsphase verarbeitet wird (also bevor die Ausführung überhaupt beginnt), ist:

  • Alle usingAnweisungen oben im Skript
  • Beliebige Typdefinitionen - beliebige classoder enumSchlüsselwörter - werden separat kompiliert

Die Fehler beim Auflösen des [Windows.Forms.NotifyIcon]Typliteral in der Klassendefinition werden also tatsächlich ausgelöst, bevor die Add-Type -AssemblyName:System.Windows.Forms Chance besteht, jemals ausgeführt zu werden!

Einige Optionen:

Verschachtelte Skripte

Schreiben Sie ein separates Loader-Skript, das die Abhängigkeit lädt:

# loader.ps1
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
. .\scriptWithTypes.ps1
# scriptWithTypes.ps1
class ClassDependentOnForms
{
  [Windows.Forms.NotifyIcon]$BalloonTipIcon
}

Mit Modulen

Mit Modulen ist es etwas einfacher, Abhängigkeiten vor dem Kompilieren der benutzerdefinierten Typdefinitionen zu verwalten. Fügen Sie einfach die Assemblynamen RequiredAssemblieszum Modulmanifest hinzu:

New-ModuleManifest ... -RootModule moduleWithTypes.psm1 -RequiredAssemblies System.Windows.Forms,System.Drawing

Laden von der Festplatte mit using assembly ...

Wenn der Pfad der erforderlichen Assembly bekannt ist, können Sie ihn zur Analysezeit mit einer using assemblyAnweisung laden :

using assembly '.\path\to\System.Drawing.dll'
using assembly '.\path\to\System.Windows.Forms.dll'

class FormsDependentClass
{
  [Windows.Forms.NotifyIcon]$BallonTipIcon
}

Für Ihren Anwendungsfall ist dieser letzte nicht sehr attraktiv, da Sie die Baugruppe fest codieren müssen, anstatt sie nur namentlich aus dem GAC zu laden.


Warum tritt das überhaupt auf?

Dieses Verhalten kann etwas verwirrend sein, da alles andere in PowerShell nur eine einfache Interpretation "eine Aussage nach der anderen" ist.

Der Grund für diese Ausnahme besteht darin, dass Skripte und Funktionen benutzerdefinierte Parametertypen kapseln können:

param(
  [CustomEnumType]$Option ) begin { enum CustomEnumType { None Option1 Option2 } } end { # do something based on $Option
}

Ohne diese vorbeugende Kompilierung zum CustomEnumTypeZeitpunkt der Analyse wäre PowerShell nicht in der Lage, eine automatische Vervollständigung und Eingabevalidierung für das -OptionParameterargument anzubieten, da der Typ nicht vorhanden wäre