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

Load a DLL into a different AppDomain and his dependencies

我在不同的 AppDomain 中通过反射加载 DLL 时遇到了一些麻烦。

这是我的场景。

  1. 我有一个名为 Interfaces.DLL 的 DLL,它只包含一个接口定义。
  2. Test.DLL 包含Interfaces.DLL 并定义一个class Connector,它实现了前面dll 上定义的接口。
  3. 我有一个应用只包含 Interfaces.dll 并且需要使用反射 Test.dll 加载
  4. 我调用 class 连接器的 public 方法,其中 returns 我通过反射加载的 dll 文件的 DLL 版本。之后,我调用一个网络服务来检查我是否有更高版本的文件。如果没有,我只好卸载dll,删除文件,然后下载新文件。

问题出在第 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

和原来的行为没有区别。

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

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

  1. 您要反序列化的 class 必须继承自 MarshalByRefObject
  2. 您的 class 中的方法返回的对象必须是可序列化的
  3. 创建这个class:

    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
    
  4. 使用此代码调用要加载的 dll 中的方法:

    Loader.Call(pathToDLL, ClasName,MethodName, parameters)
    

此解决方案在调用任何方法后卸载域。所以它并不完美,因为如果你要调用多个方法,你会在执行时间上受到惩罚。