现在大多数人都知道System.Reflection.Assembly.LoadWithPartialName 已被弃用,但事实证明Add-Type -AssemblyName Microsoft.VisualBasic does not behave much better than LoadWithPartialName:
而不是尝试在上下文中解析您的请求
在您的系统中,[Add-Type] 会查看一个静态的内部表来翻译
“部分名称”改为“全名”。
如果您的“部分名称”没有出现在他们的表格中,您的脚本将
失败。
如果您的系统上安装了多个版本的程序集
计算机,没有智能算法可以在它们之间进行选择。
你会得到他们表中出现的任何一个,可能
旧的,过时的。
如果您安装的版本都比过时的版本新
在表格中,您的脚本将失败。
Add-Type 没有像“部分名称”这样的智能解析器
.LoadWithPartialNames.
Microsoft 的 .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,cmets 将其称为 LoadWithPartialName 的“可能的最接近的近似值”。基本上,他们这样做:
loadedAssembly = Assembly.Load(new AssemblyName(assemblyName));
现在,cmets 还说“用户可以说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'))
所以 cmets 似乎有点神秘。
当没有指定版本或公钥令牌时,我不知道Assembly.Load(AssemblyName) 中的确切逻辑是什么。我希望这有许多与 LoadWithPartialName 相同的问题,如果您安装了多个,可能会加载错误版本的程序集。