简体   繁体   English

如何在 PowerShell 中加载程序集?

[英]How to load assemblies in PowerShell?

The following PowerShell code以下 PowerShell 代码

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

Gives the following error message:给出以下错误消息:

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

Every answer on the Internet writes that I have to load the assembly - well sure I can read that from the error message :-) - the question is:互联网上的每个答案都写到我必须加载程序集 - 当然我可以从错误消息中读取它:-) - 问题是:

How do you load the assembly and make the script work?你如何加载程序集并使脚本工作?

LoadWithPartialName has been deprecated. LoadWithPartialName已被弃用。 The recommended solution for PowerShell V3 is to use the Add-Type cmdlet eg: PowerShell V3 的推荐解决方案是使用Add-Type cmdlet,例如:

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

There are multiple different versions and you may want to pick a particular version.有多个不同的版本,您可能想要选择一个特定的版本。 :-) :-)

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

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 :大多数人现在都知道System.Reflection.Assembly.LoadWithPartialName已被弃用,但事实证明Add-Type -AssemblyName Microsoft.VisualBasic 表现并不比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". [Add-Type] 不是尝试在系统上下文中解析您的请求,而是查看静态内部表,将“部分名称”转换为“全名”。

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 . Add-Type 没有像.LoadWithPartialNames这样的“部分名称”的智能解析器。

What Microsoft's .Net teams says you're actually supposed to do is something like this:微软的 .Net 团队说你实际上应该做的是这样的:

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?如果我希望我的脚本始终使用 .dll 的特定版本,但我不能确定它的安装位置,我如何确定 .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?如果我知道强名称,我如何确定 .dll 路径?

    [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 [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 .我建议使用GAC PowerShell 模块 Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName works pretty well. Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName效果很好。

  1. How can I see the list that Add-Type uses?如何查看Add-Type使用的列表?

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).我可以描述如何使用 .Net 反射器为任何版本的 PowerShell 访问它(请参阅下面的 PowerShell Core 6.0 更新)。

First, figure out which library Add-Type comes from:首先,弄清楚Add-Type来自哪个库:

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

Open the resulting DLL with your reflector.使用反射器打开生成的 DLL。 I've used ILSpy for this because it's FLOSS, but any C# reflector should work.我为此使用了ILSpy ,因为它是 FLOSS,但任何 C# 反射器都应该可以工作。 Open that library, and look in Microsoft.Powershell.Commands.Utility .打开该库,然后查看Microsoft.Powershell.Commands.Utility Under Microsoft.Powershell.Commands , there should be AddTypeCommand .Microsoft.Powershell.Commands ,应该有AddTypeCommand

In the code listing for that, there is a private class, InitializeStrongNameDictionary() .在代码清单中,有一个私有类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.我看过的图书馆里有将近 750 个条目。

Update: Now that PowerShell Core 6.0 is open source.更新:现在 PowerShell Core 6.0 是开源的。 For that version, you can skip the above steps and see the code directly online in their GitHub repository .对于该版本,您可以跳过上述步骤,直接在其 GitHub 存储库中在线查看代码。 I can't guarantee that that code matches any other version of PowerShell, however.但是,我不能保证该代码与任何其他版本的 PowerShell 匹配。

Update 2: Powershell 7+ does not appear to have the hash table lookup any longer.更新 2: Powershell 7+ 似乎不再具有哈希表查找功能。 Instead they use a LoadAssemblyHelper() method which the comments call "the closest approximation possible" to LoadWithPartialName.相反,他们使用LoadAssemblyHelper()方法,评论称该方法为 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)".现在,评论还说“用户可以只说Add-Type -AssemblyName Forms (而不是 System.Windows.Forms)”。 However, that's not what I see in Powershell v7.0.3 on Windows 10 2004.但是,这不是我在 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'))

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.当没有指定版本或公钥令牌时,我不知道Assembly.Load(AssemblyName)的逻辑到底是什么。 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.我希望这有许多与 LoadWithPartialName 相同的问题,如果您安装了多个程序集,可能会加载错误版本的程序集。

If you want to load an assembly without locking it during the duration of the PowerShell session , use this:如果要在 PowerShell 会话期间加载程序集而不锁定它,请使用以下命令:

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

Where $storageAssemblyPath is the file path of your assembly.其中$storageAssemblyPath是程序集的文件路径。

This is especially useful if you need to clean up the resources within your session.如果您需要清理会话中的资源,这尤其有用。 For example in a deployment script.例如在部署脚本中。

Here are some blog posts with numerous examples of ways to load assemblies in PowerShell v1, v2 and v3.这里有一些博客文章,其中包含许多在 PowerShell v1、v2 和 v3 中加载程序集的方法示例。

The ways include:方式包括:

  • dynamically from a source file从源文件动态
  • dynamically from an assembly从程序集动态
  • using other code types, ie F#使用其他代码类型,即 F#

v1.0 How To Load .NET Assemblies In A PowerShell Session v1.0 如何在 PowerShell 会话中加载 .NET 程序集
v2.0 Using CSharp (C#) code in PowerShell scripts 2.0 v2.0 在 PowerShell 脚本 2.0 中使用 CSharp (C#) 代码
v3.0 Using .NET Framework Assemblies in Windows PowerShell v3.0 在 Windows PowerShell 中使用 .NET Framework 程序集

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

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

None of the answers helped me, so I'm posting the solution that worked for me, all I had to do is to import the SQLPS module, I realized this when by accident I ran the Restore-SqlDatabase command and started working, meaning that the assembly was referenced in that module somehow.没有一个答案对我有帮助,所以我发布了对我有用的解决方案,我所要做的就是导入 SQLPS 模块,当我偶然运行了 Restore-SqlDatabase 命令并开始工作时,我意识到了这一点,这意味着该程序集以某种方式在该模块中被引用。

Just run:赶紧跑:

Import-module SQLPS

Note: Thanks Jason for noting that SQLPS is deprecated注意:感谢 Jason 注意到 SQLPS 已被弃用

instead run:而是运行:

Import-Module SqlServer

or要么

Install-Module SqlServer

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

You could use LoadWithPartialName .您可以使用LoadWithPartialName However, that is deprecated as they said.但是,正如他们所说,这已被弃用。

You can indeed go along with Add-Type , and in addition to the other answers, if you don't want to specify the full path of the .dll file, you could just simply do:您确实可以使用Add-Type ,除了其他答案之外,如果您不想指定 .dll 文件的完整路径,您可以简单地执行以下操作:

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

To me this returned an error, because I do not have SQL Server installed (I guess), however, with this same idea I was able to load the Windows Forms assembly:对我来说,这返回了一个错误,因为我没有安装 SQL Server(我猜),但是,有了同样的想法,我能够加载 Windows 窗体程序集:

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

You can find out the precise assembly name belonging to the particular class on the MSDN site:您可以在 MSDN 站点上找到属于特定类的精确程序集名称:

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

Make sure you have below features are installed in order确保您按顺序安装了以下功能

  1. Microsoft System CLR Types for SQL Server SQL Server 的 Microsoft 系统 CLR 类型
  2. Microsoft SQL Server Shared Management Objects Microsoft SQL Server 共享管理对象
  3. Microsoft Windows PowerShell Extensions Microsoft Windows PowerShell 扩展

Also you may need to load你也可能需要加载

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"

Add the assembly references at the top.在顶部添加程序集引用。

#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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM