使用PowerShell解析程序集依赖性引用

时间:2014-04-24 12:08:43

标签: .net powershell .net-assembly

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

到目前为止,我有:

[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 current dir。
  • 将其编写为脚本,而不是从控制台编写。
  • 我在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)。如果这可能是一个问题,任何想法如何解决它?

1 个答案:

答案 0 :(得分:1)

我在NuGet包中遇到了类似的问题,我有两个程序集都使用了NuGet包(特别是Spring.NET库)。我的第一个C#项目(My1stProject)使用了.NET Framework v4.0,因此包含了特定于该Framework版本的NuGet包DLL。第二个C#项目(My2ndProject)针对.NET Framework v4.7,从而从NuGet包中获得了v4.5程序集。 Project 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,我的解决方案是预加载我需要忽略版本的二进制文件(以便将它们加载到App Domain中),然后使用一些挂钩到装配解决过程(类似于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)"
}