162

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

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

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

4

11 回答 11

189

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

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

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

于 2012-10-16T22:11:44.477 回答
78
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo")
于 2012-10-16T20:43:48.407 回答
52

Most people know by now that System.Reflection.Assembly.LoadWithPartialName is deprecated, but it turns out that Add-Type -AssemblyName Microsoft.VisualBasic does not behave much better than LoadWithPartialName:

Rather than make any attempt to parse your request in the context of your system, [Add-Type] looks at a static, internal table to translate the "partial name" to a "full name".

If your "partial name" doesn't appear in their table, your script will fail.

If you have multiple versions of the assembly installed on your computer, there is no intelligent algorithm to choose between them. You are going to get whichever one appears in their table, probably the older, outdated one.

If the versions you have installed are all newer than the obsolete one in the table, your script will fail.

Add-Type has no intelligent parser of "partial names" like .LoadWithPartialNames.

What Microsoft's .Net teams says you're actually supposed to do is something like this:

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

Or, if you know the path, something like this:

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

That long name given for the assembly is known as the strong name, which is both unique to the version and the assembly, and is also sometimes known as the full name.

But this leaves a couple questions unanswered:

  1. How do I determine the strong name of what's actually being loaded on my system with a given partial name?

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

These should also work:

Add-Type -AssemblyName $TypeName -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. If I want my script to always use a specific version of a .dll but I can't be certain of where it's installed, how do I determine what the strong name is from the .dll?

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

Or:

Add-Type $Path -PassThru | Select-Object -ExpandProperty Assembly | Select-Object -ExpandProperty FullName -Unique
  1. If I know the strong name, how do I determine the .dll path?

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

  2. And, on a similar vein, if I know the type name of what I'm using, how do I know what assembly it's coming from?

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

  3. How do I see what assemblies are available?

I suggest the GAC PowerShell module. Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName works pretty well.

  1. How can I see the list that Add-Type uses?

This is a bit more complex. I can describe how to access it for any version of PowerShell with a .Net reflector (see the update below for PowerShell Core 6.0).

First, figure out which library Add-Type comes from:

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

Open the resulting DLL with your reflector. I've used ILSpy for this because it's FLOSS, but any C# reflector should work. Open that library, and look in Microsoft.Powershell.Commands.Utility. Under Microsoft.Powershell.Commands, there should be AddTypeCommand.

In the code listing for that, there is a private class, InitializeStrongNameDictionary(). That lists the dictionary that maps the short names to the strong names. There's almost 750 entries in the library I've looked at.

Update: Now that PowerShell Core 6.0 is open source. For that version, you can skip the above steps and see the code directly online in their GitHub repository. I can't guarantee that that code matches any other version of PowerShell, however.

Update 2: Powershell 7+ does not appear to have the hash table lookup any longer. Instead they use a LoadAssemblyHelper() method which the comments call "the closest approximation possible" to LoadWithPartialName. Basically, they do this:

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

Now, the comments also say "users can just say Add-Type -AssemblyName Forms (instead of System.Windows.Forms)". However, that's not what I see in Powershell v7.0.3 on Windows 10 2004.

# 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'))

So the comments appear to be a bit of a mystery.

I don't know exactly what the logic is in Assembly.Load(AssemblyName) when there is no version or public key token specified. I would expect that this has many of the same problems that LoadWithPartialName does like potentially loading the wrong version of the assembly if you have multiple installed.

于 2016-02-29T14:40:53.140 回答
26

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

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

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

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

于 2016-05-26T18:23:42.300 回答
16

以下是一些博客文章,其中包含大量在 PowerShell v1、v2 和 v3 中加载程序集的示例。

方式包括:

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

v1.0如何在 PowerShell 会话中加载 .NET 程序集
v2.0在 PowerShell 脚本中使用 CSharp (C#) 代码 2.0
v3.0在 Windows PowerShell 中使用 .NET Framework 程序集

于 2013-03-12T09:14:06.177 回答
14

您可以加载整个 *.dll 程序集

$Assembly = [System.Reflection.Assembly]::LoadFrom("C:\folder\file.dll");
于 2013-03-12T13:31:35.220 回答
4

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

赶紧跑:

Import-module SQLPS

注意:感谢 Jason 注意到 SQLPS 已被弃用

而是运行:

Import-Module SqlServer

或者

Install-Module SqlServer
于 2017-11-08T20:33:55.373 回答
2

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

于 2016-04-01T12:07:44.023 回答
2

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

您确实可以使用Add-Type,除了其他答案之外,如果您不想指定 .dll 文件的完整路径,您可以简单地执行以下操作:

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

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

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

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

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

于 2016-07-12T08:28:28.383 回答
2

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

  1. SQL Server 的 Microsoft System 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"
于 2017-11-07T00:37:13.043 回答
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
于 2017-09-27T11:07:47.267 回答