.NET은 어셈블리 로드를 보다 세게 제어해야 하는 애플리케이션에 대한 이벤트를 제공합니다 AppDomain.AssemblyResolve . 이 이벤트를 처리하여 애플리케이션은 일반 검색 경로 외부에서 로드 컨텍스트로 어셈블리를 로드하고, 로드할 여러 어셈블리 버전을 선택하고, 동적 어셈블리를 내보내고, 반환하는 등의 작업을 수행할 수 있습니다. 이 항목에서는 이벤트 처리 AssemblyResolve 에 대한 지침을 제공합니다.
비고
리플렉션 전용 컨텍스트에서 어셈블리 로드를 확인하려면 대신 이벤트를 사용합니다 AppDomain.ReflectionOnlyAssemblyResolve .
AssemblyResolve 이벤트의 작동 방식
이벤트에 대한 AssemblyResolve 처리기를 등록하면 런타임이 이름으로 어셈블리에 바인딩하지 못할 때마다 처리기가 호출됩니다. 예를 들어 사용자 코드에서 다음 메서드를 호출하면 AssemblyResolve 이벤트가 발생할 수 있습니다.
AppDomain.Load 메서드 오버로드 또는 Assembly.Load 메서드 오버로드로, 첫 번째 인수가 로드할 어셈블리의 표시 이름을 나타내는 문자열입니다(즉, 속성에서 반환된 Assembly.FullName 문자열).
AppDomain.Load 메서드 오버로드 또는 Assembly.Load 메서드 오버로드로, 첫 번째 인수가 로드할 어셈블리를 식별하는 개체인 AssemblyName입니다.
Assembly.LoadWithPartialName 메서드 오버로드입니다.
AppDomain.CreateInstance 또는 AppDomain.CreateInstanceAndUnwrap 메서드 오버로드는 다른 애플리케이션 도메인 중 하나에서 개체를 인스턴스화합니다.
이벤트 처리기가 수행하는 기능
이벤트에 대한 AssemblyResolve 처리기는 로드할 어셈블리의 표시 이름을 ResolveEventArgs.Name 속성에서 받습니다. 처리기가 어셈블리 이름을 인식하지 못하면 null
(C#), Nothing
(Visual Basic) 또는 nullptr
(Visual C++)을 반환합니다.
처리기가 어셈블리 이름을 인식하는 경우 요청을 충족하는 어셈블리를 로드하고 반환할 수 있습니다. 다음 목록에서는 몇 가지 샘플 시나리오를 설명합니다.
처리기가 어셈블리 버전의 위치를 알고 있는 경우 또는 Assembly.LoadFrom 메서드를 사용하여 Assembly.LoadFile 어셈블리를 로드할 수 있으며, 성공적으로 로드된 어셈블리를 반환할 수 있습니다.
처리기가 바이트 배열로 저장된 어셈블리 데이터베이스에 액세스할 수 있는 경우 바이트 배열을 사용하는 메서드 오버로드 중 Assembly.Load 하나를 사용하여 바이트 배열을 로드할 수 있습니다.
처리기는 동적 어셈블리를 생성하고 반환할 수 있습니다.
비고
처리기는 어셈블리를 load-from 컨텍스트, 로드 컨텍스트, 또는 컨텍스트 없이 로드해야 합니다. 처리기가 Assembly.ReflectionOnlyLoad 방법 또는 Assembly.ReflectionOnlyLoadFrom 메서드를 사용하여 어셈블리를 리플렉션 전용 컨텍스트에 로드하는 경우, AssemblyResolve 이벤트를 발생시킨 로드 시도가 실패합니다.
적절한 어셈블리를 반환하는 것은 이벤트 처리기의 책임입니다. 처리기는 요청된 어셈블리의 표시 이름을 구문 분석할 수 있도록 ResolveEventArgs.Name 속성 값을 AssemblyName(String) 생성자에 전달할 수 있습니다. .NET Framework 4부터 처리기는 속성을 사용하여 ResolveEventArgs.RequestingAssembly 현재 요청이 다른 어셈블리의 종속성인지 여부를 확인할 수 있습니다. 이 정보는 종속성을 충족하는 어셈블리를 식별하는 데 도움이 될 수 있습니다.
이벤트 처리기는 요청된 버전과 다른 버전의 어셈블리를 반환할 수 있습니다.
대부분의 경우 처리기가 반환하는 어셈블리는 처리기가 로드하는 컨텍스트에 관계없이 로드 컨텍스트에 표시됩니다. 예를 들어 처리기가 메서드를 Assembly.LoadFrom 사용하여 어셈블리를 로드 원본 컨텍스트로 로드하는 경우 어셈블리는 처리기가 반환할 때 로드 컨텍스트에 표시됩니다. 그러나 다음 경우 어셈블리는 처리기가 반환할 때 컨텍스트 없이 표시됩니다.
처리기는 컨텍스트 없이 어셈블리를 로드합니다.
속성이 ResolveEventArgs.RequestingAssembly null이 아닙니다.
요청 어셈블리(즉, 속성에서 ResolveEventArgs.RequestingAssembly 반환되는 어셈블리)가 컨텍스트 없이 로드되었습니다.
컨텍스트에 대한 자세한 내용은 메서드 오버로드를 Assembly.LoadFrom(String) 참조하세요.
동일한 어셈블리의 여러 버전을 동일한 애플리케이션 도메인에 로드할 수 있습니다. 형식 할당 문제가 발생할 수 있으므로 이 방법은 권장되지 않습니다. 어셈블리 로드에 대한 모범 사례를 참조하세요.
이벤트 처리기가 수행하지 않아야 하는 사항
이벤트 처리를 AssemblyResolve 위한 기본 규칙은 인식할 수 없는 어셈블리를 반환하려고 시도해서는 안 된다는 것입니다. 처리기를 작성할 때 이벤트를 발생시킬 수 있는 어셈블리를 알아야 합니다. 처리기는 다른 어셈블리에 대해 null을 반환해야 합니다.
중요합니다
.NET Framework 4에서 위성 어셈블리에 대한 AssemblyResolve 이벤트가 시작됩니다. 이 변경 내용은 처리기가 모든 어셈블리 로드 요청을 해결하려고 하는 경우 이전 버전의 .NET Framework용으로 작성된 이벤트 처리기에 영향을 줍니다. 인식할 수 없는 어셈블리를 무시하는 이벤트 처리기는 이 변경의 영향을 받지 않습니다. 반환 null
되며 일반적인 대체 메커니즘이 뒤따릅니다.
어셈블리를 로드할 때, 이벤트 처리기가 AppDomain.Load 또는 Assembly.Load 메서드 오버로드 중 하나를 사용하지 않도록 해야 합니다. 이러한 사용은 AssemblyResolve 이벤트가 재귀적으로 발생하게 되어 스택 오버플로로 이어질 수 있기 때문입니다. (이 항목의 앞부분에서 제공된 목록을 참조하세요.) 이는 모든 이벤트 처리기가 반환될 때까지 예외가 throw되지 않으므로 부하 요청에 대한 예외 처리를 제공하는 경우에도 발생합니다. 따라서 다음 코드를 찾을 수 없는 경우 MyAssembly
스택 오버플로가 발생합니다.
using System;
using System.Reflection;
class BadExample
{
static void Main()
{
AppDomain ad = AppDomain.CreateDomain("Test");
ad.AssemblyResolve += MyHandler;
try
{
object obj = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static Assembly MyHandler(object source, ResolveEventArgs e)
{
Console.WriteLine("Resolving {0}", e.Name);
// DO NOT DO THIS: This causes a StackOverflowException
return Assembly.Load(e.Name);
}
}
/* This example produces output similar to the following:
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Process is terminated due to StackOverflowException.
*/
Imports System.Reflection
Class BadExample
Shared Sub Main()
Dim ad As AppDomain = AppDomain.CreateDomain("Test")
AddHandler ad.AssemblyResolve, AddressOf MyHandler
Try
Dim obj As object = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Shared Function MyHandler(ByVal source As Object, _
ByVal e As ResolveEventArgs) As Assembly
Console.WriteLine("Resolving {0}", e.Name)
// DO NOT DO THIS: This causes a StackOverflowException
Return Assembly.Load(e.Name)
End Function
End Class
' This example produces output similar to the following:
'
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'...
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
'
'Process is terminated due to StackOverflowException.
using namespace System;
using namespace System::Reflection;
ref class Example
{
internal:
static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
{
Console::WriteLine("Resolving {0}", e->Name);
// DO NOT DO THIS: This causes a StackOverflowException
return Assembly::Load(e->Name);
}
};
void main()
{
AppDomain^ ad = AppDomain::CreateDomain("Test");
ad->AssemblyResolve += gcnew ResolveEventHandler(&Example::MyHandler);
try
{
Object^ obj = ad->CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType");
}
catch (Exception^ ex)
{
Console::WriteLine(ex->Message);
}
}
/* This example produces output similar to the following:
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
...
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Resolving MyAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=null
Process is terminated due to StackOverflowException.
*/
AssemblyResolve를 처리하는 올바른 방법
이벤트 처리기에서 어셈블리를 AssemblyResolve 통해 확인하는 경우, 처리기가 StackOverflowException 메서드 호출을 사용하면 결국 Assembly.Load 또는 AppDomain.Load 예외가 발생할 것입니다. 대신 LoadFile 또는 LoadFrom 메서드를 사용하세요. 이 메서드들은 AssemblyResolve
이벤트를 발생시키지 않습니다.
MyAssembly.dll
가 실행 중인 어셈블리 근처에 위치해 있으며, 어셈블리에 대한 경로가 주어질 경우 Assembly.LoadFile
을 사용하여 이를 해결할 수 있다고 가정해 보십시오.
using System;
using System.IO;
using System.Reflection;
class CorrectExample
{
static void Main()
{
AppDomain ad = AppDomain.CreateDomain("Test");
ad.AssemblyResolve += MyHandler;
try
{
object obj = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
static Assembly MyHandler(object source, ResolveEventArgs e)
{
Console.WriteLine("Resolving {0}", e.Name);
var path = Path.GetFullPath("../../MyAssembly.dll");
return Assembly.LoadFile(path);
}
}
Imports System.IO
Imports System.Reflection
Class CorrectExample
Shared Sub Main()
Dim ad As AppDomain = AppDomain.CreateDomain("Test")
AddHandler ad.AssemblyResolve, AddressOf MyHandler
Try
Dim obj As Object = ad.CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType")
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Sub
Shared Function MyHandler(ByVal source As Object,
ByVal e As ResolveEventArgs) As Assembly
Console.WriteLine("Resolving {0}", e.Name)
Dim fullPath = Path.GetFullPath("../../MyAssembly.dll")
Return Assembly.LoadFile(fullPath)
End Function
End Class
using namespace System;
using namespace System::IO;
using namespace System::Reflection;
ref class Example
{
internal:
static Assembly^ MyHandler(Object^ source, ResolveEventArgs^ e)
{
Console::WriteLine("Resolving {0}", e->Name);
String^ fullPath = Path::GetFullPath("../../MyAssembly.dll");
return Assembly::LoadFile(fullPath);
}
};
void main()
{
AppDomain^ ad = AppDomain::CreateDomain("Test");
ad->AssemblyResolve += gcnew ResolveEventHandler(&Example::MyHandler);
try
{
Object^ obj = ad->CreateInstanceAndUnwrap(
"MyAssembly, version=1.2.3.4, culture=neutral, publicKeyToken=null",
"MyType");
}
catch (Exception^ ex)
{
Console::WriteLine(ex->Message);
}
}
참고하십시오
.NET