Ответ 1
Хотя вы можете сделать вызов в делегат, который будет обрабатываться отдельным AppDomain, я лично всегда использовал метод CreateInstanceAndUnwrap, который создает объект в чужом домене приложения и возвращает ему прокси.
Для этого ваш объект должен наследовать от MarshalByRefObject.
Вот пример:
public interface IRuntime
{
bool Run(RuntimesetupInfo setupInfo);
}
// The runtime class derives from MarshalByRefObject, so that a proxy can be returned
// across an AppDomain boundary.
public class Runtime : MarshalByRefObject, IRuntime
{
public bool Run(RuntimeSetupInfo setupInfo)
{
// your code here
}
}
// Sample code follows here to create the appdomain, set startup params
// for the appdomain, create an object in it, and execute a method
try
{
// Construct and initialize settings for a second AppDomain.
AppDomainSetup domainSetup = new AppDomainSetup()
{
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
LoaderOptimization = LoaderOptimization.MultiDomainHost
};
// Create the child AppDomain used for the service tool at runtime.
childDomain = AppDomain.CreateDomain(
"Your Child AppDomain", null, domainSetup);
// Create an instance of the runtime in the second AppDomain.
// A proxy to the object is returned.
IRuntime runtime = (IRuntime)childDomain.CreateInstanceAndUnwrap(
typeof(Runtime).Assembly.FullName, typeof(Runtime).FullName);
// start the runtime. call will marshal into the child runtime appdomain
return runtime.Run(setupInfo);
}
finally
{
// runtime has exited, finish off by unloading the runtime appdomain
if(childDomain != null) AppDomain.Unload(childDomain);
}
В приведенном выше примере закодировано для выполнения метода "Запуск", передаваемого в некоторой информации об установке, и завершение метода "Выполнить" определяется, чтобы указать, что весь код в дочернем домене AppDomain завершен, поэтому мы имеем наконец блок, обеспечивающий выгрузку AppDomain.
Часто вы можете быть осторожны, какие типы вы размещаете в сборках - вы можете использовать интерфейс и поместить его в отдельную сборку, которая и вызывающая программа (наш код, который устанавливает приложение, и вызывает его), и разработчик (класс Runtime) зависит от. Этот IIRC позволяет родительскому AppDomain загружать только сборку, содержащую интерфейс, в то время как дочерний домен appdomain загружает как сборку, содержащую Runtime, так и ее зависимость (сборку IRuntime). Любые пользовательские типы, которые используются интерфейсом IRuntime (например, наш класс RuntimeSetupInfo), обычно также должны быть помещены в ту же сборку, что и IRuntime. Кроме того, будьте осторожны с тем, как вы определяете эти пользовательские типы - если они являются объектами передачи данных (вероятно, это RuntimeSetupInfo), вы должны, вероятно, пометить их атрибутом [serializable] - чтобы была передана копия объекта (сериализована из родительский appdomain для ребенка). Вы хотите избежать перенаправления вызовов из одного приложения в другой, поскольку это довольно медленно. Передача DTO по значению (сериализация) означает, что доступ к значениям в DTO не требует межсетевого вызова (так как у дочернего appdomain есть собственная копия оригинала). Разумеется, это также означает, что изменения стоимости не отражаются в исходном исходном DTO родительского приложения.
Как закодировано в примере, родительский appdomain фактически загрузит как сборки IRuntime, так и Runtime, но это связано с тем, что при вызове CreateInstanceAndUnwrap я использую typeof (Runtime), чтобы получить имя сборки и полное имя типа, Вместо этого вы можете жестко задавать или извлекать эти строки из файла, что отделяет зависимость.
Также есть метод в AppDomain с именем "DoCallBack", который выглядит так, будто он позволяет вызывать делегат в чужом AppDomain. Однако тип делегата, который он принимает, имеет тип "CrossAppDomainDelegate". Определение которого:
public delegate void CrossAppDomainDelegate()
Таким образом, он не позволит вам передавать в него какие-либо данные. И, так как я никогда не использовал его, я не могу сказать вам, есть ли какие-то особые проблемы.
Кроме того, я бы рекомендовал изучить свойство LoaderOptimization. То, что вы установили для этого, может существенно повлиять на производительность, поскольку некоторые настройки этого свойства заставляют новый appdomain загружать отдельные копии всех сборок (и JIT их и т.д.), Даже если (IIRC) сборка находится в GAC ( т.е. это включает сборки CLR). Это может дать вам ужасную производительность, если вы используете большое количество сборок из вашего дочернего приложения. Например, я использовал WPF из дочерних доменов, что вызвало большие задержки запуска для моего приложения, пока я не установил более подходящую политику загрузки.