.net - 使用 powershell 解析程序集依赖项引用

标签 .net powershell .net-assembly

我正在尝试对我们的内部 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)。如果这可能是一个问题,任何想法如何解决它?

    最佳答案

    我在使用 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 ,我的解决方案是预加载我需要忽略版本的二进制文件(以便将它们加载到应用程序域中),然后使用一些与程序集解析过程 Hook 的代码(类似于 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)"
    }
    

    关于.net - 使用 powershell 解析程序集依赖项引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23268475/

    相关文章:

    c# - 在 ASP.NET MVC 应用程序中异步接收数据

    c# - .net:为什么在命名空间中使用 int 是不合法的?

    证书存储中的Powershell选择对象

    powershell - 无法捕获 NoProcessFoundForGivenName

    C# : Type. GetType 在动态加载程序集的类上为 null

    C# CompilerResults GenerateInMemory?

    c# - 使用 AppDomain.CreateInstance 时实例的包装器是什么?

    .net - 如何从.NET解决方案构建 'dependency tree diagram'

    wpf - PowerShell ISE WPF 控件内联

    C# 将不同版本的程序集加载到同一个项目