Домены приложений и междоменное взаимодействие - очень тонкий вопрос, поэтому нужно убедиться, что он действительно понимает, как все работает, прежде чем что-то делать ... Ммм ... Скажем, "нестандартный": -)
Прежде всего, ваш метод создания потока фактически выполняется в вашем домене по умолчанию (сюрприз-сюрприз!). Зачем? Все просто: метод, который вы передаете в AppDomain.DoCallBack
, определен для объекта AppDomainDelegateWrapper
, и этот объект существует в вашем домене по умолчанию, поэтому его метод выполняется. MSDN не говорит об этой маленькой «функции», но ее достаточно легко проверить: просто установите точку останова в AppDomainDelegateWrapper.Invoke
.
Так что, по сути, вы должны обходиться без объекта-обертки. Используйте статический метод для аргумента DoCallBack.
Но как передать свой аргумент "func" в другой домен, чтобы ваш статический метод мог его взять и выполнить?
Наиболее очевидный способ - использовать AppDomain.SetData
, или вы можете бросить свой собственный, но независимо от того, как именно вы это делаете, есть другая проблема: если «func» - это нестатический метод, тогда объект, который он должен быть каким-то образом передан в другой домен приложения. Он может быть передан либо по значению (тогда как оно копируется, поле за полем), либо по ссылке (создавая междоменную ссылку на объект со всей красотой Remoting). Прежде всего, класс должен быть помечен атрибутом [Serializable]
. Для этого он должен наследовать от MarshalByRefObject
. Если класс не равен ни одному, исключение будет выдано при попытке передать объект в другой домен. Имейте в виду, однако, что передача по ссылке в значительной степени убивает саму идею, потому что ваш метод будет по-прежнему вызываться в том же домене, в котором находится объект, то есть по умолчанию.
Завершая вышеприведенный абзац, у вас остается две опции: либо передать метод, определенный для класса, помеченного атрибутом [Serializable]
(и помните, что объект будет скопирован), либо передать статический метод. Я подозреваю, что для ваших целей вам понадобится первое.
И на всякий случай, если это ускользнуло от вашего внимания, я хотел бы отметить, что ваша вторая перегрузка RunInAppDomain
(та, которая занимает Action
) передает метод, определенный в классе, который не помечен [Serializable]
. Не видите там ни одного класса? Вам не нужно: с анонимными делегатами, содержащими связанные переменные, компилятор создаст их для вас. И так уж получилось, что компилятор не потрудился пометить этот автоматически сгенерированный класс [Serializable]
. Жаль, но это жизнь: -)
Сказав все это (много слов, не так ли? :-), и принимая ваше обещание не передавать какие-либо нестатические и не [Serializable]
методы, вот ваши новые RunInAppDomain
методы:
/// <summary>
/// Executes a method in a separate AppDomain. This should serve as a simple replacement
/// of running code in a separate process via a console app.
/// </summary>
public static T RunInAppDomain<T>(Func<T> func)
{
AppDomain domain = AppDomain.CreateDomain("Delegate Executor " + func.GetHashCode(), null,
new AppDomainSetup { ApplicationBase = Environment.CurrentDirectory });
try
{
domain.SetData("toInvoke", func);
domain.DoCallBack(() =>
{
var f = AppDomain.CurrentDomain.GetData("toInvoke") as Func<T>;
AppDomain.CurrentDomain.SetData("result", f());
});
return (T)domain.GetData("result");
}
finally
{
AppDomain.Unload(domain);
}
}
[Serializable]
private class ActionDelegateWrapper
{
public Action Func;
public int Invoke()
{
Func();
return 0;
}
}
public static void RunInAppDomain(Action func)
{
RunInAppDomain<int>( new ActionDelegateWrapper { Func = func }.Invoke );
}
Если ты все еще со мной, я ценю: -)
Теперь, потратив так много времени на исправление этого механизма, я скажу вам, что в любом случае это было бесполезно.
Дело в том, что домены приложений не помогут вам в ваших целях. Они заботятся только об управляемых объектах, в то время как неуправляемый код может просочиться и привести к сбою. Неуправляемый код даже не знает, что существуют такие вещи, как домены приложений. Он знает только о процессах.
Итак, в конце концов, вашим лучшим вариантом остается текущее решение: просто создайте другой процесс и будьте довольны этим. И, я бы согласился с предыдущими ответами, вам не нужно писать другое консольное приложение для каждого случая. Просто передайте полное имя статического метода, и консольное приложение загрузит вашу сборку, загрузит ваш тип и вызовет метод. На самом деле вы можете упаковать его довольно аккуратно, почти так же, как вы пытались использовать с доменами приложений. Вы можете создать метод с именем что-то вроде «RunInAnotherProcess», который будет проверять аргумент, извлекать из него полное имя типа и метода (при этом удостоверившись, что метод статический) и порождая консольное приложение, которое сделает все остальное.