Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
.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, поскольку это может привести к переполнению стека. (См. список, указанный ранее в этом разделе.) Это происходит, даже если вы предоставляете обработку исключений для запроса загрузки, так как исключение не создается до тех пор, пока не будут возвращены все обработчики событий. Таким образом, следующий код приводит к переполнению стека, если 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. Вместо этого используйте 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);
}
}