如何 import/load 一个 .dll 文件在 PowerShell 脚本中使用而不出现 "TypeNotFound" 错误?

How can I import/load a .dll file to use in a PowerShell script without getting "TypeNotFound" error?

 [void] CreateSession() {
    try {
        # Load WinSCP .NET assembly
        Add-Type -Path (Join-Path $PSScriptRoot "\winscp\WinSCPnet.dll")
        # Setup session options
        $this.sessionOptions = New-Object WinSCP.SessionOptions -Property @{
            Protocol = [WinSCP.Protocol]::Sftp 

在上面的代码部分中,我遇到了关于“[WinSCP.Protocol]”的“TypeNotFound”错误消息。

  14 |                  Protocol = [WinSCP.Protocol]::Sftp
     |                              ~~~~~~~~~~~~~~~
     | Unable to find type [WinSCP.Protocol].

.dll文件可以正确加载,我之前已经验证过了。我知道正在发生的事情是 PowerShell“解析器”抛出错误,因为它在加载时无法识别 WinSCP 库。我曾尝试添加模块和清单,但我找不到如何正确执行此操作的简单示例。另外,我是 运行 PowerShell 5.x 还是 7.x 都没关系。我想要的只是加载此 DLL,以便我可以从中使用 classes/functions。为什么在 PowerShell 中加载 DLL 如此困难?

我需要做什么才能让这个 WinSCP DLL 在运行时加载而不抛出错误?

注意可能的重复项

几年前有人在这个网站上问过一个非常相似的问题,但没有答案。

关于建议重复的注意事项

我正在寻找有关如何将 DLL 文件加载到脚本中的真实示例。链接的问题没有适当地做到这一点。为什么我需要创建一个清单模块来导入 DLL?

tl;dr:

  • 问题源于试图通过 class 定义 中引用刚刚加载的 WinSCP 类型 ,例如[WinSCP.Protocol],如下所述。

  • 通过完全不使用 class 而是使用 functions 可以 绕过 问题。


I am looking for a real example for how to load a DLL file into a script.

Add-Type -Path / -LiteralPath,如您的代码所示:

它加载指定的 .NET 程序集并使其 public 类型在调用会话中可用。

但是,由于您使用的是 class 定义 试图通过类型文字(例如[WinSCP.Protocol])从您正在加载的程序集中从同一脚本文件引用类型, class 定义 失败 :

在脚本文件 解析时间 ,在 class 定义中通过类型文字引用的所有类型必须已经加载到会话中,从 PowerShell 7.2 开始。[1]

解决方法

  • 提供两种解决方案:

  • 提供了一个简单的 双脚本 解决方案:首先加载依赖程序集,然后调用(点源)另一个根据依赖程序集的类型定义 class。


并不总是需要解决方法,即如果您避免使用,例如[WinSCP.Protocol]

  • 关于 [WinSCP.SessionOptions],您已经使用 New-Object WinSCP.SessionOptions 而不是更现代的 (PSv5+) [WinSCP.SessionOptions]::new()

  • 您也可以通过简单地使用 string - 'Sftp' 来避免 [WinSCP.Protocol]::Sftp 枚举值 - 至少对于显示的代码片段可以解决您的问题;这是一个简化的例子:

class Foo {
  # Note: Do NOT use [WinSCP.SessionOptions] here.
  [object] $sessionOptions 
  [void] CreateSession() {
      # Load WinSCP .NET assembly
      Add-Type -LiteralPath (Join-Path $PSScriptRoot "\winscp\WinSCPnet.dll")
      # Set up session options
      # Note the use of *string* 'Sftp' in lieu of [WinSCP.Protocol]::Sftp
      $this.sessionOptions = New-Object WinSCP.SessionOptions -Property @{
        Protocol = 'Sftp'  
      }
  }
}

现在您可以像往常一样实例化 [Foo] - 使用 New-Object Foo 或者最好使用 [Foo]::new() (一旦 [Foo] 本身被成功定义,就可以通过类型文字引用 itoutside class 定义)。


[1] 类 是相对较晚添加到 PowerShell 语言中的,不幸的是,仍有许多问题需要解决 - 请参阅 [中的未决问题列表=44=]。但是请注意,与 C# classes 等功能相媲美从来都不是目标。

最简单的解决方案,也是我决定使用的解决方案,就是在 PowerShell 中只使用函数而不是 类。

只使用函数,而不是在 PowerShell 中 类。