Assembly.LoadFrom之后的ResolveEventHandler

Tar*_*rik 4 .net c# vb.net assemblies

我正在加载Assembly.LoadFrom()程序集使用,因为程序集位于与Application Base目录不同的路径中.

Dim oAssembly As Assembly = _
Assembly.LoadFrom("C:\\MyFolder\\" + ddlXlate.SelectedItem.ToString() + ".dll")
Run Code Online (Sandbox Code Playgroud)

Type从该程序集中消耗了一个没有任何问题:

oXML = CType(oAssembly.CreateInstance(sBaseType + ".XlateContainer"), _
XlateBase.XlateContainer)
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试Type从下面的另一个方法中使用此程序集中的a时,会出现问题:

oComboBox.DataSource = _
[Enum].GetValues(Type.GetType(sType + "+ItemEnum," + sAssemblyName))
Run Code Online (Sandbox Code Playgroud)

sAssemblyName是我LoadFrom()实际加载的那个.在它说无法找到装配后,我使用了AssemblyResolve解决了我的问题的事件:

订阅AssemblyResolve活动:

AddHandler AppDomain.CurrentDomain.AssemblyResolve, _
AddressOf MyResolveEventHandler
Run Code Online (Sandbox Code Playgroud)

事件处理程序方法:

Private Shared Function MyResolveEventHandler(ByVal sender As Object, _
    ByVal args As ResolveEventArgs) As Assembly
    Return Assembly.LoadFrom("C:\\PSIOBJ\\" + args.Name + ".dll")
End Function
Run Code Online (Sandbox Code Playgroud)

我想也许错误发生,因为它找不到我LoadFrom()已经加载的程序集清单文件中定义的依赖程序集但是当我检查时args.Name,我看到它试图加载相同的程序集,之后它工作没有任何问题.因此,在添加更改的事件之前,基本上无法找到已加载程序集中的类型.

我的旧代码正在使用AppDomain.CurrentDomain.Load()Assembly.Load()方法,他们在没有AssemblyResolve事件的情况下工作正常.我能够Assembly从同一个地方的每个地方到达动态加载的类型AppDomain.

LoadFrom()可以在同一个请求的程序集路径中自动找到依赖项,这可能不是问题,因为它dll需要的一切都在那里.所以起初它看起来像是一个AppDomain问题,因为看起来它似乎可以从Load上下文而不是LoadFrom上下文到达程序集,我现在正在使用LoadFrom上下文.

  1. 但现在看来我应该通过oAssemblyevertwhere实例来使用加载程序集中的任何类型?
  2. 它是否使用简单的Type.GetType(...)方法加载我可以到达任何地方(相同的AppDomain)的程序集?

有人可以填写错过的分数并回答我的问题吗?

你可以使用C#,实际上我不喜欢VB.NET,但我必须在Office中使用它.

Abe*_*bel 9

如果我正确地理解了你的问题,那么你正试图按照以下方式做一些事情:

var asm = Assembly.LoadFrom(@"D:\Projects\_Libraries\FluentNH 1.1\Castle.Core.dll");
var obj = asm.CreateInstance("Castle.Core.GraphNode");
var type = Type.GetType(obj.GetType().AssemblyQualifiedName, true);  // fails
Run Code Online (Sandbox Code Playgroud)

您遇到的问题是,无论您使用何种形式的程序集加载,当库与可执行文件不在同一路径中时,变量type将始终存在null.

原因

您遇到的是.NET中程序集不同加载上下文的问题.简而言之,通常有三种,实际上有四种类型的加载上下文:

  • 默认的加载上下文.这用于GAC中的所有程序集,当前执行的程序集,当前路径中的程序集(请参阅BaseDirectory参考资料)和PrivatePath中的程序集(请参阅参考资料RelativeSearchPath).Assembly.Load(string,..)使用这个上下文.
  • 负载来自上下文.这是用于磁盘上不在探测路径中的任何程序集的上下文,通常是加载的Assembly.LoadFrom.
  • 仅反射的背景.此上下文中的类型无法执行.
  • 无上下文的背景.使用任何当加载的组件Assembly.Load(byte\[\],..)Assembly.LoadFile方法,或在加载动态组件不保存到磁盘中,使用这个上下文.

在一个上下文中加载的类型与另一个上下文不兼容(您甚至无法将相同类型从一个上下文转换为另一个上下文!).专门针对一个上下文操作的方法无法访问另一个上下文.除非您稍微帮助该方法,否则Type.GetType(string) 只能在默认上下文中加载类型.

这正是你遇到的.当程序集dll在你的应用程序的路径中时,一切正常.一旦你移动它,事情开始崩溃.

更具体地说:
当您调用时Type.GetType(string),它将查询路径中的所有静态引用的程序集,并在当前路径(AppDomain.BaseDirectory),GAC和in中查询动态加载的程序集AppDomain.RelativeSearchPath.不幸的是,相对搜索路径必须相对于基目录.

结果:
这种行为的结果是的GetType不能简单地检查所有加载的程序集.相反,它以相反的方式工作,它确实:

  1. GetType使用第一个参数的程序集部分来定位程序集 Assembly.Load
  2. 如果找到,则反映该程序集中的类型.
  3. 如果没有找到,则返回null而不尝试其他任何东西(或抛出FileNotFoundException,这可能相当混乱).

您可以自己测试:Assembly.Load当您只提供程序集名称时,它将无法工作.

解决方案

有几种解决方案.一个你已经为自己命名并保持装配对象的一个​​.还有一些,每个都有自己的缺点:

  1. 使用GetType()实例化对象本身,而不是静态方法Type.GetType(string).这样做的好处是你不需要类型的汇编限定名称,这可能很难得到(在你的例子中,你没有说你如何设置sAssemblyName,但这不是你需要浮动的东西吗? ).

  2. 使用通用解析程序检查已加载的程序集并返回已加载的程序集.你不需要再打电话LoadFrom.我对以下内容进行了测试,结果非常出色:

    // works for any loaded assembly, regardless of the path
    private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args)
    {
        // you may not want to use First() here, consider FirstOrDefault() as well
        var asm = (from a in AppDomain.CurrentDomain.GetAssemblies()
                  where a.GetName().FullName == args.Name
                  select a).First();
        return asm;
    }
    
    // set it as follows somewhere in the beginning of your program:
    AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve;
    
    Run Code Online (Sandbox Code Playgroud)
  3. 同时使用AppDomain.CurrentDomain.AssemblyLoad和.AssemblyResolve事件.第一个用于记忆字典缓存中的每个已加载的程序集(通过全名),第二个用于通过按名称获取其中的值来探测该字典.这实现起来相对简单,并且可能比先前的解决方案稍微好一些.

  4. 使用AppDomain.CurrentDomain.TypeResolve事件处理程序.我没试过这个,所以我不确定它会在你的场景中起作用.:这不起作用.GetType 首先尝试加载程序集,当失败时,它不会尝试解析类型,并且此事件永远不会触发.

  5. 将要解析的库添加到GAC或应用程序的任何(相对)路径.这是迄今为止最简单的解决方案.

  6. 添加到app.config的路径.这仅适用于强类型程序集,在这种情况下,您可以轻松地将它们加载到GAC中.非强类型程序集仍必须位于当前应用程序的相对路径中.

结论

静态方法组Type.GetType(..)在第一次看到加载的程序集时表现得相当不直观.一旦理解了几个上下文背后的想法,尝试将程序集放在默认上下文中.如果无法做到这一点,您可以创建一个AssemblyResolve事件处理程序,这一点并不难以实现.