【问题标题】:Resolving assembly dependency references with powershell使用 powershell 解析程序集依赖引用
【发布时间】:2014-06-09 17:07:10
【问题描述】:

我正在尝试针对我们的一个内部 API 使用 PowerShell v4.0 (x86/64) 来做一些相当基本的事情,但我似乎无法通过依赖加载。

到目前为止我有:

[Reflection.Assembly]::LoadFrom("C:\Users\David Shaw\Desktop\API\API.dll")

根据 Dat Bui 的blog post

这很好,然后我尝试在这个 DLL 中使用一个类型:

$a = New-Object API.API("", 1234)

这给了我以下错误:

New-Object : Exception calling ".ctor" with "2" argument(s): "Unable to find assembly API.Dependency, 
Version=1.2.5.0, Culture=neutral, PublicKeyToken=null'."
At line:1 char:6
+ $a = New-Object API.API("", 1234)
+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

在 FusionLog 中查找依赖项的唯一位置是: C:\Windows\System32\WindowsPowerShell\v1.0

到目前为止我尝试过的事情:

  • 设置 powershell 当前目录。
  • 将其编写为脚本而不是从控制台。
  • 我的依赖项与 API.dll 位于同一位置
  • 使用LoadFile 代替LoadFrom
  • 使用Add-Type -Path API.dll
  • Setting the .net CurrentDirectory
  • 在依赖项上调用 LoadFrom
  • 在 Powershell 中执行 AppDomain.AssemblyResolve 事件见下文,但此堆栈溢出 powershell:

来自我的脚本:

$OnAssemblyResolve = [System.ResolveEventHandler] {
  param($sender, $e)
    $n = New-Object System.Reflection.AssemblyName($e.Name).Name
      $fn = "C:\Users\David Shaw\Desktop\API\$n.dll"
      return [Reflection.Assembly]::LoadFile($fn)       
}

[System.AppDomain]::CurrentDomain.add_AssemblyResolve($OnAssemblyResolve)

根据评论,API.dll 是 .Net 4.0 (AnyCPU),API.Dependency.dll 是 .Net 2.0 (AnyCPU)。如果这可能是一个问题,有什么想法可以解决吗?

【问题讨论】:

  • 这里的猜测是......可能与支持的运行时有关吗?您的 API.dll 及其依赖项是用什么版本的 .Net 编译的?还是与您使用的 PowerShell 的位数不对应的 DLL 位数相关?
  • @DavidBrabant 我已经尝试了两个 powershell 版本,没有区别。我在最后的问题中添加了 .net 版本。
  • 我很惊讶Add-Type -Path C:\Users\David Shaw\Desktop\API\API.dll; Add-Type -Path C:\Users\David Shaw\Desktop\API\API.Dependency.dll 不起作用。或者至少指向加载器找不到的另一个依赖程序集。
  • @KeithHill,可以肯定的是,我已在此 API 所需的每个 DLL 上使用了 Add-Type -Path。它仍然以同样的方式失败。
  • 作为一个快速实验来确定这是加载程序问题还是其他问题,请将所有必需的程序集复制到 C:\Windows\System32\WindowsPowerShell\v1.0。如果仍然失败,那么还有其他事情发生(缺少程序集依赖等)。

标签: .net powershell .net-assembly


【解决方案1】:

我在使用 NuGet 包时遇到了类似的问题,我有两个程序集都使用了 NuGet 包(特别是 Spring.NET 库)。我的第一个 C# 项目 (My1stProject) 使用 .NET Framework v4.0,因此包含特定于该框架版本的 NuGet 包 DLL。第二个 C# 项目 (My2ndProject) 以 .NET Framework v4.7 为目标,因此从 NuGet 包中获得了 v4.5 程序集。 My2ndProject 项目依赖于 My1stProject。

当我编译代码时,一切正常。项目 My2ndProject 已编译,但 NuGet 包中包含的程序集适用于 v4.5 框架。

现在,当我尝试 - 在 My2ndProject 的二进制输出目录中 - 使用 Powershell 代码加载和获取程序集的类型时:$assembly = [System.Reflection.Assembly]::LoadFrom($My1stProjectFullPath),然后是 $assembly.GetTypes(),由于版本差异,这将失败 - v4 .5 NuGet DLL 在那里,但它期待的是 v4.0。

因此,按照excellent code example,我的解决方案是预加载我需要忽略版本的二进制文件(以便将它们加载到应用程序域中),然后使用一些挂钩到程序集解析的代码过程(类似于 OP 问题中的过程)和:

  • 首次尝试根据全名(包括版本、区域设置等)进行加载匹配。
  • 如果失败,请尝试仅对名称进行匹配(忽略版本等)

代码如下:

$candidateAssembly =  "C:\My2ndProject\bin\Debug\My1stProject.exe"

# Load your target version of the assembly (these were from the NuGet package, and 
# have a version incompatible with what My2ndProject.exe expects)
[System.Reflection.Assembly]::LoadFrom("C:\My2ndProject\bin\Debug\Spring.Aop.dll")
[System.Reflection.Assembly]::LoadFrom("C:\My2ndProject\bin\Debug\Spring.Core.dll")
[System.Reflection.Assembly]::LoadFrom("C:\My2ndProject\bin\Debug\Spring.Data.dll")

# Method to intercept resolution of binaries
$onAssemblyResolveEventHandler = [System.ResolveEventHandler] {
    param($sender, $e)

    Write-Host "ResolveEventHandler: Attempting FullName resolution of $($e.Name)" 
    foreach($assembly in [System.AppDomain]::CurrentDomain.GetAssemblies()) {
        if ($assembly.FullName -eq $e.Name) {
            Write-Host "Successful FullName resolution of $($e.Name)" 
            return $assembly
        }
    }

    Write-Host "ResolveEventHandler: Attempting name-only resolution of $($e.Name)" 
    foreach($assembly in [System.AppDomain]::CurrentDomain.GetAssemblies()) {
        # Get just the name from the FullName (no version)
        $assemblyName = $assembly.FullName.Substring(0, $assembly.FullName.IndexOf(", "))

        if ($e.Name.StartsWith($($assemblyName + ","))) {

            Write-Host "Successful name-only (no version) resolution of $assemblyName" 
            return $assembly
        }
    }

    Write-Host "Unable to resolve $($e.Name)" 
    return $null
}

# Wire-up event handler
[System.AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolveEventHandler)

# Load into app domain
$assembly = [System.Reflection.Assembly]::LoadFrom($candidateAssembly) 

try
{
    # this ensures that all dependencies were loaded correctly
    $assembly.GetTypes() 
} 
catch [System.Reflection.ReflectionTypeLoadException] 
{ 
     Write-Host "Message: $($_.Exception.Message)" 
     Write-Host "StackTrace: $($_.Exception.StackTrace)"
     Write-Host "LoaderExceptions: $($_.Exception.LoaderExceptions)"
}

【讨论】:

  • 这让我解决了System.Threading.Tasks.Extensions.dll 的第一个绑定问题,但System.Runtime.CompilerServices.Unsafe.dll 的下一个绑定问题失败了。不知道为什么,似乎 GetTypes() 不会触发该 dll 的解析处理程序。
  • powershell 加载 custom1.dll,然后在结果加载错误中加载引用 custom1.dll 的 custom2.dll,这段代码救了我。这超出了我的理解,当加载引用此 DLL 的库时,不会自动使用 AppDomain 中的 DLL ... PS。我忘记了这个事件,但我像 8 年前一样使用它:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-14
  • 1970-01-01
相关资源
最近更新 更多