나는 약간 다른 접근 방식을 취했습니다. 필자는 사용되는 임베디드 어셈블리를 자동으로 초기화하고 동적으로로드하는 기능을 원했습니다. 또한 현재 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 지원 및 이벤트를 Initialized 및 Uninitialized에 추가하여 몇 가지 종소리와 휘파람을 추가했지만, 필요하지 않으면 해제 할 수 있습니다.
나머지 어셈블리 코드를 EmbeddedAssemblyResolverModule
에 작성하여 내 어셈블리에 전역 적으로 적용 할 수 있으며 또한 LoadEmbeddedAssembly 메서드가 Extension 메서드이고 Modules에만 만들 수 있기 때문에 사용할 수 있습니다.
이제 할 일은 리소스에 포함 된 어셈블리를 사용하는 응용 프로그램의 다른 클래스에서 EmbeddedAssemblyResolverClass
을 만들고 인스턴스화하는 것입니다.
'''' <summary>
'''' Used to auto initialize the EmbeddedAssemblyResolverClass.
'''' </summary>
Public WithEvents EAR As New EmbeddedAssemblyResolverClass
이 포함 된 리소스에서 메소드를 호출하면 처음 어셈블리가 이미 현재 응용 프로그램 도메인에로드 된 경우 다음 어셈블리가 반환되는 경우, 참조 찾을 것입니다. 임베디드 어셈블리가로드되지 않은 경우 임베디드 어셈블리가있는 경우 동적으로로드됩니다.
이 코드에 대한 좋은 점 중 하나는 클래스 라이브러리와 같은 EntryPoint가없는 어셈블리에서 작동한다는 것입니다. 또한이 코드를 사용하는 어셈블리가 포함 된 임베디드 어셈블리를로드하는 데 성공했습니다.
TLS, 정말 고마워요. 내가 제공 한 코드에 이상한 오류가 발견되었습니다. "FormatWith"는 System.String의 멤버가 아니며 "First"는 System.Array의 멤버가 아니며 "ToBytes"는 System.IO.Stream의 멤버가 아닙니다. 내가 그 기능을 대체 할 수있는 아이디어가 있습니까? –
죄송합니다! 나는 그것을 정화했으나 충분히 멀리 가지 않았다. 우리가 코드에서 가지고있는 확장 메서드 중 일부는 표준 메서드로 다시 변환하는 것을 잊었습니다. 코드를 업데이트하고 예제를 완성하기 위해'ToBytes'의 정의를 추가했습니다. – TLS
내 대답은 본질적으로 [this C# answer] (http://stackoverflow.com/a/97290/475820)의 변환입니다. 내 답변을 검토 한 후이 게시물을 보았습니다. 이제 우리는 C#과 VB 버전을 가지고 있습니다! – TLS