如何在PowerShell中加载程序集?


153

以下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

互联网上的每个答案都写道我必须加载程序集-可以肯定的是,我可以从错误消息中读取它:-)-问题是:

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

Answers:


179

LoadWithPartialName已不推荐使用。对于PowerShell V3,推荐的解决方案是使用Add-Typecmdlet,例如:

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

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


1
好的,我使用PowerShell3-这些包含命令似乎非常复杂。我只希望“包含文件名”之类的东西。
巴克斯特2012年

6
PowerShell不区分大小写(除非使用-cmatch,-ceq等运算符告诉它区分大小写)。因此,命令名称和参数的大小写无关紧要。
基思·希尔

5
是。msdn.microsoft.com/zh-cn/library/12xc5368(v=vs.110).aspx 请参阅顶部的注释- This API is now obsolete. 当然,这并不能阻止人们使用它。
基思·希尔

2
尽管从技术上讲它LoadWithPartialName已被弃用,但其原因(如blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx所述)显然不适用于交互式Powershell会话。我建议您添加一点说明,该API适用于交互式Powershell使用。
Micha Wiedenmann

大多数时候,我对SMO组件没有任何问题,但是有时我需要取消powershell,当我这样做时,我开始遇到SMO加载问题。添加add-type -Path可以解决此问题。
Nicolas de Fontenay

73
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")

8
这太有用了,无法替换而已弃用!我的团队混合使用了2008年和2012年的客户端工具。这是使我的PowerShell脚本适用于所有团队的唯一方法,而不会包括笨拙的版本后备逻辑。
伊恩·塞缪尔·麦克莱恩

4
Out-Null如果您不希望GAC回显内容,则可以将输出传递给。
伊恩·塞缪尔·麦克莱恩

3
@Baxter-您应该接受此答案或Keith的答案,并将此问题标记为已回答。
Jaykul

3
我使用[void] [System.Reflection.Assembly] :: LoadWithPartialName(“ Microsoft.SqlServer.Smo”)
Soeren L. Nielsen

@IainElder“笨拙的版本回退逻辑”您说直到遇到版本不兼容!说起来并不难Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...]
培根钻头

44

到目前为止,大多数人都知道System.Reflection.Assembly.LoadWithPartialName已弃用该方法,但事实证明它的Add-Type -AssemblyName Microsoft.VisualBasic 行为并不比LoadWithPartialName

[Add-Type]不会尝试在系统的上下文中解析您的请求,而是查看静态的内部表,以将“部分名称”转换为“全名”。

如果您的“部分名称”未出现在其表中,则脚本将失败。

如果您的计算机上安装了多个版本的程序集,则没有智能算法可供选择。您将得到他们表中出现的任何一个,可能是较旧的,过时的。

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

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

微软说你实际上应该做的是这样的:

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'

为程序集提供的长名称为Strong name,它是版本和程序集唯一的,有时也称为全名。

但这留下了几个问题无法回答:

  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的特定版本,但是不确定安装位置,那么如何确定.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使用的列表?

这有点复杂。我可以描述如何使用.Net反射器访问任何版本的PowerShell(对于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相匹配。


第三个未解决的问题:如果我不想要求特定的版本怎么办?
jpmc26,2017年

1
@ jpmc26好吧,您可以使用Add-TypeLoadWithPartialName(),但是您需要注意,前者在各个版本中不会保持100%一致,而后者是一种过时的方法。换句话说,.Net希望您关心加载的库的版本。
培根钻头

@BaconBits jpmc26问题的完整答案是,取决于您使用的是PowerShell 5还是PowerShell 6,加载的程序集可能有所不同。JSON.NET在Azure PS功能中存在此问题。
约翰·扎布罗斯基

@BaconBits这是对PowerShell的真正深入了解。你应该写一本书。
John Zabroski

1
@KolobCanyon因为在那种情况下,您通常应该使用Add-Type -Path,它是提到的第二个代码,或者Assembly.LoadFrom()可以为您解决依赖关系(据我所知,这是Add-Type -Path使用什么)。唯一应该使用的时间Assembly.LoadFile()是是否需要加载多个具有相同标识但路径不同的程序集。那是一个奇怪的情况。
培根

23

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

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

$storageAssemblyPath程序集的文件路径在哪里。

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


1
👍太棒了。因为在Visual Studio中,调试Powershell时,PS会话在执行后(通过PowerShellToolsProcessHost)挂起。这种方法可以解决此问题。谢谢。
CJBS


10

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

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

3

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

赶紧跑:

Import-module SQLPS

注意:感谢Jason指出已弃用SQLPS

而是运行:

Import-Module SqlServer

要么

Install-Module SqlServer

2
对于使用此方法的任何人,sqlps不建议使用模块的FYIsqlserver
Jason


2

您可以使用LoadWithPartialName。但是,正如他们所说,这已被弃用。

您确实可以与一起使用Add-Type,除了其他答案,如果您不想指定.dll文件的完整路径,则只需执行以下操作:

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

对我来说,这返回了一个错误,因为我没有安装SQL Server(我想),但是,出于同样的想法,我能够加载Windows Forms程序集:

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

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

查找属于特定类的程序集名称的示例


2

确保按顺序安装了以下功能

  1. SQL Server的Microsoft系统CLR类型
  2. Microsoft SQL Server共享管理对象
  3. Microsoft 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"

我花了一个星期的时间尝试加载程序集,但从加载它们的语句中看不到任何输出,但是当我尝试使用它时,我得到了错误。当我安装这三件事时,它就起作用了。-谢谢
pparas

0

在顶部添加程序集引用。

#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

你能举一个例子吗?
end.anaconda

1
您只需要在powershell脚本的开头添加它即可。例如:创建数据库备份:[System.Reflection.Assembly] :: LoadWithPartialName(“ Microsoft.SqlServer.SMO”)| 无空[System.Reflection.Assembly] :: LoadWithPartialName(“ Microsoft.SqlServer.SmoExtended”)| 无空$ SQLServer =读取主机-提示'SQL Server名称(可选)'IF([string] :: IsNullOrWhitespace($ SQLServer)){$ SQLServer =“ XXX”;} $ SQLDBName =读取主机-提示' SQL数据库名称(可选)'IF([string] :: IsNullOrWhitespace($ SQLDBName)){$ SQLDBName =“ XXX”;} $ SQLLogin =读取主机-提示“登录”
Amrita Basu,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.