Типы .NET в классах PowerShell
Я пытаюсь разобраться в передовых методах работы с классом PowerShell и натолкнулся на некоторую путаницу с простым классом для обработки всплывающих подсказок.
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')
У меня два вопроса:
1: Почему [PxMessage]::balloon = New-Object Windows.Forms.NotifyIconработает, а [PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()нет ( Unable to find typeошибка)? И означает ли это, что использование [Type]::new()еще не полностью поддерживается, и для единообразия мне лучше везде использовать New-Object? Или, по крайней мере, везде в моих классах?
2: Я хотел бы ввести свои свойства и параметры, но я также получаю Unable to find typeошибки при вводе свойств $balloon& $defaultIconили при вводе $processIconпараметра в GetInstanceметоде.
Очевидно, я могу ввести Properties, даже если мой тип определен. Так чем же отличается два [System.Drawing]& [System.Windows.Forms], и это ошибка или особенность? И есть ли другие типы, которые ведут себя аналогичным образом?
Ответы
По сути, это состояние гонки!
Когда PowerShell начинает выполнение файла сценария, он проходит 3 фазы:
- Парсинг
- Компиляция
- Исполнение
Самое первое, что обрабатывается на этапе компиляции (то есть еще до начала выполнения):
- Все
usingутверждения в верхней части скрипта - Определения любых типов - любые
classилиenumключевые слова - составляются отдельно.
Таким образом, ошибки, связанные с разрешением [Windows.Forms.NotifyIcon]литерала типа внутри определения класса, на самом деле возникают до того, как Add-Type -AssemblyName:System.Windows.Forms появится шанс когда-либо запустить!
Пара вариантов:
Вложенные скрипты
Напишите отдельный сценарий загрузчика, который загружает зависимость:
# loader.ps1
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
. .\scriptWithTypes.ps1
# scriptWithTypes.ps1
class ClassDependentOnForms
{
[Windows.Forms.NotifyIcon]$BalloonTipIcon
}
С модулями
С модулями немного проще управлять зависимостями перед компиляцией определений пользовательского типа - просто добавьте имена сборок RequiredAssembliesв манифест модуля:
New-ModuleManifest ... -RootModule moduleWithTypes.psm1 -RequiredAssemblies System.Windows.Forms,System.Drawing
Загрузить с диска с помощью using assembly ...
Если путь к требуемой сборке известен, вы можете загрузить его во время синтаксического анализа с помощью using assemblyоператора:
using assembly '.\path\to\System.Drawing.dll'
using assembly '.\path\to\System.Windows.Forms.dll'
class FormsDependentClass
{
[Windows.Forms.NotifyIcon]$BallonTipIcon
}
Для вашего варианта использования последний не очень привлекателен, потому что вам нужно жестко закодировать сборку, а не просто загружать ее из GAC по имени.
Почему это вообще происходит?
Такое поведение может немного сбивать с толку, потому что все остальное в PowerShell является простой интерпретацией «по одному утверждению за раз».
Причина этого исключения состоит в том, чтобы разрешить скриптам и функциям инкапсулировать типы настраиваемых параметров:
param(
[CustomEnumType]$Option ) begin { enum CustomEnumType { None Option1 Option2 } } end { # do something based on $Option
}
Без этой превентивной компиляции CustomEnumTypeво время синтаксического анализа PowerShell не смог бы предложить автозаполнение и проверку ввода для -Optionаргумента параметра - потому что его тип не существовал бы