以下 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
互联网上的每个答案都写着我必须加载程序集 - 很确定我可以从错误消息中读取:-) - 问题是:
你如何加载程序集并使脚本工作?
LoadWithPartialName
已被弃用。 PowerShell V3 的推荐解决方案是使用 Add-Type
cmdlet,例如:
Add-Type -Path 'C:\Program Files\Microsoft SQL Server\110\SDK\Assemblies\Microsoft.SqlServer.Smo.dll'
有多个不同的版本,您可能想要选择一个特定的版本。 :-)
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
Out-Null
。
Add-Type -Path [...]; if (!$?) { Add-Type -Path [...] } elseif [...]
并不难。
现在大多数人都知道 System.Reflection.Assembly.LoadWithPartialName
已被弃用,但事实证明 Add-Type -AssemblyName Microsoft.VisualBasic
does not behave much better than 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'
为程序集提供的长名称称为强名称,它对版本和程序集都是唯一的,有时也称为全名。
但这留下了几个没有答案的问题:
如何使用给定的部分名称确定系统上实际加载的内容的强名称? [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
如果我希望我的脚本始终使用特定版本的 .dll 但我无法确定它的安装位置,我如何确定 .dll 中的强名称是什么? [System.Reflection.AssemblyName]::GetAssemblyName($Path).FullName;
或者:
Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
如果我知道强名称,我如何确定 .dll 路径? [Reflection.Assembly]::Load('Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a').Location;而且,类似地,如果我知道我正在使用的类型名称,我怎么知道它来自哪个程序集? [Reflection.Assembly]::GetAssembly([Type]).Location [Reflection.Assembly]::GetAssembly([Type]).FullName 如何查看可用的程序集?
我建议GAC PowerShell module。 Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName
效果很好。
如何查看 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 是开源的。对于该版本,您可以跳过上述步骤,直接查看代码online in their GitHub repository。但是,我不能保证该代码与任何其他版本的 PowerShell 匹配。
更新 2: Powershell 7+ 似乎不再具有哈希表查找功能。相反,他们使用 a LoadAssemblyHelper()
method,评论称其为 LoadWithPartialName 的“最接近的可能”。基本上,他们这样做:
loadedAssembly = Assembly.Load(new AssemblyName(assemblyName));
现在,评论还说“用户可以只说 Add-Type -AssemblyName Forms
(而不是 System.Windows.Forms)”。但是,这不是我在 Windows 10 2004 上的 Powershell v7.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 相同的问题,如果您安装了多个,可能会加载错误版本的程序集。
Add-Type
或 LoadWithPartialName()
,但您需要注意,前者不会在各个版本之间保持 100% 一致,而后者是一种过时的方法。换句话说,.Net 希望您关心您加载的库的版本。
Add-Type -Path
,这是提到的第二个代码,或者 Assembly.LoadFrom()
可以为您解决依赖关系(据我所知,是 Add-Type -Path
使用的)。唯一应该使用 Assembly.LoadFile()
的情况是,如果您需要加载具有相同标识但路径不同的多个程序集。这是一个奇怪的情况。
如果要在 PowerShell 会话期间加载程序集而不锁定它,请使用以下命令:
$bytes = [System.IO.File]::ReadAllBytes($storageAssemblyPath)
[System.Reflection.Assembly]::Load($bytes)
其中 $storageAssemblyPath
是程序集的文件路径。
如果您需要清理会话中的资源,这将特别有用。例如在部署脚本中。
以下是一些博客文章,其中包含大量在 PowerShell v1、v2 和 v3 中加载程序集的示例。
方式包括:
从源文件动态
从程序集中动态地
使用其他代码类型,即 F#
v1.0 How To Load .NET Assemblies In A PowerShell Session
v2.0 Using CSharp (C#) code in PowerShell scripts 2.0
v3.0 Using .NET Framework Assemblies in Windows PowerShell
您可以加载整个 *.dll 程序集
$Assembly = [System.Reflection.Assembly]::LoadFrom("C:\folder\file.dll");
没有一个答案对我有帮助,所以我发布了对我有用的解决方案,我所要做的就是导入 SQLPS 模块,当我偶然运行 Restore-SqlDatabase 命令并开始工作时,我意识到了这一点,这意味着该程序集以某种方式在该模块中被引用。
赶紧跑:
Import-module SQLPS
注意:感谢 Jason 注意到 SQLPS 已被弃用
而是运行:
Import-Module SqlServer
或者
Install-Module SqlServer
sqlps
以支持模块 sqlserver
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
为我工作。
您可以使用 LoadWithPartialName
。但是,正如他们所说,这已被弃用。
您确实可以使用 Add-Type
,除了其他答案之外,如果您不想指定 .dll 文件的完整路径,您可以简单地执行以下操作:
Add-Type -AssemblyName "Microsoft.SqlServer.Management.SMO"
对我来说,这返回了一个错误,因为我没有安装 SQL Server(我猜),但是,以同样的想法,我能够加载 Windows 窗体程序集:
Add-Type -AssemblyName "System.Windows.Forms"
您可以在 MSDN 站点上找到属于特定类的确切程序集名称:
https://i.stack.imgur.com/6PuKE.png
确保您按顺序安装了以下功能
SQL Server 的 Microsoft System CLR 类型 Microsoft SQL Server 共享管理对象 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"
在顶部添加程序集引用。
#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
This API is now obsolete.
当然,这并不能阻止人们使用它。LoadWithPartialName
已被弃用在技术上是正确的,但原因(如 blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx 中所述)显然不适用于交互式 Powershell 会话。我建议您添加一个注释,说明该 API 适用于交互式 Powershell 使用。add-Type -Path 'C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Microsoft.SqlServer.Smo.dll'