将 DLL 加载到不同的 AppDomain 及其依赖项中
Load a DLL into a different AppDomain and his dependencies
我在不同的 AppDomain 中通过反射加载 DLL 时遇到了一些麻烦。
这是我的场景。
- 我有一个名为 Interfaces.DLL 的 DLL,它只包含一个接口定义。
- Test.DLL 包含Interfaces.DLL 并定义一个class Connector,它实现了前面dll 上定义的接口。
- 我有一个应用只包含 Interfaces.dll 并且需要使用反射 Test.dll 加载
- 我调用 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 的建议,但他的评论帮助我找到了这个解决方案。所以,感谢您花时间帮助我:)
对于我这种情况的任何人来说,这是一个可行的解决方案:
- 您要反序列化的 class 必须继承自 MarshalByRefObject
- 您的 class 中的方法返回的对象必须是可序列化的
创建这个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
使用此代码调用要加载的 dll 中的方法:
Loader.Call(pathToDLL, ClasName,MethodName, parameters)
此解决方案在调用任何方法后卸载域。所以它并不完美,因为如果你要调用多个方法,你会在执行时间上受到惩罚。
我在不同的 AppDomain 中通过反射加载 DLL 时遇到了一些麻烦。
这是我的场景。
- 我有一个名为 Interfaces.DLL 的 DLL,它只包含一个接口定义。
- Test.DLL 包含Interfaces.DLL 并定义一个class Connector,它实现了前面dll 上定义的接口。
- 我有一个应用只包含 Interfaces.dll 并且需要使用反射 Test.dll 加载
- 我调用 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 的建议,但他的评论帮助我找到了这个解决方案。所以,感谢您花时间帮助我:)
对于我这种情况的任何人来说,这是一个可行的解决方案:
- 您要反序列化的 class 必须继承自 MarshalByRefObject
- 您的 class 中的方法返回的对象必须是可序列化的
创建这个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
使用此代码调用要加载的 dll 中的方法:
Loader.Call(pathToDLL, ClasName,MethodName, parameters)
此解决方案在调用任何方法后卸载域。所以它并不完美,因为如果你要调用多个方法,你会在执行时间上受到惩罚。