Powershell:使用PS 5类时无法找到类型 [英] Powershell: Unable to find type when using PS 5 classes

查看:156
本文介绍了Powershell:使用PS 5类时无法找到类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在WinSCP Powershell Assembly中使用PS中的类.在其中一种方法中,我使用的是WinSCP中的各种类型.

I'm using classes in PS with WinSCP Powershell Assembly. In one of the methods I'm using various types from WinSCP.

只要我已经添加了程序集,此方法就可以正常工作-但是,由于Powershell在使用类时读取脚本的方式(我认为是?),因此在加载程序集之前会引发错误.

This works fine as long as I already have the assembly added - however, because of the way Powershell reads the script when using classes (I assume?), an error is thrown before the assembly could be loaded.

实际上,即使我将Write-Host放在顶部,它也不会加载.

In fact, even if I put a Write-Host at the top, it will not load.

在解析文件的其余部分之前,是否有任何方法可以强制某些内容运行?

Is there any way of forcing something to run before the rest of the file is parsed?

Transfer() {
    $this.Logger = [Logger]::new()
    try {

        Add-Type -Path $this.Paths.WinSCP            
        $ConnectionType = $this.FtpSettings.Protocol.ToString()
        $SessionOptions = New-Object WinSCP.SessionOptions -Property @{
            Protocol = [WinSCP.Protocol]::$ConnectionType
            HostName = $this.FtpSettings.Server
            UserName = $this.FtpSettings.Username
            Password = $this.FtpSettings.Password
        }

导致这样的错误:

Protocol = [WinSCP.Protocol]::$ConnectionType
Unable to find type [WinSCP.Protocol].

推荐答案

如您所见, PowerShell拒绝运行包含引用当时不可用(尚未加载)类型的类定义的脚本 strong>-脚本解析阶段失败.

As you've discovered, PowerShell refuses to run scripts that contains class definitions that reference then-unavailable (not-yet-loaded) types - the script parsing stage fails.

  • As of PSv5.1, even a using assembly statement at the top of a script does not help in this case, because in your case the type is referenced in the context of a PS class definition - this may get fixed in PowerShell Core, however; the required work is being tracked in this GitHub issue.

正确的解决方案是创建脚本 module (*.psm1),该脚本的关联清单(*.psd1)将包含引用类型的程序集声明为 prerequisite > ,通过RequiredAssemblies键.

The proper solution is to create a script module (*.psm1) whose associated manifest (*.psd1) declares the assembly containing the referenced types a prerequisite, via the RequiredAssemblies key.

如果不能使用模块,请参见底部的替代解决方案.

这是一个简化的演练:

创建测试模块tm如下:

Create test module tm as follows:

  • 创建模块文件夹./tm并在其中显示(*.psd1):

# Create module folder
mkdir ./tm

# Create manifest file that declares the WinSCP assembly a prerequisite.
# Modify the path to the assembly as needed; you may specify a relative path, but
# note that the path must not contain variable references (e.g., $HOME).
New-ModuleManifest ./tm/tm.psd1 -RootModule tm.psm1 `
  -RequiredAssemblies C:\path\to\WinSCPnet.dll

  • 在模块文件夹中创建脚本模块文件(*.psm1):

    使用您的类定义创建文件./tm/tm.psm1;例如:

    Create file ./tm/tm.psm1 with your class definition; e.g.:

    class Foo {
      # Simply return the full name of the WinSCP type.
      [string] Bar() {
        return [WinSCP.Protocol].FullName
      }
    }
    

    注意:在现实世界中,模块通常放置在$env:PSMODULEPATH中定义的标准位置之一中,因此该模块只能由 name 引用,而无需指定(相对)路径.

    Note: In the real world, modules are usually placed in one of the standard locations defined in $env:PSMODULEPATH, so that the module can be referenced by name only, without needing to specify a (relative) path.

    使用模块:

    PS> using module ./tm; (New-Object Foo).Bar()
    WinSCP.Protocol
    

    using module语句导入模块,并且-与Import-Module不同- 还使模块中定义的 class 可用于当前会话.

    The using module statement imports the module and - unlike Import-Module - also makes the class defined in the module available to the current session.

    由于导入模块隐式加载了WinSCP程序集,这要归功于模块清单中的RequiredAssemblies键,实例化引用程序集类型的类Foo成功.

    Since importing the module implicitly loaded the WinSCP assembly thanks to the RequiredAssemblies key in the module manifest, instantiating class Foo, which references the assembly's types, succeeded.

    如果您的用例不允许使用模块,则可以在紧急情况下使用Invoke-Expression,但是请注意,通常最好避免使用Invoke-Expression,以确保健壮性和可靠性.为了避免安全风险 [1]

    If your use case doesn't allow the use of modules, you can use Invoke-Expression in a pinch, but note that it's generally better to avoid Invoke-Expression in the interest of robustness and so as to avoid security risks[1] .

    # Adjust this path as needed.
    Add-Type -LiteralPath C:\path\to\WinSCPnet.dll
    
    # By placing the class definition in a string that is invoked at *runtime*
    # via Invoke-Expression, *after* the WinSCP assembly has been loaded, the
    # class definition succeeds.
    Invoke-Expression @'
    class Foo {
      # Simply return the full name of the WinSCP type.
      [string] Bar() {
        return [WinSCP.Protocol].FullName
      }
    }
    '@
    
    (New-Object Foo).Bar()
    


    [1]在这种情况下并不重要,但是通常,考虑到Invoke-Expression可以调用存储在字符串中的 any 命令并应用它完全不受您控制的字符串可能会导致执行恶意命令. 此警告类似地适用于其他语言,例如Bash的内置eval命令.


    [1] It's not a concern in this case, but generally, given that Invoke-Expression can invoke any command stored in a string, applying it to strings not fully under your control can result in the execution of malicious commands. This caveat applies to other language analogously, such as to Bash's built-in eval command.

    这篇关于Powershell:使用PS 5类时无法找到类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆