PowerShell 类中的 .NET 类型

Gor*_*don 4 .net powershell class

我试图了解 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')
Run Code Online (Sandbox Code Playgroud)

我有两个问题:

1:为什么可以[PxMessage]::balloon = New-Object Windows.Forms.NotifyIcon工作,但[PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()不能(Unable to find type错误)?这是否表明 using[Type]::new()尚未得到完全支持,并且为了保持一致性,我最好在各处使用 New-Object ?或者至少在我自己的班级中到处都是?

2:我想输入我的属性和参数,但是Unable to find type当我输入$balloon&$defaultIcon属性或$processIconGetInstance方法中输入参数时,我也会收到错误。

显然,即使我的类型已定义,我也可以键入 Properties。[System.Drawing]那么这两个&有什么不同[System.Windows.Forms],这是一个错误还是一个功能?还有其他类型的行为类似吗?

Mat*_*sen 7

这本质上是一个竞争条件!

当 PowerShell 开始执行脚本文件时,它会经历 3 个阶段:

  • 解析
  • 汇编
  • 执行

在编译阶段处理的第一件事(因此在执行开始之前)是:

  • using脚本顶部的所有语句
  • 任何类型定义(anyclassenum关键字)均单独编译

[Windows.Forms.NotifyIcon]因此,与解析类定义中的类型文字相关的错误实际上是在有机会运行之前抛出的! Add-Type -AssemblyName:System.Windows.Forms

几个选项:

嵌套脚本

编写一个单独的加载器脚本来加载依赖项:

# loader.ps1
Add-Type -AssemblyName System.Windows.Forms,System.Drawing
. .\scriptWithTypes.ps1
Run Code Online (Sandbox Code Playgroud)
# scriptWithTypes.ps1
class ClassDependentOnForms
{
  [Windows.Forms.NotifyIcon]$BalloonTipIcon
}
Run Code Online (Sandbox Code Playgroud)

带模块

使用模块,在编译自定义类型定义之前管理依赖关系会更简单 - 只需将程序集名称添加RequiredAssemblies到模块清单中即可:

New-ModuleManifest ... -RootModule moduleWithTypes.psm1 -RequiredAssemblies System.Windows.Forms,System.Drawing
Run Code Online (Sandbox Code Playgroud)

从磁盘加载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
}
Run Code Online (Sandbox Code Playgroud)

对于您的用例,最后一个不是很有吸引力,因为您需要对程序集进行硬编码,而不是仅按名称从 GAC 加载它。


首先为什么会发生这种情况?

这种行为可能会有点令人困惑,因为 PowerShell 中的其他所有内容只是简单的“一次一个语句”解释。

出现此异常的原因是允许脚本和函数封装自定义参数类型:

param(
  [CustomEnumType]$Option
)

begin {
  enum CustomEnumType {
    None
    Option1
    Option2
  }
}

end {
  # do something based on $Option
}
Run Code Online (Sandbox Code Playgroud)

如果没有解析时的抢先编译CustomEnumType,PowerShell 将无法为-Option参数参数提供自动完成和输入验证 - 因为它的类型将不存在