我已經看到這在C#中完成,如here雖然,我似乎無法弄清楚如何在VB.NET中做到這一點。對於某些背景,我創建了一個自定義ComboBox控件作爲.dll,並且需要在另一個.dll(ArcMap組件)中實現它。VB.NET嵌入式DLL在另一個DLL中作爲嵌入式資源?
不幸的是,ArcMap不容許「第三方」的DLL與組件一起被加載,因爲沒有選項來引用任何第三方組件爲您的加載項。
如果有人能指出我正確的方向,它將不勝感激。
我已經看到這在C#中完成,如here雖然,我似乎無法弄清楚如何在VB.NET中做到這一點。對於某些背景,我創建了一個自定義ComboBox控件作爲.dll,並且需要在另一個.dll(ArcMap組件)中實現它。VB.NET嵌入式DLL在另一個DLL中作爲嵌入式資源?
不幸的是,ArcMap不容許「第三方」的DLL與組件一起被加載,因爲沒有選項來引用任何第三方組件爲您的加載項。
如果有人能指出我正確的方向,它將不勝感激。
我們使用VB.NET這種技術在Visual Studio 2008 ...
首先,該項目需要知道包括「其他」 DLL作爲嵌入資源。在解決方案資源管理器中,將dll作爲文件添加到項目中(而不是作爲參考)。然後,打開文件的屬性並將生成操作設置爲「嵌入式資源」。建議您在項目結構中創建dll文件的本地副本,而不是鏈接到其他某個位置。一旦項目包含dll文件,您就可以添加對該dll副本的引用,以便在設計時使用它的內容。
這就確保了「其他」 DLL包含在您編譯DLL,但它不會使在需要的時候自動加載。這就是下面的代碼進來:
Public Module Core
Private _initialized As Boolean
Public Sub EnsureInitialized()
If Not _initialized Then
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf AssemblyResolve
_initialized = True
End If
End Sub
Private Function AssemblyResolve(ByVal sender As Object, ByVal e As ResolveEventArgs) As Assembly
Dim resourceFullName As String = String.Format("[CONTAINER ASSEMBLY].{0}.dll", e.Name.Split(","c)(0))
Dim thisAssembly As Assembly = Assembly.GetExecutingAssembly()
Using resource As Stream = thisAssembly.GetManifestResourceStream(resourceFullName)
If resource IsNot Nothing Then Return Assembly.Load(ToBytes(resource))
Return Nothing
End Using
End Function
Private Function ToBytes(ByVal instance As Stream) As Byte()
Dim capacity As Integer = If(instance.CanSeek, Convert.ToInt32(instance.Length), 0)
Using result As New MemoryStream(capacity)
Dim readLength As Integer
Dim buffer(4096) As Byte
Do
readLength = instance.Read(buffer, 0, buffer.Length)
result.Write(buffer, 0, readLength)
Loop While readLength > 0
Return result.ToArray()
End Using
End Function
End Module
將這個模塊在某處你的項目,並確保前致電EnsureInitialized
方法來連接AssemblyResolve
處理調用您的DLL任何其他代碼。
注意:您需要使用您的DLL的名稱替換[容器組件。
還要注意的是,上面的代碼是什麼,我們實際使用的,因爲我們包括戰略要地log4net的日誌消息的精簡版本。日誌消息不是真正的功能所必需的,所以爲了簡潔和清晰起見,我將它們刪除了。
主要告誡這種做法是,AssemblyResolve
處理程序必須手動連接。即使你不能設置,所以EnsureInitialized
在消費代碼的初始化期間只被調用一次,你可以在任何你自己的模塊中調用EnsureInitialized
,這些模塊需要執行「其他」dll。這會使代碼變得更加微妙,因爲您必須記住進行初始化調用,但它確實允許您在晚上睡覺,知道該DLL在您需要時可用。
以我的經驗,一些「其他」的dll沒有發揮好,當他們作爲嵌入的資源提供的,因此你可能需要了一下週圍玩把事情的工作。
Final注意:我從未使用ArcMap組件,所以您的里程可能會有所不同!
我採取了一點不同的方法。我想要的東西幾乎可以自動初始化,並在使用嵌入式程序集時動態加載嵌入式程序集。我也想避免加載已存在於當前AppDomain中的程序集的多個實例。下面的代碼爲我完成了所有這些。
Imports System.Reflection
Imports System.Runtime.CompilerServices
''' <summary>
''' This class initializes a special AssemblyResolve handler for assemblies embedded in the current assembly's resources. <para/>
''' To auto initialize create a variable as a New EmbeddedAssemblyResolverClass in any class using an embedded assembly.
''' </summary>
Public Class EmbeddedAssemblyResolverClass
Implements IDisposable
''' <summary>
''' Initialization flag.
''' </summary>
''' <returns>[Boolean]</returns>
Public ReadOnly Property Initialized As Boolean
''' <summary>
''' Raised when successfully initialized.
''' </summary>
Public Event Initilized()
''' <summary>
''' Raised when successfully uninitialized.
''' </summary>
Public Event Uninitilized()
Sub New()
Try
If Not Initialized Then
AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf ResolveAppDomainAssemblies
Initialized = True
RaiseEvent Initilized()
End If
Catch ex As Exception
'Maybe some error logging in the future.
MsgBox(ex.Message)
End Try
End Sub
#Region "IDisposable Support"
Private disposedValue As Boolean ' To detect redundant calls
' IDisposable
Protected Overridable Sub Dispose(disposing As Boolean)
If Not disposedValue Then
If disposing Then
RemoveHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf ResolveAppDomainAssemblies
_Initialized = False
RaiseEvent Uninitilized()
End If
End If
disposedValue = True
End Sub
' This code added by Visual Basic to correctly implement the disposable pattern.
Public Sub Dispose() Implements IDisposable.Dispose
' Do not change this code. Put cleanup code in Dispose(disposing As Boolean) above.
Dispose(True)
End Sub
#End Region
End Class
Public Module EmbeddedAssemblyResolverModule
''' <summary>
''' Returns a dictionary of assemblies loaded in the current AppDomain by full name as key.
''' </summary>
''' <returns>[Dictionary(Of String, Assembly)]</returns>
Public ReadOnly Property AppDomainAssemblies As Dictionary(Of String, Assembly)
Get
Return AppDomain.CurrentDomain.GetAssemblies.ToDictionary(Function(a) a.FullName)
End Get
End Property
''' <summary>
''' Method that attempts to resolve assemblies already loaded to the current AppDomain.
''' </summary>
''' <param name="sender">[Object]</param>
''' <param name="args">[ResolveEventArgs]</param>
''' <returns>[Assembly]</returns>
Public Function ResolveAppDomainAssemblies(sender As Object, args As ResolveEventArgs) As Assembly
'Return the existing assembly if it has already been loaded into the current AppDomain.
If AppDomainAssemblies.ContainsKey(args.Name) Then Return AppDomainAssemblies.Item(args.Name)
'Build the potential embedded resource name.
Dim ResourceName As String = String.Format("{0}.{1}.dll", Assembly.GetExecutingAssembly().FullName.Split(",").First, args.Name.Split(",").First)
'Attempt to load the requested assembly from the current assembly's embedded resources.
Return Assembly.GetExecutingAssembly.LoadEmbeddedAssembly(ResourceName)
End Function
''' <summary>
''' Loads an assembly from the current assembly's embedded resources.
''' </summary>
''' <param name="CurrentAssembly">[Assembly] Current assembly which contains the embedded assembly.</param>
''' <param name="EmbeddedAssemblyName">[String] Full name of the embedded assembly.</param>
''' <returns>[Assembly]</returns>
<Extension>
Public Function LoadEmbeddedAssembly(CurrentAssembly As Assembly, EmbeddedAssemblyName As String) As Assembly
'Return the existing assembly if it has already been loaded into the current AppDomain.
If AppDomainAssemblies.ContainsKey(EmbeddedAssemblyName) Then Return AppDomainAssemblies.Item(EmbeddedAssemblyName)
'Attempt to load the requested assembly from the current assembly's embedded resources.
Using Stream = CurrentAssembly.GetManifestResourceStream(EmbeddedAssemblyName)
If Stream Is Nothing Then Return Nothing
Dim RawAssembly As [Byte]() = New [Byte](Stream.Length - 1) {}
Stream.Read(RawAssembly, 0, RawAssembly.Length)
Return Assembly.Load(RawAssembly)
End Using
End Function
End Module
的EmbeddedAssemblyResolverClass
用於創建實際的AssemblyResolve事件處理程序。我通過爲Initialized和Uninitialized添加了IDisposable支持和事件添加了一些花裏胡哨的功能,但如果不需要,您可以將它們修剪掉。
我在EmbeddedAssemblyResolverModule
中創建了其餘代碼,以便它們對我的程序集是全局的,也因爲LoadEmbeddedAssembly方法是擴展方法,只能在Modules中創建。
現在唯一要做的就是在應用程序的任何其他類中創建並實例化EmbeddedAssemblyResolverClass
,該類使用嵌入其資源中的程序集。
'''' <summary>
'''' Used to auto initialize the EmbeddedAssemblyResolverClass.
'''' </summary>
Public WithEvents EAR As New EmbeddedAssemblyResolverClass
一旦從嵌入的資源調用的方法會先看看如果大會在當前AppDomain中已經加載,如果是,則返回大會。如果嵌入式程序集尚未加載,則它將從嵌入式資源動態加載(如果存在)。
這段代碼很好的一件事是它可以在沒有EntryPoint的程序集上工作,就像類庫一樣。另外,我也成功地使用嵌入式程序集來加載嵌入式程序集,並使用此代碼。
TLS,非常感謝。我在你提供的代碼中發現了一些奇怪的錯誤。 「FormatWith」不是System.String的成員,「First」不是System.Array的成員,「ToBytes」不是System.IO.Stream的成員。任何想法,我可以用這些功能替代? – 2012-03-13 05:55:40
對不起!我清理了它,但沒有足夠的。在我們的代碼中有一些擴展方法,我忘記了將其轉換回標準方法。我更新了代碼並添加了「ToBytes」的定義來完成示例。 – TLS 2012-03-13 14:21:36
我的答案本質上是[本C#答案](http://stackoverflow.com/a/97290/475820)的轉換。我在發佈了我的回覆後查看了這個答案後,我看到了這個。現在我們有一個C#和一個VB版本! – TLS 2012-03-13 14:25:26