大部分人现在都知道System.Reflection.Assembly.LoadWithPartialName
已经过时了,但是事实证明Add-Type -AssemblyName Microsoft.VisualBasic
的表现并不比LoadWithPartialName
更好:(参见此链接)
相比于在上下文中解析请求,[Add-Type]只看一个静态的内部表来将“partial name”翻译成“full name”。
如果你的“partial name”没有出现在他们的表中,你的脚本会失败。
如果你的电脑上安装了多个版本的程序集,[Add-Type]没有智能算法来选择它们之间的区别。你只会得到出现在他们表中的那个,很可能是更老、过时的那个。
如果你安装的所有版本都比表中的过时版本新,你的脚本也会失败。
.LoadWithPartialNames
有着Add-Type所没有的智能“partial names”解析器。
微软的 .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 模块。 Get-GacAssembly -Name 'Microsoft.SqlServer.Smo*' | Select Name, Version, FullName
的效果相当好。
- 我该如何查看
Add-Type
使用的列表?
这有点复杂。我可以描述如何在任何版本的 PowerShell 中使用 .Net 反射器访问它(请参阅下面的更新以了解 PowerShell Core 6.0)。
首先,找出 Add-Type
是来自哪个库:
Get-Command -Name Add-Type | Select-Object -Property DLL
用你的反射器打开生成的DLL,我用ILSpy ,因为它是自由软件,但任何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)”。然而,在Windows 10 2004上的Powershell v7.0.3中,我看不到这个。
Add-Type -AssemblyName Forms
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('Forms'))
Add-Type -AssemblyName System.Windows.Forms
[System.Reflection.Assembly]::Load([System.Reflection.AssemblyName]::new('System.Windows.Forms'))
因此,评论似乎有些神秘。
当未指定版本或公钥令牌时,我不知道Assembly.Load(AssemblyName)
中的逻辑是什么。我预期这会遇到与LoadWithPartialName相同的许多问题,如在安装了多个程序集的情况下可能加载错误版本的程序集。
LoadWithPartialName
已被弃用,但是根据 http://blogs.msdn.com/b/suzcook/archive/2003/05/30/57159.aspx 中所述的原因,在交互式 Powershell 会话中显然不适用。我建议您添加一条注释,说明该 API 在交互式 Powershell 中使用是可以的。 - Micha Wiedenmannadd-Type -Path 'C:\Program Files (x86)\Microsoft SQL Server Management Studio 18\Common7\IDE\Microsoft.SqlServer.Smo.dll'
- GoldenAge