2

我在 Windows 10 x64、PowerShell 版本 5.1 中工作。

我有多个版本的 .NET Framework 4.0 程序集 (.dll) 用 C#(我自己)编写。程序集未签名。他们的版本是AssemblyInfo.cs通过设置的[assembly: AssemblyVersion("X.X.X.X")]。我的[assembly: AssemblyFileVersion("X.X.X.X")]标签不存在AssemblyInfo.cs!这些程序集在常规 .NET Framework 应用程序中使用,它们不是专门的 PowerShell 模块,也没有清单文件。

我在一些 PowerShell 脚本中使用这个程序集来创建对象并从中调用方法。

我有这个程序集的两个版本,比如说1.1.1.1001.2.1.1001.1.1.100当我通过 PowerShell导入假设版本时Import-Module "D:\path\to_v1.1\MyAssembly.dll",它工作得很好。当我打电话时,Get-Module我在列表中看到了这个导入的程序集:

ModuleType Version    Name
---------- -------    ----
Binary     1.1.1.100  MyAssembly

现在事情开始变得有趣了。我导入了其他版本的程序集Import-Module "D:\another\path\to_v1.2\MyAssembly.dll"。它再次工作得很好。所以这是我的问题:

Q1。为什么没有显示错误?我希望得到一个错误:我正在尝试加载一个名称相同但版本不同的程序集。我认为您无法在一个上下文中加载同一程序集的两个不同版本。这些情况是如何处理的,使用了哪些加载上下文?也许根本没有加载第二个版本?

Q2。 Get-Module现在显示两个具有相同名称和版本的模块,如下所示:

ModuleType Version    Name
---------- -------    ----
Binary     1.1.1.100  MyAssembly
Binary     1.1.1.100  MyAssembly

我先导入哪个版本并不重要。在第二个Import-Module最早导入的版本Get-Module列表中重复。我也只能使用最早导入的程序集版本中的类型/方法。

Q3。我的程序集对我的其他程序集(在同一文件夹中)有一些依赖关系。这些依赖项会自动解析并隐式“导入”到当前会话(我可以使用它们中的类型没问题)。主程序的每个版本都MyAssembly.dll依赖于这些辅助程序集的不同版本。当我导入另一个版本时MyAssembly,我也没有收到关于版本冲突的错误。再一次,我只能使用最早导入的辅助程序集中的类型。我读过“依赖地狱”,这应该是不可能的——那怎么可能呢?

我对 NuGet 包Microsoft.CodeAnalysis.CSharp.dll、版本3.4.03.11.0. 结果是一样的:版本及其依赖导入没问题,但只有最早导入的版本可用。

概括

当我导入相同程序集的不同版本时,我没有收到任何错误,但只有最早的导入版本可用。我想了解为什么我能够在一个会话中加载程序集的多个版本而不会出错,PowerShell 通常如何处理这些情况以及为什么它显示两个具有相同版本的模块,为什么我没有得到依赖地狱。

我想了解发生了什么,而不仅仅是“让它工作”。

我在这里想念什么?

谢谢!

4

1 回答 1

1

在您的方案中,您的模块是实现 PowerShell cmdlet的独立 .NET 程序集。

在 Windows PowerShell 和 PowerShell (Core) 7+ 中,所有程序集都加载到同一个

  • Windows PowerShell中的应用程序域,它基于.NET Framework

  • PowerShell (Core) 7+中的 ALC(程序集加载上下文),它基于.NET Core / .NET 5+

默认情况下,支持加载同一程序集的多个版本,加载到会话中的第一个获胜。

随后尝试加载不同版本的程序集

  • 在 Windows PowerShell中被悄悄忽略- 无论您是否使用.NET API 方法,Import-Module(请务必将完整文件路径传递给后者,因为 PowerShell 的工作目录。通常与 .NET 不同;该方法输出一个对象表面上表示刚加载的程序集,但它实际上表示最初加载的版本)。Add-Type -LiteralPath[System.Reflection.Assembly]::LoadFrom()

  • 在 Powershell (Core) 7+ 中导致语句终止错误

    Assembly with same name is already loaded.
    

注意:您可以加载具有不同版本的程序集,即通过[System.Reflection.Assembly]::LoadFile()方法(注意File代替From),但其类型可用的唯一方法是通过反射


据我所知:

  • 与您的情况一样,没有针对cmdlet-implementing assembly的解决方案。

    • 通过cmdlet 实现,我的意思是模块的 cmdlet 本身实现为存储在程序集中的 .NET 类,而不是在脚本模块文件中在 PowerShell 代码中实现的 cmdlet。*.psm1

    • 无论您碰巧在会话中首先导入/加载的 cmdlet 实现程序集的哪个版本,都是会话剩余部分中唯一可用的版本;程序集一旦加载,就无法卸载。

    • 另一种说法:虽然您可以在磁盘上拥有此类模块的并行版本,但据我所知,您不能同时将它们加载到会话中 - 这仅适用于基于脚本的模块(请参阅下一点) .

  • 将多个版本的辅助程序集加载到同一个会话中有一些非常重要的解决方案;这些在解决 PowerShell 模块程序集依赖冲突中进行了详细说明,它还提供了广泛的背景信息。

    • 也就是说,如果您的 cmdlet在 PowerShell 代码中实现并且该代码仅使用辅助.NET 程序集,则链接文档提供了加载这些程序集版本的解决方案,这些版本默认情况下会与其他模块加载的不同版本或相同的不同版本发生冲突模块。
于 2021-08-30T22:03:20.497 回答