Невозможно привести прозрачный прокси в dll при вызове из PowerShell, но успешно в консольном приложении C#
Я пытаюсь создать библиотеку с открытым исходным кодом, которая порождает новый AppDomain
и управляет PowerShell
скрипт в нем. У меня есть статический метод, который принимает имя файла powershell и имя AppDomain
, Метод успешно выполняется при вызове из консольного приложения C#, но не PowerShell
,
Я знаю, что DLL загружается во второй домен приложения из-за этой записи в журнале Fusion.
Объявление класса и конструктор выглядит следующим образом.
public class AppDomainPoshRunner : MarshalByRefObject{
public AppDomainPoshRunner (){
Console.WriteLine("Made it here.");
}
}
Это сообщение в конструкторе получает вывод, когда я вызываю CreateInstanceFromAndUnwrap, независимо от того, запускаю ли я dll из консольного приложения C# или из приложения PowerShell.
Ошибка возникает, когда я приведу значение, возвращенное CreateInstanceFromAndUnwrap
AppDomainPoshRunner в статическом методе ниже.
public static string[] RunScriptInAppDomain(string fileName, string appDomainName = "Unamed")
{
var assembly = Assembly.GetExecutingAssembly();
var setupInfo = new AppDomainSetup
{
ApplicationName = appDomainName,
// TODO: Perhaps we should setup an even handler to reload the AppDomain similar to ASP.NET in IIS.
ShadowCopyFiles = "true"
};
var appDomain = AppDomain.CreateDomain(string.Format("AppDomainPoshRunner-{0}", appDomainName), null, setupInfo);
try {
var runner = appDomain.CreateInstanceFromAndUnwrap(assembly.Location, typeof(AppDomainPoshRunner).FullName);
if (RemotingServices.IsTransparentProxy(runner))
Console.WriteLine("The unwrapped object is a proxy.");
else
Console.WriteLine("The unwrapped object is not a proxy!");
Console.WriteLine("The unwrapped project is a {0}", runner.GetType().FullName);
/* This is where the error happens */
return ((AppDomainPoshRunner)runner).RunScript(fileName);
}
finally
{
AppDomain.Unload(appDomain);
}
}
При запуске в PowerShell я получаю InvalidCastExcception
с сообщением Невозможно привести прозрачный прокси к типу JustAProgrammer.ADPR.AppDomainPoshRunner
,
Что я делаю неправильно?
2 ответа
У меня была та же проблема: я создал изолированную программную среду с разрешениями "Выполнять только" (минимум) для выполнения ненадежного кода в очень ограниченной среде. Все отлично работало в приложении C#, но не работало (то же исключение приведения), когда отправной точкой был vbs-скрипт, создающий.NET COM-объект. Я думаю, что PowerShell также использует COM. Я нашел обходной путь, используя AppDomain.DoCallBack, который позволяет избежать получения прокси из домена приложения. Это код. Если вы найдете лучший вариант, пожалуйста, напишите. Регистрация в GAC не является хорошим решением для меня...
class Test
{
/*
create appdomain as usually
*/
public static object Execute(AppDomain appDomain, Type type, string method, params object[] parameters)
{
var call = new CallObject(type, method, parameters);
appDomain.DoCallBack(call.Execute);
return call.GetResult();
}
}
[Serializable]
public class CallObject
{
internal CallObject(Type type, string method, object[] parameters)
{
this.type = type;
this.method = method;
this.parameters = parameters;
}
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
public void Execute()
{
object instance = Activator.CreateInstance(this.type);
MethodInfo target = this.type.GetMethod(this.method);
this.result.Data = target.Invoke(instance, this.parameters);
}
internal object GetResult()
{
return result.Data;
}
private readonly string method;
private readonly object[] parameters;
private readonly Type type;
private readonly CallResult result = new CallResult();
private class CallResult : MarshalByRefObject
{
internal object Data { get; set; }
}
}
Это очень похоже на проблему контекста загрузки. Идентификация типа - это не только файл физической сборки; это также о том, как и где это было загружено. Вот старая запись в блоге от Сюзанны Кук, которую вам, вероятно, придется читать пятнадцать раз, пока вы не начнете понимать свою проблему.
Выбор связующего контекста
http://blogs.msdn.com/b/suzcook/archive/2003/05/29/57143.aspx
Прежде чем сказать "но это работает в консольном приложении", помните, что при запуске его из powershell у вас есть совершенно другой инструмент, который касается контекста вызывающего домена приложения, путей поиска, идентификации и т. Д.
Удачи!