通过AppDomain读取.NET生成的DLL中信息
对于DLL文件,取出他的Assembly信息可以使用Reflection.Assembly.LoadFrom(Path)语句来实现。但是这样读取之后,如果我们的程序不退出,这个DLL就会被我们的程序进程锁死,不能修改或删除了。为了解决这样的问题,我们通过AppDomain(应用程序域)来实现。
由于我们通过AppDomain来操作,所以我们的信息必须要支持跨应用程序域的访问。为此,我们需要做一个继承自MarshalByRefObject的类来进行我们的核心操作Reflection.Assembly.LoadFrom(Path)。这样在另外一个AppDomain下面我们就可以通过这个类操作Reflection.Assembly.LoadFrom(Path),并且把相关的信息返回回来。具体代码如下。
Private NotInheritable Class AssemblyLoader
Inherits MarshalByRefObject
Private m_Asm As Reflection.Assembly
Public Sub LoadAssemblyFile(ByVal Path As String)
If IO.File.Exists(Path) = True Then
m_Asm = Reflection.Assembly.LoadFrom(Path)
End If
End Sub
End Class
下面我们再做一个类来专门调用这个类,读取DLL文件的Assembly信息。这个类没有什么特别的要求,从Object继承就可以了。然后我们需要一个AppDomain(应用程序域)Private m_ap As AppDomain。
下面我们建立一个函数,它的返回值是我们刚刚建立的那个继承自MarshalByRefObject的类,让我们能够调用Reflection.Assembly.LoadFrom(Path)来读取Assembly信息。
Private Function CreateAssemblyLoaderInstanse(Optional ByVal DomainName As String = "") As AssemblyLoader
Dim objh As System.Runtime.Remoting.ObjectHandle
Dim obj As Object
Dim al As AssemblyLoader
Try
If DomainName.Trim = "" Then
DomainName = DNAME_DEFAULT & Rnd()
End If
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf OnResolveEventHandler
m_ap = AppDomain.CreateDomain(DomainName)
objh = m_ap.CreateInstanceFrom(System.Reflection.Assembly.GetExecutingAssembly().Location, GetType(AssemblyLoader).FullName)
obj = objh.Unwrap()
al = CType(obj, AssemblyLoader)
Catch ex As Exception
al = Nothing
End Try
Return al
End Function
前几行代码是为了设定应用程序域的名字的,没有什么特别的。核心的语句是
m_ap = AppDomain.CreateDomain(DomainName)
objh = m_ap.CreateInstanceFrom(System.Reflection.Assembly.GetExecutingAssembly().Location, GetType(AssemblyLoader).FullName)
obj = objh.Unwrap()
al = CType(obj, AssemblyLoader)
首先我们通过AppDomain.CreateDomain(DomainName)建立了自己的AppDomain。然后我们在这个AppDomain下面通过CreateInstanceFrom方法建立AssemblyLoader类的实例。最后通过ObjectHandle.Unwrap方法返回被包装的对象。最后CType成我们的AssemblyLoader。
这样,这个AssemblyLoader就被建立在了一个新的AppDomain里面,而AssemblyLoader的所有操作都是在这个AppDomain中,通过MarshalByRefObject返回给我们主AppDomain。
我们直接调用al.LoadAssemblyFile(Path)就可以读取Assembly信息了。
当我们操作完了这个DLL之后,我们通过AppDomain.Unload(m_ap)卸载这个AppDomain,这样这个DLL的读写LOCK就会解除了。这样就解决了我们的问题。
相关的代码如下。
Private Function OnResolveEventHandler(ByVal sender As Object, ByVal args As ResolveEventArgs) As System.Reflection.Assembly
Dim MyAssembly As System.Reflection.Assembly
If args.Name = System.Reflection.Assembly.GetExecutingAssembly().FullName Then
MyAssembly = System.Reflection.Assembly.LoadFrom(System.Reflection.Assembly.GetExecutingAssembly().Location)
End If
Return MyAssembly
End Function
Private Sub Unload()
RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf OnResolveEventHandler
If Not m_ap Is Nothing Then
AppDomain.Unload(m_ap)
End If
End Sub
但是需要我们注意的是,跨应用程序域的成员通信只支持简单数据类型,或者是从MarshalByRefObject继承的。也就是说,我们通过AssemblyLoader得到的只能是诸如Integer、String这样的东西。所以我们在使用的时候,需要在AssemblyLoader里面做好读取我们需要信息的方法,通过简单数据类型来返回。这也就决定了我们不可能使用这个办法制作出公用性很强的类。