如何在 PowerShell 中加载程序集?

下面的 PowerShell 代码

#Get a server object which corresponds to the default instance
$srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
... rest of the script ...

提供以下错误消息:

New-Object : Cannot find type [Microsoft.SqlServer.Management.SMO.Server]: make sure
the assembly containing this type is loaded.
At C:\Users\sortelyn\ ... \tools\sql_express_backup\backup.ps1:6  char:8
+ $srv = New-Object -TypeName Microsoft.SqlServer.Management.SMO.Server
+        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidType: (:) [New-Object], PSArgumentException
+ FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

互联网上的每一个答案都写着我必须加载这个程序集——当然我可以从错误信息中读出这一点: ——)——问题是:

如何加载程序集并使脚本工作?

313298 次浏览
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

PowerShell V3推荐的解决方案是使用 Add-Type cmdlet,例如:

Add-Type -Path 'C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll'

有多个不同的版本,您可能需要选择一个特定的版本。 : -)

下面是一些博客文章,其中有许多在 PowerShell v1、 v2和 v3中加载程序集的方法示例。

方法包括:

  • 动态地从源文件
  • 从程序集动态地
  • 使用其他代码类型,例如 F #

V1.0 如何在 PowerShell 会话中加载.NET 程序集
V2.0 在 PowerShell 脚本2.0中使用 CSharp (C #)代码
V3.0 < a href = “ https://blogs.technet.microsoft.com/heyscriptingguy/2013.01/21/Using-NET-Framework-Assembly-in-windows-powershell/”rel = “ norefrer”> 在 Windows PowerShell 中使用.NET Framework Assembly

可以用以下命令加载整个 * . dll 程序集

$Assembly = [System.Reflection.Assembly]::LoadFrom("C:\folder\file.dll");

现在大多数人都知道 System.Reflection.Assembly.LoadWithPartialName已经被废弃了,但事实证明 Add-Type -AssemblyName Microsoft.VisualBasic 表现并不比 LoadWithPartialName好多少:

而不是尝试在 在您的系统中,[ Add-Type ]查看一个静态的内部表来转换 「部分名称」改为「全名」。

如果您的“部分名称”没有出现在他们的表中,那么您的脚本将出现 失败。

如果程序集的多个版本已安装在 计算机,没有智能算法可以在它们之间进行选择。 你会得到任何一个出现在他们的表,可能 那个又老又过时的。

如果您安装的版本都比过时的版本更新 在表中,您的脚本将失败。

Add-Type 没有像下面这样的“部分名称”智能解析器 .LoadWithPartialNames.

微软的.Net 团队说你的 事实上应该做的事情是这样的:

Add-Type -AssemblyName 'Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

或者,如果你知道路径,像这样的东西:

Add-Type -Path 'C:\WINDOWS\Microsoft.Net\assembly\GAC_MSIL\Microsoft.VisualBasic\v4.0_10.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualBasic.dll'

为程序集提供的长名称称为 好名字,它对于版本和程序集都是唯一的,有时也称为全名。

但这留下了几个问题没有得到解答:

  1. 如何用给定的部分名称确定系统上实际加载的内容的强名称?

    [System.Reflection.Assembly]::LoadWithPartialName($TypeName).Location; [System.Reflection.Assembly]::LoadWithPartialName($TypeName).FullName;

这些措施也应该奏效:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. 如果希望脚本始终使用。但是我不能确定它的安装位置,如何从。DLL?

    [System.Reflection.AssemblyName]::GetAssemblyName($Path).FullName;

或者:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. 如果我知道强名称,如何确定. dll 路径?

    [Reflection.Assembly]::Load('Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a').Location;

  2. 同样,如果我知道要使用的类型名称,我怎么知道它来自哪个程序集呢?

    [Reflection.Assembly]::GetAssembly([Type]).Location [Reflection.Assembly]::GetAssembly([Type]).FullName

  3. 如何查看可用的程序集?

我建议 GAC PowerShell 模块Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName工作得很好。

  1. 如何查看 Add-Type使用的列表?

这有点复杂。我可以描述如何为任何版本的 PowerShell 访问它。Net 反射器(请参阅下面 PowerShell Core 6.0的更新)。

首先,找出 Add-Type来自哪个库:

Get-Command -Name Add-Type | Select-Object -Property DLL

用反射器打开生成的 DLL。我使用了 ILSpy,因为它是 FLOSS,但是任何 C # 反射器都应该可以工作。打开那个库,查看 Microsoft.Powershell.Commands.Utility。在 Microsoft.Powershell.Commands下,应该有 AddTypeCommand

在该代码清单中,有一个私有类 InitializeStrongNameDictionary()。列出将短名称映射到强名称的字典。我在图书馆里看了将近750个条目。

更新: 现在 PowerShell Core 6.0是开源的。对于该版本,您可以跳过上述步骤,直接查看代码 在他们的 GitHub 知识库里。但是,我不能保证这些代码与 PowerShell 的任何其他版本相匹配。

更新2: Powershell 7 + 似乎不再有哈希表查找了。相反,他们使用 一种 LoadAssemblyHelper()方法,注释中称之为“最接近 LoadWithPartialName 的近似值”。基本上,他们是这样做的:

loadedAssembly = Assembly.Load(new AssemblyName(assemblyName));

现在,评论还说“用户可以直接说 Add-Type -AssemblyName Forms (而不是 System. Windows. Forms)”然而,这并不是我在2004年的 Powershell 版本7.0.3中看到的。

# Returns an error
Add-Type -AssemblyName Forms


# Returns an error
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('Forms'))


# Works fine
Add-Type -AssemblyName System.Windows.Forms


# Works fine
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('System.Windows.Forms'))

所以这些评论看起来有点神秘。

当没有指定版本或公钥令牌时,我不确切地知道 Assembly.Load(AssemblyName)中的逻辑是什么。我认为,如果您安装了多个程序集,那么与 LoadWithPartialName 相同的问题可能会导致加载错误的程序集版本。

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")为我工作。

如果要加载程序集 而不在 PowerShell 会话期间锁定它,请使用以下命令:

$bytes = [System.IO.File]::ReadAllBytes($storageAssemblyPath)
[System.Reflection.Assembly]::Load($bytes)

其中 $storageAssemblyPath是程序集的文件路径。

如果需要清理会话中的资源,这尤其有用。例如,在部署脚本中。

您可以使用 LoadWithPartialName。但是,正如他们所说的那样,这是不推荐的。

您确实可以使用 Add-Type,除了其他答案之外,如果您不想指定。Dll 文件,你只需要做:

Add-Type -AssemblyName "Microsoft.SqlServer.Management.SMO"

对我来说,这返回了一个错误,因为我没有安装 SQLServer (我猜) ,但是,带着同样的想法,我能够加载 Windows 窗体程序集:

Add-Type -AssemblyName "System.Windows.Forms"

您可以在 MSDN 站点上找到属于特定类的精确程序集名称:

Example of finding out assembly name belonging to a particular class

在顶部添加程序集引用。

#Load the required assemblies SMO and SmoExtended.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SmoExtended") | Out-Null

请确保您有以下功能是按顺序安装的

  1. 用于 SQLServer 的 MicrosoftSystemCLR 类型
  2. Microsoft SQL Server 共用管理对象
  3. 微软 Windows PowerShell 扩展

此外,您可能需要加载

Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll"
Add-Type -Path "C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.SqlWmiManagement.dll"

没有一个答案对我有帮助,所以我发布了适合我的解决方案,我所要做的就是导入 SQLPS 模块,当我偶然运行 Restore-SqlDatabase 命令并开始工作时,我意识到了这一点,这意味着程序集以某种方式被引用在那个模块中。

快跑:

Import-module SQLPS

注意: 谢谢 Jason 注意到 SQLPS 是不推荐的

而是运行:

Import-Module SqlServer

或者

Install-Module SqlServer