.NET loại trong các lớp PowerShell

Oct 23 2020

Tôi đang cố gắng tìm hiểu các phương pháp hay nhất của PowerShell Class và gặp một số nhầm lẫn với một lớp đơn giản để xử lý Balloon Tips.

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')

Tôi có hai câu hỏi:

1: Tại sao [PxMessage]::balloon = New-Object Windows.Forms.NotifyIconhoạt động, nhưng [PxMessage]::balloon = [Windows.Forms.NotifyIcon]::new()không ( Unable to find typelỗi)? Và điều này có cho thấy rằng việc sử dụng [Type]::new()vẫn chưa được hỗ trợ đầy đủ, và vì lợi ích nhất quán, tôi nên sử dụng New-Object ở mọi nơi? Hoặc ít nhất là ở mọi nơi trong Lớp học của riêng tôi?

2: Tôi muốn nhập thuộc tính & tham số của mình, nhưng tôi cũng gặp Unable to find typelỗi khi nhập thuộc tính $balloon& $defaultIconhoặc nếu tôi nhập $processIcontham số trong GetInstancephương thức.

Rõ ràng là tôi có thể gõ Thuộc tính, ngay cả khi kiểu của tôi đang được định nghĩa. Vậy điều gì khác biệt về hai [System.Drawing]& [System.Windows.Forms], và đây là một lỗi hay một tính năng? Và có những loại khác hoạt động tương tự không?

Trả lời

4 MathiasR.Jessen Oct 22 2020 at 22:44

Đây thực chất là một điều kiện chạy đua!

Khi PowerShell bắt đầu thực thi một tệp script, nó sẽ trải qua 3 giai đoạn:

  • Phân tích cú pháp
  • Tổng hợp
  • Chấp hành

Điều đầu tiên được xử lý trong giai đoạn biên dịch , (vì vậy trước khi bắt đầu thực thi) là:

  • Tất cả các usingcâu lệnh ở đầu tập lệnh
  • Bất kỳ định nghĩa loại nào - bất kỳ classhoặc enumtừ khóa nào - được biên dịch riêng biệt

Vì vậy, các lỗi liên quan đến việc giải quyết [Windows.Forms.NotifyIcon]kiểu chữ bên trong định nghĩa lớp thực sự được ném ra trước khi Add-Type -AssemblyName:System.Windows.Forms có cơ hội chạy!

Một số lựa chọn:

Tập lệnh lồng nhau

Viết một tập lệnh trình tải riêng để tải phần phụ thuộc:

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

Với các mô-đun

Với các mô-đun, việc quản lý các phần phụ thuộc đơn giản hơn một chút trước khi biên dịch các định nghĩa kiểu tùy chỉnh - chỉ cần thêm tên RequiredAssemblieshợp ngữ vào tệp kê khai mô-đun:

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

Tải từ đĩa với using assembly ...

Nếu đường dẫn của hợp ngữ được yêu cầu được biết, bạn có thể tải nó tại thời điểm phân tích cú pháp bằng một using assemblycâu lệnh:

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

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

Đối với trường hợp sử dụng của bạn, điều cuối cùng này không hấp dẫn lắm vì bạn cần phải mã hóa hội đồng thay vì chỉ tải nó từ GAC theo tên.


Tại sao điều này xảy ra ngay từ đầu?

Hành vi này có thể hơi khó hiểu vì mọi thứ khác trong PowerShell chỉ là diễn giải "một câu lệnh tại một thời điểm" đơn giản.

Lý do cho ngoại lệ này là để cho phép các tập lệnh và hàm đóng gói các loại thông số tùy chỉnh:

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

Nếu không có quá trình biên dịch ưu tiên này CustomEnumTypetại thời điểm phân tích cú pháp, PowerShell sẽ không thể cung cấp tính năng tự động hoàn thành và xác thực đầu vào cho đối -Optionsố tham số - bởi vì kiểu của nó sẽ không tồn tại