将DLL加载到不同的AppDomain及其依赖项

时间:2015-05-26 08:16:07

标签: .net vb.net reflection appdomain

我在通过反射在另一个AppDomain中加载DLL时有点麻烦。

这是我的情景。

  1. 我有一个名为Interfaces.DLL的DLL,它只包含一个接口定义。
  2. Test.DLL包含Interfaces.DLL并定义一个名为Connector的类,它实现了之前dll中定义的接口。
  3. 我有一个只包含Interfaces.dll的应用程序,需要使用反射
  4. 加载Test.dll
  5. 我调用类连接器的公共方法,它返回由反射加载的DLL文件的DLL版本。之后,我调用一个Web服务来检查我是否有更高版本的文件。如果没有,我必须卸载DLL,删除文件,然后下载新文件。
  6. 问题出在第3步。当我尝试在不同的AppDomain中加载Test.DLL时,我收到错误,因为它无法在AppDomain中找到Interfaces.Dll。消息是:

    System.IO.FileNotFoundException was unhandled
     FileName=BankInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
    

    那么,如何在AppDomain中加载2个不同的Dll?

    这是我的代码:

    Interface.DLL

    Public Interface BankInterface
           Function getDLLVersion() As Double
           'Other methods here
    End Interface
    

    Test.dll的

    Public Class Connector
        Implements BankInterfaces.BankInterface
        Public Function getDLLVersion() As Double Implements BankInterfaces.BankInterface.getDLLVersion
             Return 2.5
        End Function
    

    MainApplication

    Public Sub Main()
        Dim domainSetup As New AppDomainSetup
        domainSetup.ApplicationName = appDomainName
        domainSetup.ApplicationBase = "C:\Users\jferrer.GLOBAL\AppData\Roaming\Enterprise\AppName\DllFiles\"
    
        Dim LocalAppDomain As AppDomain = AppDomain.CreateDomain("BankDLL" & Guid.NewGuid.ToString.GetHashCode.ToString("x"), Nothing, domainSetup)
        AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf CurrentDomain_AssemblyResolve
    
        LocalAppDomain.CreateInstanceFrom("C:\Users\jferrer.GLOBAL\AppData\Roaming\Enterprise\AppName\DllFiles\TestDLL.dll", "TestDLL.Connector") 'This line throw the error
        Dim conector As Type = LocalAppDomain.GetType()
    
        'Irrelevant code here
    end sub
    
    Private Function CurrentDomain_AssemblyResolve(ByVal sender As Object, ByVal args As ResolveEventArgs) As Assembly
        Try
            Dim myassembly As Assembly = Assembly.Load(args.Name)
            If Not IsNothing(myassembly) Then
                Return myassembly
            End If
        Catch ex As Exception
        End Try
    
        Dim parts As String() = args.Name.Split(",")
        Dim myfile As String = "C:\Users\jferrer.GLOBAL\AppData\Roaming\Enterprise\AppName\DllFiles\" & parts(0).Trim() & ".dll"
    
        Return Assembly.LoadFrom(myfile)
    end function
    

    更新:

    如果我改变

    AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf CurrentDomain_AssemblyResolve 
    

    AddHandler LocalAppDomain.CurrentDomain.AssemblyResolve, AddressOf CurrentDomain_AssemblyResolve
    

    我从Visual Studio收到警告:

    Warning 1   Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated.  C:\Proyectos\BANK\BankTest\Form1.vb 49  20  BankTest
    

    原始行为之间没有区别。

1 个答案:

答案 0 :(得分:0)

尽管我没有直接申请@James Barras的建议,但他的评论帮助我找到了这个解决方案。所以,感谢您花时间帮助我:)

对于我所处的任何人来说,这是一个可行的解决方案:

  1. 要反序列化的类必须从MarshalByRefObject
  2. 继承
  3. 类中方法返回的对象必须是可序列化的
  4. 创建此类:

    Imports System.Reflection
    
    Public Class Loader
    Inherits MarshalByRefObject
    Private Function CallInternal(dll As String, typename As String, method As String, parameters As Object()) As Object
        Dim a As Assembly = Assembly.LoadFile(dll)
        Dim o As Object = a.CreateInstance(typename)
        Dim t As Type = o.[GetType]()
        Dim m As MethodInfo = t.GetMethod(method)
        Return m.Invoke(o, parameters)
    End Function
    Public Shared Function [Call](dll As String, typename As String, method As String, ParamArray parameters As Object()) As Object
        Dim dom As AppDomain = AppDomain.CreateDomain("MyNewDomain")
        Dim ld As Loader = DirectCast(dom.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, GetType(Loader).FullName), Loader)
        Dim result As Object = ld.CallInternal(dll, typename, method, parameters)
        AppDomain.Unload(dom)
        Return result
    End Function
    End Class
    
  5. 使用此代码调用要加载的dll中的方法:

    Loader.Call(pathToDLL, ClasName,MethodName, parameters)
    
  6. 此解决方案在您调用任何方法后卸载域。所以它并不完美,因为如果你想调用几种方法,你将在执行时受到惩罚。