.NET 4.0 中对 .NET 对象的后期绑定






4.20/5 (2投票s)
本文讨论了对 .NET 对象(而非 COM 对象)的后期绑定。
引言
本文展示了如何在 .NET 中进行后期绑定。提供的函数 LoadClass()
类似于 CreateObject()
函数,它允许你在运行时引用和使用外部库。该库必须在 GAC(全局程序集缓存)中注册。
背景
大约六年前,我曾在 Planet Source Code 上写过一篇类似的文章。但是,由于 Reflection.[Assembly].LoadWithPartialName
函数在 .NET 的后续版本中已被弃用,因此该代码只能在 .NET 1.1 和 2.0 中工作。
后期绑定到库并不需要将其注册到 GAC。你可以始终使用此简单函数从 DLL 中实例化一个类
Dim o As Object = LoadClass("c:\Microsoft.ReportViewer.WebForms.dll", _
"Microsoft.Reporting.WebForms.ReportViewer")
Public Shared Function LoadClass(ByVal sAssemblyPath As String, ByVal sClassName As String) As Object
'Get Assembly
Dim oAssembly As Reflection.Assembly
Try
oAssembly = Reflection.[Assembly].LoadFrom(sAssemblyPath)
Catch
Throw New ArgumentException("Can't load assembly " + sAssemblyPath)
End Try
'Get Class
Dim oType As Type = oAssembly.GetType(sClassName, False, False)
If oType Is Nothing Then
Throw New ArgumentException("Can't load type " + sClassName)
End If
'Instantiate
Dim oTypes(-1) As Type
Dim oInfo As Reflection.ConstructorInfo = oType.GetConstructor(oTypes)
Dim oRetObj As Object = oInfo.Invoke(Nothing)
If oRetObj Is Nothing Then
Throw New ArgumentException("Can't instantiate type " + sClassName)
End If
Return oRetObj
End Function
Using the Code
要从 GAC 加载 .NET 类,只需像这样调用 LoadClass
函数
Dim o As Object = AssemblyCache.LoadClass_
("CrystalDecisions.Web", "CrystalDecisions.Web.CrystalReportViewer")
请注意,“CrystalDecisions.Web
” 是在 GAC 中注册的库的名称,而“CrystalDecisions.Web.CrystalReportViewer
” 是类的名称。
这是 AssemblyCache.vb 的源代码,你也可以下载它。
Imports System.Runtime.InteropServices
Imports System.Text
Imports System.Globalization
<Flags()> _
Public Enum ASM_DISPLAY_FLAGS
VERSION = &H1
CULTURE = &H2
PUBLIC_KEY_TOKEN = &H4
PUBLIC_KEY = &H8
[CUSTOM] = &H10
PROCESSORARCHITECTURE = &H20
LANGUAGEID = &H40
End Enum
Public Enum ASM_NAME
ASM_NAME_PUBLIC_KEY = 0
ASM_NAME_PUBLIC_KEY_TOKEN
ASM_NAME_HASH_VALUE
ASM_NAME_NAME
ASM_NAME_MAJOR_VERSION
ASM_NAME_MINOR_VERSION
ASM_NAME_BUILD_NUMBER
ASM_NAME_REVISION_NUMBER
ASM_NAME_CULTURE
ASM_NAME_PROCESSOR_ID_ARRAY
ASM_NAME_OSINFO_ARRAY
ASM_NAME_HASH_ALGID
ASM_NAME_ALIAS
ASM_NAME_CODEBASE_URL
ASM_NAME_CODEBASE_LASTMOD
ASM_NAME_NULL_PUBLIC_KEY
ASM_NAME_NULL_PUBLIC_KEY_TOKEN
ASM_NAME_CUSTOM
ASM_NAME_NULL_CUSTOM
ASM_NAME_MVID
ASM_NAME_MAX_PARAMS
End Enum
<Flags()> _
Public Enum ASM_CACHE_FLAGS
ASM_CACHE_ZAP = &H1
ASM_CACHE_GAC = &H2
ASM_CACHE_DOWNLOAD = &H4
End Enum
#Region "COM Interface Definitions"
<ComImport(), Guid("CD193BC0-B4BC-11d2-9833-00C04FC31D2E"), _
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IAssemblyName
<PreserveSig()> _
Function SetProperty(PropertyId As ASM_NAME, _
pvProperty As IntPtr, cbProperty As Integer) As Integer
<PreserveSig()> _
Function GetProperty(PropertyId As ASM_NAME, pvProperty As IntPtr, _
ByRef pcbProperty As Integer) As Integer
<PreserveSig()> _
Overloads Function Finalize() As Integer
<PreserveSig()> _
Function GetDisplayName(<Out(), MarshalAs(UnmanagedType.LPWStr)> _
szDisplayName As StringBuilder, ByRef pccDisplayName As Integer, _
dwDisplayFlags As ASM_DISPLAY_FLAGS) As Integer
<PreserveSig()> _
Function BindToObject(ByRef refIID As Guid, <MarshalAs(UnmanagedType.IUnknown)> _
pUnkSink As Object, <MarshalAs(UnmanagedType.IUnknown)> pUnkContext As Object, _
<MarshalAs(UnmanagedType.LPWStr)> szCodeBase As String, llFlags As Long, pvReserved As IntPtr, _
cbReserved As Integer, ByRef ppv As IntPtr) As Integer
<PreserveSig()> _
Function GetName(ByRef lpcwBuffer As Integer, <Out(), MarshalAs(UnmanagedType.LPWStr)> _
pwzName As StringBuilder) As Integer
<PreserveSig()> _
Function GetVersion(ByRef pdwVersionHi As Integer, ByRef pdwVersionLow As Integer) As Integer
End Interface
<ComImport(), Guid("21b8916c-f28e-11d2-a473-00c04f8ef448"), _
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface IAssemblyEnum
<PreserveSig()> _
Function GetNextAssembly(pvReserved As IntPtr, _
ByRef ppName As IAssemblyName, dwFlags As Integer) As Integer
<PreserveSig()> _
Function Reset() As Integer
<PreserveSig()> _
Function Clone(ByRef ppEnum As IAssemblyEnum) As Integer
End Interface
#End Region
Public Class AssemblyCache
Private Shared oList As New Hashtable
<DllImport("fusion.dll", SetLastError:=True, PreserveSig:=False)> _
Private Shared Sub CreateAssemblyEnum(ByRef pEnum As IAssemblyEnum, _
pUnkReserved As IntPtr, pName As IAssemblyName, dwFlags As ASM_CACHE_FLAGS, pvReserved As IntPtr)
End Sub
Public Shared Function LoadClass(ByVal sAssemblyName As String, _
ByVal sClassName As String) As Object
Dim sFullName As String = GetFullAssemblyName(sAssemblyName)
'Get Assembly
Dim oAssembly As Reflection.Assembly
Try
oAssembly = Reflection.[Assembly].Load(sFullName)
Catch
Throw New ArgumentException("Can't load assembly " + sAssemblyName)
End Try
'Get Class
Dim oType As Type = oAssembly.GetType(sClassName, False, False)
If oType Is Nothing Then
Throw New ArgumentException("Can't load type " + sClassName)
End If
'Instantiate
Dim oTypes(-1) As Type
Dim oInfo As Reflection.ConstructorInfo = oType.GetConstructor(oTypes)
Dim oRetObj As Object = oInfo.Invoke(Nothing)
If oRetObj Is Nothing Then
Throw New ArgumentException("Can't instantiate type " + sClassName)
End If
Return oRetObj
End Function
Public Shared Function GetFullAssemblyName(sAssemblyName As String) As String
If oList.ContainsKey(sAssemblyName) Then
Return oList(sAssemblyName)
End If
Dim ae As IAssemblyEnum = CreateGACEnum()
Dim an As IAssemblyName = Nothing
While GetNextAssembly(ae, an) = 0
If GetName(an) = sAssemblyName Then
Dim name As System.Reflection.AssemblyName = GetAssemblyName(an)
Dim sRet As String = name.FullName
oList.Add(sAssemblyName, sRet)
Return sRet
End If
End While
Return ""
End Function
Public Shared Function GetAssemblyName(nameRef As IAssemblyName) _
As System.Reflection.AssemblyName
Dim name As New System.Reflection.AssemblyName()
name.Name = GetName(nameRef)
name.Version = GetVersion(nameRef)
name.CultureInfo = GetCulture(nameRef)
name.SetPublicKeyToken(GetPublicKeyToken(nameRef))
Return name
End Function
Public Shared Function GetName(name As IAssemblyName) As [String]
Dim bufferSize As Integer = 255
Dim buffer As New StringBuilder(CInt(bufferSize))
name.GetName(bufferSize, buffer)
Return buffer.ToString()
End Function
Public Shared Function GetVersion(name As IAssemblyName) As Version
Dim major As Integer
Dim minor As Integer
name.GetVersion(major, minor)
If minor < 0 Then
minor = 0
End If
Return New Version(CInt(major) >> 16, CInt(major) And &HFFFF, _
CInt(minor) >> 16, CInt(minor) And &HFFFF)
End Function
Public Shared Function GetPublicKeyToken(name As IAssemblyName) As Byte()
Dim result As Byte() = New Byte(7) {}
Dim bufferSize As Integer = 8
Dim buffer As IntPtr = Marshal.AllocHGlobal(CInt(bufferSize))
name.GetProperty(ASM_NAME.ASM_NAME_PUBLIC_KEY_TOKEN, buffer, bufferSize)
For i As Integer = 0 To 7
result(i) = Marshal.ReadByte(buffer, i)
Next
Marshal.FreeHGlobal(buffer)
Return result
End Function
Public Shared Function GetCulture(name As IAssemblyName) As CultureInfo
Dim bufferSize As Integer = 255
Dim buffer As IntPtr = Marshal.AllocHGlobal(CInt(bufferSize))
name.GetProperty(ASM_NAME.ASM_NAME_CULTURE, buffer, bufferSize)
Dim result As String = Marshal.PtrToStringAuto(buffer)
Marshal.FreeHGlobal(buffer)
Return New CultureInfo(result)
End Function
Public Shared Function CreateGACEnum() As IAssemblyEnum
Dim ae As IAssemblyEnum = Nothing
AssemblyCache.CreateAssemblyEnum(ae, CType(0, IntPtr), _
Nothing, ASM_CACHE_FLAGS.ASM_CACHE_GAC, CType(0, IntPtr))
Return ae
End Function
Public Shared Function GetNextAssembly(enumerator As IAssemblyEnum, _
ByRef name As IAssemblyName) As Integer
Return enumerator.GetNextAssembly(CType(0, IntPtr), name, 0)
End Function
End Class
历史
- 2012 年 11 月 4 日:初始版本