PowerShell 클래스의 .NET 유형

Oct 23 2020

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$processIconGetInstance

내 유형이 정의되어 있어도 속성을 입력 할 수 있습니다. 그래서 두 [System.Drawing]&의 차이점은 무엇이며 [System.Windows.Forms]이것은 버그입니까, 아니면 기능입니까? 유사하게 작동하는 다른 유형이 있습니까?

답변

4 MathiasR.Jessen Oct 22 2020 at 22:44

이것은 본질적으로 경쟁 조건입니다!

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매개 변수 인수에 대한 자동 완성 및 입력 유효성 검사를 제공 할 수 없습니다. 유형이 존재하지 않기 때문입니다.