2012-03-13 24 views
4

これはC#で行われたのですが、hereなどですが、VB.NETでこれを行う方法を理解できないようです。いくつかの背景のために、カスタムのComboBoxコントロールを.dllとして作成しました。これを別の.dll(ArcMapコンポーネント)に実装する必要があります。埋め込みリソースとして別のDLLにVB.NET埋め込みDLL?

残念ながら、ArcMapでは、アドインのサードパーティ製アセンブリを参照するオプションがないため、ArcMapではコンポーネントとともに「サードパーティ製」DLLを読み込むことはできません。

誰かが私を正しい方向に向けることができれば、それは高く評価されます。

我々は、Visual Studio 2008でVB.NETでこの手法を使用

答えて

9

...

まず、プロジェクトが埋め込まれたリソースとして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 

置き、このモジュールをして、あなたのDLL内の他のコードを呼び出す前AssemblyResolveハンドラを添付するEnsureInitializedメソッドを呼び出してください:以下のコードの出番です。

注:[CONTAINER ASSEMBLY]をdllの名前に置き換える必要があります。

また、上記のコードは、戦略的な場所でlog4netのログメッセージが含まれているため、実際に使用しているものを除外したものです。ロギングメッセージは本当の機能性のために必要ではないので、わかりやすく簡潔にするためにロギングメッセージを削除しました。

この方法の主な注意点は、AssemblyResolveハンドラを手動で接続する必要があることです。消費コードの初期化中にEnsureInitializedが一度だけ呼び出されるように設定することができない場合でも、 "other" dllが必要なモジュール内でEnsureInitializedを呼び出すことができます。これはコードを少し繊細にしますが、その初期化の呼び出しを覚えておく必要があるためですが、必要なときにdllが利用可能になることを知って夜間に眠ることができます。

私の経験では、埋め込まれたリソースとして提供されていると「他の」dllファイルがうまく再生されないため、作業を進めるために少しでも遊ぶ必要があるかもしれません。

最終ノート:私はArcMapコンポーネントを使用したことがないので、あなたのマイルは変更されることがあります!

+0

TLSは、どうもありがとうございました。あなたが提供したコードに奇妙なエラーが見つかりました。 「FormatWithは」可能System.Stringのメンバーではない、「第一」のSystem.Arrayのメンバーではない、と「ToBytes」System.IO.Streamのメンバーではありません。どのようなアイデアを私はそれらの機能を置き換えることができますか? –

+0

申し訳ありません!私はそれをきれいにしましたが、十分には行きませんでした。私たちのコードには、標準的なメソッドに変換するのを忘れてしまった拡張メソッドがいくつかあります。私はコードを更新し、例を完成させるために 'ToBytes'の定義を追加しました。 – TLS

+0

私の答えは、本質的に[このC#の答え(http://stackoverflow.com/a/97290/475820)の変換です。私は私の投稿後にその答えを見直した後にこれを見る。今はC#とVB版です! – TLS

0

少し違ったアプローチをとった。私は、使用されている組み込みアセンブリを自動で初期化し、動的にロードするものを望んでいました。また、現在の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イベントハンドラの作成に使用されます。私は初期化され、初期化されていないためIDisposableをサポートし、イベントを追加することによって、いくつかの添えものを追加しましたが、希望しない場合は、それらをオフにトリミングすることができます。

残りのコードをEmbeddedAssemblyResolverModuleに作成しました。これは、アセンブリにグローバルになるように、また、LoadEmbeddedAssemblyメソッドがExtensionメソッド(モジュールでのみ作成可能)になっているためです。

残っている唯一のことは、リソース内に埋め込まれているアセンブリを使用するアプリケーション内の他のクラスにあるEmbeddedAssemblyResolverClassを作成してインスタンス化することです。

'''' <summary> 
'''' Used to auto initialize the EmbeddedAssemblyResolverClass. 
'''' </summary> 
Public WithEvents EAR As New EmbeddedAssemblyResolverClass 

あなたが埋め込まれたリソースからメソッドをコールすると、それは最初のアセンブリがすでに現在のAppDomainにロードされている場合、それはその後、アセンブリが返されているかどうかを確認することになります。埋め込みアセンブリが読み込まれていない場合、埋め込みアセンブリが存在する場合は、組み込みアセンブリから動的に読み込まれます。このコードについての素晴らしいです

ことの一つは、それがクラスライブラリのように、エントリーポイントを持っていないアセンブリに動作することです。また、このコードを使用した組み込みアセンブリを含む組み込みアセンブリの読み込みに成功しました。