У меня есть сборка в памяти в формате COFF, которая используется в моей основной сборке MyApp.exe
:
byte[] assemblyData = GetAssemblyDataFromSomewhere();
(Для тестирования метод GetAssemblyDataFromSomewhere
может просто сделать File.ReadAllBytes
дляфайл существующей сборки, но в моем реальном приложении нет файла.)
Давайте назовем эту сборку MyAssembly
.Он имеет только ссылки .NET Framework и не имеет никаких зависимостей от любого другого пользовательского кода.
Я могу загрузить эту сборку в текущий (по умолчанию) AppDomain
:
Assembly.Load(assemblyData);
// this works
var obj = Activator.CreateInstance("MyAssembly", "MyNamespace.MyType").Unwrap();
Теперь яхотите загрузить эту сборку в другой AppDomain
и создать экземпляр класса там.MyNamespace.MyType
является производным от MarshalByRefObject
, поэтому я могу предоставить общий доступ к экземпляру в доменах приложений.
var newAppDomain = AppDomain.CreateDomain("DifferentAppDomain");
// this doesn't really work...
newAppDomain.Load(assemblyData);
// ...because this throws a FileNotFoundException
var obj = newAppDomain.CreateInstanceAndUnwrap("MyAssembly", "MyNamespace.MyType");
Да, я знаю, что в AppDomain.Load
документах есть примечание:
Этот метод следует использовать только для загрузки сборки в текущий домен приложения.
Да, для этого нужно использовать ,но ...
Если текущий объект AppDomain
представляет домен приложения A , а метод Load
вызывается из домена приложения B ,сборка загружается в оба домена приложения.
Я могу жить с этим.Для меня нет проблем, если сборка будет загружена в оба домена приложения (потому что я все равно загружаю ее в домен приложения по умолчанию).
Я вижу, что сборка загружена в новый домен приложения.Вид.
var assemblies = newAppDomain.GetAssemblies().Select(a => a.GetName().Name);
Console.WriteLine(string.Join("\r\n", assemblies));
Это дает мне:
mscorlib
MyAssembly
Но попытка создания экземпляра класса всегда приводит к FileNotFoundException
, потому что CLR пытается загрузить сборку из файла (несмотря на то, что он уже загружен, по крайней мере в соответствии с AppDomain.GetAssemblies
).
Я мог бы сделать это в MyApp.exe
:
newAppDomain.AssemblyResolve += CustomResolver;
private static Assembly CustomResolver(object sender, ResolveEventArgs e)
{
byte[] assemblyData = GetAssemblyDataFromSomewhere();
return Assembly.Load(assemblyData);
}
Это работает, но это приводит к тому, что второй домен приложениязагрузить вызывающую сборку (MyApp.exe
) из файла.Это происходит потому, что домену приложения теперь нужен код (метод CustomResolver
) из вызывающей сборки.
Я мог бы переместить логику создания домена приложения и обработчик событий в другую сборку, например, MyAppServices.dll
,поэтому новый домен приложения будет загружать эту сборку вместо MyApp.exe
.
Однако я хочу любой ценой избежать доступа файловой системы к каталогу моего приложения: новый домен приложения не должен загружать какие-либо пользовательские сборки изфайлы.
Я также пробовал AppDomain.DefineDynamicAssembly
, но это тоже не сработало, потому что тип возвращаемого значения System.Reflection.Emit.AssemblyBuilder
не MarshalByRefObject
и не помечен [Serializable]
.
IsЕсть ли способ загрузить сборку из байтового массива в нестандартный AppDomain
без загрузки вызывающей сборки из файла в этот домен приложения?На самом деле, без какой-либо файловой системы доступ к каталогу моего приложения?