使用Powershell解决程序集依赖项引用 [英] Resolving assembly dependency references with powershell

查看:52
本文介绍了使用Powershell解决程序集依赖项引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试对内部API之一使用PowerShell v4.0(x86/64)来做一些相当基本的事情,但是我似乎无法摆脱依赖项的加载.

到目前为止,我有:

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

根据Dat Bui的博客文章.

这工作正常,然后我尝试在此DLL中使用一种类型:

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

这给了我以下错误:

  New-Object:带有"2"参数的调用".ctor"的异常:无法找到程序集API.版本= 1.2.5.0,文化=中性,PublicKeyToken =空".在线:1个字符:6个+ $ a =新对象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
  • 使用添加类型-Path API.dll
  • 设置.net CurrentDirectory
  • 在依赖项上调用 LoadFrom .
  • 在Powershell中执行AppDomain.AssemblyResolve事件,请参见下文,但是此堆栈会溢出powershell:

根据我的脚本:

  $ OnAssemblyResolve = [System.ResolveEventHandler] {参数($ sender,$ e)$ n =新对象System.Reflection.AssemblyName($ e.Name).Name$ fn ="C:\ Users \ David Shaw \ Desktop \ API \ $ n.dll"返回[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包的程序集(特别是优秀的代码示例,我的解决方案是预加载我需要忽略其版本的二进制文件(以便将它们加载到App Domain中),然后使用一些与程序集解析过程挂钩的代码(类似于OP中的代码)问题)和

  • 首次尝试根据全名(包括版本,语言环境等)进行负载匹配.
  • 如果失败,则尝试仅按名称进行匹配(忽略版本等)

这是代码:

  $ candidateAssembly ="C:\ My2ndProject \ bin \ Debug \ My1stProject.exe"#加载程序集的目标版本(这些版本来自NuGet包,并且#具有与My2ndProject.exe期望的版本不兼容的版本)[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")#拦截二进制解析的方法$ onAssemblyResolveEventHandler = [System.ResolveEventHandler] {参数($ sender,$ e)写主机"ResolveEventHandler:尝试$($ e.Name)的FullName解析"foreach([System.AppDomain]中的$ assembly :: CurrentDomain.GetAssemblies()){如果($ assembly.FullName -eq $ e.Name){写主机"$($ e.Name)的成功全名解析"返回$ assembly}}写主机"ResolveEventHandler:尝试$($ e.Name)的仅名称解析"foreach([System.AppDomain]中的$ assembly :: CurrentDomain.GetAssemblies()){#从全名中获取名称(无版本)$ assemblyName = $ assembly.FullName.Substring(0,$ assembly.FullName.IndexOf(,"))如果($ e.Name.StartsWith($($ assemblyName +,"))){写主机"$ assemblyName仅成功的名称(无版本)解析"返回$ assembly}}写主机无法解析$($ e.Name)"返回$ null}#连线事件处理程序[System.AppDomain] :: CurrentDomain.add_AssemblyResolve($ onAssemblyResolveEventHandler)#加载到应用程序域$ assembly = [System.Reflection.Assembly] :: LoadFrom($ candidateAssembly)尝试{#这样可确保所有依赖项均已正确加载$ assembly.GetTypes()}捕获[System.Reflection.ReflectionTypeLoadException]{写主机消息:$($ _.Exception.Message)"写主机"StackTrace:$($ _.Exception.StackTrace)"写主机"LoaderExceptions:$($ _.Exception.LoaderExceptions)"} 

I am trying to use PowerShell v4.0 (x86/64) against one of our internal API's to do some fairly basic stuff, but I cannot seem to get past the dependency loading.

So far I have:

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

as per Dat Bui's blog post.

This works fine, I then try to use a type inside this DLL:

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

This gives me the following error:

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

Looking in FusionLog, the only places it looks for the dependency is: C:\Windows\System32\WindowsPowerShell\v1.0

Things I've tried so far:

  • Setting the powershell current dir.
  • Writing it as a script instead of from the console.
  • I have the dependency in the same location as API.dll
  • Using LoadFile instead of LoadFrom
  • Using Add-Type -Path API.dll
  • Setting the .net CurrentDirectory
  • Calling LoadFrom on the dependency.
  • Doing an AppDomain.AssemblyResolve event in Powershell see below, but this stack overflows powershell:

From my Script:

$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)

Based on the comment, the API.dll is .Net 4.0 (AnyCPU) and the API.Dependency.dll is .Net 2.0 (AnyCPU). If this could be an issue, any idea's how to resolve it?

解决方案

I had a similar problem with NuGet packages whereby I had two assemblies that both used a NuGet package (specifically, the Spring.NET libraries). My first C# project (My1stProject) used the .NET Framework v4.0, and thus included the NuGet package DLL specific to that Framework version. A second C# project (My2ndProject) targeted the .NET Framework v4.7, and thus got the v4.5 assembly from the NuGet package. Project My2ndProject had a dependency on My1stProject.

When I compiled the code, everything worked. Project My2ndProject compiled, but the included assembly from the NuGet package was for the v4.5 Framework.

Now, when I attempted - in My2ndProject's binary output directory - to use Powershell code to load and get types for the assembly: $assembly = [System.Reflection.Assembly]::LoadFrom($My1stProjectFullPath), followed by $assembly.GetTypes(), this would fail due to the version discrepancy -- the v4.5 NuGet DLL was there, but it was expecting the v4.0 one.

So, following this excellent code example, my solution is to pre-load the binaries that I need to ignore the version for (so that they are loaded into the App Domain), then use some code that hooks into the assembly resolution process (similar to that in the OP question) and:

  • first attempt to do a load match based on the full name (including version, locale, etc.
  • and if that failed, attempt to do the match on name only (ignoring version etc.)

Here is the code:

$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)"
}

这篇关于使用Powershell解决程序集依赖项引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆