.NET w klasach programu PowerShell
Próbuję zapoznać się z najlepszymi praktykami klasy PowerShell i wpadam w pewne zamieszanie z prostą klasą do obsługi wskazówek dotyczących balonów.
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')
Mam dwa pytania:
1: Dlaczego [PxMessage]::balloon = New-Object Windows.Forms.NotifyIcondziała, ale [PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()nie ( Unable to find typebłąd)? I czy to sugeruje, że używanie [Type]::new()nie jest jeszcze w pełni obsługiwane, a ze względu na spójność lepiej jest wszędzie używać New-Object? A przynajmniej wszędzie w moich własnych klasach?
2: Chciałbym wpisać moje właściwości i parametry, ale pojawiają się również Unable to find typebłędy, gdy wpisuję $balloon& $defaultIconproperties, lub gdy wpisuję $processIconparametr w GetInstancemetodzie.
Oczywiście mogę wpisać Właściwości, nawet gdy mój typ jest zdefiniowany. Więc czym różni się te dwa [System.Drawing]& [System.Windows.Forms]i czy jest to błąd, czy funkcja? Czy są inne typy, które zachowują się podobnie?
Odpowiedzi
Zasadniczo jest to stan wyścigu!
Gdy PowerShell rozpoczyna wykonywanie pliku skryptu, przechodzi przez 3 fazy:
- Rozbiór gramatyczny zdania
- Kompilacja
- Wykonanie
Pierwszą rzeczą , która jest przetwarzana w kompilacji fazy (tak przed egzekucja zaczyna nawet) to:
- Wszystkie
usinginstrukcje u góry skryptu - Wszelkie definicje typów - dowolne
classlubenumsłowa kluczowe - są kompilowane osobno
Więc błędy związane z rozwiązywaniem [Windows.Forms.NotifyIcon]literału typu wewnątrz definicji klasy są w rzeczywistości wyrzucane, zanim będą Add-Type -AssemblyName:System.Windows.Forms miały szansę kiedykolwiek działać!
Kilka opcji:
Zagnieżdżone skrypty
Napisz osobny skrypt modułu ładującego, który ładuje zależność:
# loader.ps1
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
. .\scriptWithTypes.ps1
# scriptWithTypes.ps1
class ClassDependentOnForms
{
[Windows.Forms.NotifyIcon]$BalloonTipIcon
}
Z modułami
W przypadku modułów nieco prostsze jest zarządzanie zależnościami przed kompilacją niestandardowych definicji typów - wystarczy dodać nazwy zestawów zgodnie RequiredAssembliesz manifestem modułu:
New-ModuleManifest ... -RootModule moduleWithTypes.psm1 -RequiredAssemblies System.Windows.Forms,System.Drawing
Załaduj z dysku za pomocą using assembly ...
Jeśli ścieżka wymaganego zestawu jest znana, możesz ją załadować w czasie analizy za pomocą using assemblyinstrukcji:
using assembly '.\path\to\System.Drawing.dll'
using assembly '.\path\to\System.Windows.Forms.dll'
class FormsDependentClass
{
[Windows.Forms.NotifyIcon]$BallonTipIcon
}
W twoim przypadku ten ostatni nie jest zbyt atrakcyjny, ponieważ musiałbyś zakodować na stałe zestaw zamiast po prostu ładować go z GAC po nazwie.
Dlaczego tak się dzieje w pierwszej kolejności?
To zachowanie może być nieco zagmatwane, ponieważ wszystko inne w programie PowerShell jest po prostu prostą interpretacją „jednej instrukcji na raz”.
Powodem tego wyjątku jest zezwolenie skryptom i funkcjom na hermetyzację niestandardowych typów parametrów:
param(
[CustomEnumType]$Option ) begin { enum CustomEnumType { None Option1 Option2 } } end { # do something based on $Option
}
Bez tej wywłaszczeniowej kompilacji CustomEnumTypew czasie analizy program PowerShell nie byłby w stanie zaoferować autouzupełniania i walidacji danych wejściowych dla -Optionargumentu parametru - ponieważ jego typ nie istniałby