Загрузка / выгрузка сборки в другой домен приложений
Мне нужно выполнить метод в сборке, загруженной во время выполнения. Теперь я хочу выгрузить эти загруженные сборки после вызова метода. Я знаю, что мне нужен новый домен приложений, чтобы я мог выгружать библиотеки. Но здесь возникает проблема.
Сборки, которые будут загружаться, являются плагинами в моей структуре плагинов. У них вообще нет точки входа. Все, что я знаю, это то, что они содержат некоторые типы, которые реализуют данный интерфейс. Старый не-AppDomain-код выглядит следующим образом (немного сокращен):
try
{
string path = Path.GetFullPath("C:\library.dll");
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Assembly asm = Assembly.LoadFrom(path);
Type[] types = asm.GetExportedTypes();
foreach (Type t in types)
{
if ((t.GetInterface("IStarter") != null) && !t.IsAbstract)
{
object tempObj = Activator.CreateInstance(t);
MethodInfo info = t.GetMethod("GetParameters");
if (info != null)
{
return info.Invoke(tempObj, null) as string;
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.StartsWith("MyProject.View,"))
{
string path = Path.GetFullPath("C:\view.dll"));
return Assembly.LoadFrom(path);
}
return null;
}
Теперь я хочу, чтобы они загрузили свой собственный домен приложений:
try
{
string path = Path.GetFullPath("C:\library.dll");
AppDomain domain = AppDomain.CreateDomain("TempDomain");
domain.AssemblyResolve += CurrentDomain_AssemblyResolve; // 1. Exception here!!
domain.ExecuteAssembly(path); // 2. Exception here!!
domain.CreateInstanceFrom(...); // 3. I have NO clue, how the type is named.
domain.Load(...); // 4. I have NO clue, how the assembly is named.
domain.DoCallBack(...); // 5. Exception here!!
// ...
}
catch (Exception ex)
{
MessageBox.Show(String.Format("Damn '{0}'.", ex.Message), "Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
Как видите, я поставил в 5 случаях.
Если я устанавливаю обработчик событий, я получаю исключение, что сборка (это консоль управления (mmc.exe) SnapIn. Не может быть найдена / загружена.
ExecuteAssembly не находит точку входа (ну, ее нет).
Я понятия не имею, как тип назван. Как загрузить по интерфейсу?
Похоже на 3. Как получить название сборки?
Та же ошибка, что и в 1.
Я думаю, что проблема может заключаться в том, что консоль управления каким-то образом или я просто не знаю, что я делаю неправильно. Любая помощь приветствуется.
ОБНОВЛЕНИЕ 1
Сейчас я попытался использовать опубликованный прокси-решение.
AppDomain domain = AppDomain.CreateDomain("TempDomain");
InstanceProxy proxy = domain.CreateInstanceAndUnwrap(Assembly.GetAssembly(
typeof(InstanceProxy)).FullName, typeof(InstanceProxy).ToString()) as InstanceProxy;
if (proxy != null)
{
proxy.LoadAssembly(path);
}
AppDomain.Unload(domain);
public class InstanceProxy : MarshalByRefObject
{
public void LoadAssembly(string path)
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
Assembly asm = Assembly.LoadFrom(path);
Type[] types = asm.GetExportedTypes();
// ...see above...
}
}
Это тоже не работает. При попытке создать объект прокси я получаю исключение:
Не удалось загрузить файл или сборку 'MyProject.SnapIn, Version=1.0.0.0, Culture= нейтральный, PublicKeyToken=null' или одну из ее зависимостей. Система не может найти указанный файл.
Файл в сообщении об ошибке загружен в mmc (SnapIn). Есть идеи, как исправить эту ошибку? AppDomain.AssemblyResolve не вызывается (ни в старом, ни в новом домене).
ОБНОВЛЕНИЕ 2
Теперь я попробовал решение с AppDomainSetup. Теперь исключение изменилось на:
Не удалось загрузить файл или сборку 'file:///C:/Development/MyProject/bin/SnapIn/MyProject.SnapIn.DLL' или одну из ее зависимостей. Указанное имя сборки или кодовая база недопустимы. (Исключение из HRESULT: 0x80131047)
Любая идея?
3 ответа
Попробуй это:
namespace SeperateAppDomainTest
{
class Program
{
static void Main(string[] args)
{
LoadAssembly();
}
public static void LoadAssembly()
{
string pathToDll = Assembly.GetExecutingAssembly().CodeBase;
AppDomainSetup domainSetup = new AppDomainSetup { PrivateBinPath = pathToDll };
var newDomain = AppDomain.CreateDomain("FooBar", null, domainSetup);
ProxyClass c = (ProxyClass)(newDomain.CreateInstanceFromAndUnwrap(pathToDll, typeof(ProxyClass).FullName));
Console.WriteLine(c == null);
Console.ReadKey(true);
}
}
public class ProxyClass : MarshalByRefObject { }
Взгляните на этот предыдущий ответ: Как загрузить сборку в другой домен приложений в Windows Mobile (.NET CF)?, Этот ответ создает прокси-класс, который работает в новом контексте AppDomain, поэтому вы можете полностью контролировать инициализацию.
Вы могли бы создать Start()
метод в ServiceApplicationProxy
класс и просто позвонить как обычно от вашего хостера с proxy.Start()
,
https://msdn.microsoft.com/en-us/library/3c4f1xde%28v=vs.110%29.aspx
указывает, что
typeName Тип: System.String
The fully qualified name of the requested type, including the namespace but not the assembly, as returned by the Type.FullName
имущество.
Поэтому попробуйте позвонить с полностью определенным именем, а не с помощью typeof(InstanceProxy).ToString()
использовать строку / текст "<<Namespace>>.InstanceProxy"
как показано ниже
InstanceProxy proxy = domain.CreateInstanceAndUnwrap(path, "<<Namespace>>.InstanceProxy") as InstanceProxy;