【问题标题】:Using .NET objects within a Powershell (V5) class在 Powershell (V5) 类中使用 .NET 对象
【发布时间】:2022-01-26 18:59:33
【问题描述】:

以下是我遇到问题的确切代码。

简要说明:

我正在尝试设置一个 PowerShell 类,该类将保存不同类型的对象以便于访问。我已经在 C# 中做过很多次了,所以我认为它会相当简单。需要的类型是 [System.Printing] 和 WMI-Objects。

最初我试图将类直接写入我的 PowerShell 配置文件以便于使用,但是当我必须在其中对代码进行分类时,我的配置文件无法加载。说它找不到类型名称“System.Printing.PrintServer”或任何其他明确列出的类型。

失败后,我将其移至其自己的特定模块,然后将我的配置文件设置为在打开时导入该模块。但是,即使存储在自己的模块中,如果我明确列出任何属性的 .NET 类型,整个模块也无法加载。不管我是否添加或导入了类型/dll。

具体问题区域是这样的:

    [string]$Name
    [System.Printing.PrintServer]$Server
    [System.Printing.PrintQueue]$Queue
    [System.Printing.PrintTicket]$Ticket
    [System.Management.ManagementObject]$Unit
    [bool]$IsDefault

当我将它设置为这个时,所有“种类”的东西都可以工作,但是我所有的属性都有 _Object 类型,这没有帮助。

    [string]$Name
    $Server
    $Queue
    $Ticket
    $Unit
    $IsDefault


Add-Type -AssemblyName System.Printing
Add-Type -AssemblyName ReachFramework
Class PrinterObject
{
    [string]$Name
    [System.Printing.PrintServer]$Server
    [System.Printing.PrintQueue]$Queue
    [System.Printing.PrintTicket]$Ticket
    [System.Management.ManagementObject]$Unit
    [bool]$IsDefault

   PrinterObject([string]$Name)
    {
        #Add-Type -AssemblyName System.Printing
        #Add-Type -AssemblyName ReachFramework
        $this.Server = New-Object System.Printing.PrintServer -ArgumentList [System.Printing.PrintSystemDesiredAccess]::AdministrateServer
        $this.Queue =  New-Object System.Printing.PrintQueue (($this.Server), ($this.Server.GetPrintQueues() |
        Where-Object {$_.Name -match $Name} | Select-Object -ExpandProperty Name))

        $this.Ticket = $this.Queue.UserPrintTicket
        $this.Unit = Get-WmiObject -Query "SELECT * FROM Win32_Printer WHERE Name LIKE `"%$Name%`""
    }

    PrinterObject([string]$Name, [bool]$IsNetwork)
    {
        #Add-Type -AssemblyName System.Printing
        #Add-Type -AssemblyName ReachFramework
        if($IsNetwork -eq $true) {
        $this.Server = New-Object System.Printing.PrintServer ("\\Server")
        $this.Queue =  New-Object System.Printing.PrintQueue (($this.Server), ($this.Server.GetPrintQueues() |
        Where-Object {$_.Name -match $Name} | Select-Object -ExpandProperty Name))

        $this.Ticket = $this.Queue.UserPrintTicket
        $this.Unit = Get-WmiObject -Query "SELECT * FROM Win32_Printer WHERE Name LIKE `"%$Name%`""
        }
        else {
        $This.Server = New-Object System.Printing.PrintServer -argumentList [System.Printing.PrintSystemDesiredAccess]::AdministrateServer
        $this.Queue =  New-Object System.Printing.PrintQueue (($this.Server), ($this.Server.GetPrintQueues() |
        Where-Object {$_.Name -match $Name} | Select-Object -ExpandProperty Name))

        $this.Ticket = $this.Queue.UserPrintTicket
        $this.Unit = Get-WmiObject -Query "SELECT * FROM Win32_Printer WHERE Name LIKE `"%$Name%`"" }
    }
    [void]SetPrintTicket([int]$Copies, [string]$Collation, [string]$Duplex)
    {
        $this.Ticket.CopyCount = $Copies
        $this.Ticket.Collation = $Collation
        $this.Ticket.Duplexing = $Duplex
        $this.Queue.Commit()
    }

    [Object]GetJobs($Option)
    {
            if($Option -eq 1) { return $this.Queue.GetPrintJobInfoCollection() | Sort-Object -Property JobIdentifier | Select-Object -First 1}
            else { return $this.Queue.GetPrintJobInfoCollection() }
    }
    static [Object]ShowAllPrinters()
    {
        Return Get-WmiObject -Class Win32_Printer | Select-Object -Property Name, SystemName
    }

}

【问题讨论】:

  • 你能用“指定的类型”具体/字面意思吗?您提供的伪代码没有任何意义。您可能没有正确分离命名空间、类和值部分。没有人能从 [System.Object.SomeDotNetObject]::Enum 中看出这一点。
  • 问题类型属于哪里?是在标准.NET 程序集中,在GAC 中自定义程序集,通过路径加载的自定义程序集还是在动态程序集Add-Type -TypeDefinition ...
  • @PetSerAl 没关系——如果命名空间/类型可以在类定义之外解析,为什么不应该在类内部也适用?对我来说听起来像是一个错误。
  • @MathiasR.Jessen 我无法在我的电脑上重现这种行为,所以我要求提供更多详细信息以检查我是否遗漏了什么。
  • @MartinMaat 我已经更新了我正在使用的类的确切代码。我觉得这可能是类型未正确加载到 powershell 中的问题,因为如果我手动加载类型然后手动将此类键入到 shell 中,它工作得非常好

标签: .net powershell


【解决方案1】:

在执行脚本中的第一条语句之前,每个 PowerShell 脚本都会被完全解析。类定义中无法解析的类型名称标记被视为解析错误。要解决您的问题,您必须在解析类定义之前加载您的类型,因此类定义必须位于单独的文件中。例如:

Main.ps1:

Add-Type -AssemblyName System.Printing
Add-Type -AssemblyName ReachFramework

. $PSScriptRoot\Class.ps1

Class.ps1:

using namespace System.Management
using namespace System.Printing

Class PrinterObject
{
    [string]$Name
    [PrintServer]$Server
    [PrintQueue]$Queue
    [PrintTicket]$Ticket
    [ManagementObject]$Unit
    [bool]$IsDefault
}

另一种可能性是将Class.ps1 嵌入为字符串并使用Invoke-Expression 执行它。这会将类定义的解析延迟到类型可用的时间。

Add-Type -AssemblyName System.Printing
Add-Type -AssemblyName ReachFramework

Invoke-Expression @'
    using namespace System.Management
    using namespace System.Printing

    Class PrinterObject
    {
        [string]$Name
        [PrintServer]$Server
        [PrintQueue]$Queue
        [PrintTicket]$Ticket
        [ManagementObject]$Unit
        [bool]$IsDefault
    }
'@

【讨论】:

  • 所以。 . .我尝试了这个,我猜是一个非常奇怪的错误导致无限数量的 powershell 窗口快速弹出,以至于我的系统内存不足。 . .
  • 您对 dot-source 语句所在的文件进行了点源处理?就像把 ".$PSScriptRoot\Class.ps1" 放在一个名为 "Class.ps1" 的文件中?
  • 那么这种行为是一个错误,还是 PowerShell 的导入系统如此糟糕?
  • @tyteen4a03 using assembly 不会导致解析时程序集加载。程序集加载可能会导致执行任意代码,这可能是不希望的。并且从程序集元数据中提取类型而不加载它尚未实现,AFAIK。
  • @TNT eval 越过常量字符串怎么更危险,然后执行任意脚本文件?
【解决方案2】:

补充 PetSerAl's helpful answer:

using assembly 应该是正确的解决方案,但它在解析时的使用尚未在 PowerShell (Core) 7.2 中实现,[ 1] 因为它需要额外的工作以避免在加载程序集时意外执行任意代码的可能性。

GitHub issue #3641 已批准实施此功能,并且作为GitHub issue #6652 的一部分正在跟踪必要的工作。


[1] 由于 Windows PowerShell 不再积极开发并且只会看到关键的修复,因此问题不会在那里得到解决。

【讨论】:

    【解决方案3】:

    一个更好的解决方案(不仅仅是在一个字符串中调用整个类)是创建你的对象并将它们作为参数传递给类。例如,这运行良好:

    Add-Type -AssemblyName PresentationCore,PresentationFramework
    
    class ExampleClass {
        $object
    
        ExampleClass ($anotherClass) {
            $this.object = $anotherClass
        }
    
        [void] Show () {
            $this.object::Show('Hello')
        }
    }
    
    $y = [ExampleClass]::new([System.Windows.MessageBox])
    $y.Show()
    

    但是,如果您要做这样的事情,您可以期待Unable to find type [System.Windows.MessageBox].

    Add-Type -AssemblyName PresentationCore,PresentationFramework
    
    class ExampleClass2 {
        $object
    
        ExampleClass () {
            $this.object = [System.Windows.MessageBox]
        }
    
        [void] Show () {
            $this.object::Show('Hello')
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2022-11-11
      • 2015-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-08
      • 1970-01-01
      相关资源
      最近更新 更多