从 PowerShell 调用时无法在 dll 中转换透明代理,但在 C# 控制台应用程序中成功
Posted
技术标签:
【中文标题】从 PowerShell 调用时无法在 dll 中转换透明代理,但在 C# 控制台应用程序中成功【英文标题】:Unable to cast transparent proxy in a dll when called from PowerShell, but successful in C# console app 【发布时间】:2011-09-24 02:34:00 【问题描述】:我正在尝试创建一个open source library,它会生成一个新的AppDomain
并在其中运行一个PowerShell
脚本。我有一个静态方法,它采用 powershell 文件的名称和AppDomain
的名称。从 C# 控制台应用程序调用该方法时成功执行,但不是 PowerShell
。
由于this entry in the fusionlog,我知道 dll 正在第二个应用程序域中加载。
类声明和构造函数如下所示。
public class AppDomainPoshRunner : MarshalByRefObject
public AppDomainPoshRunner ()
Console.WriteLine("Made it here.");
无论我是从 C# 控制台应用程序还是从 PowerShell 应用程序运行 dll,当我调用 CreateInstanceFromAndUnwrap 时,构造函数中的该消息都会得到输出。
当我在下面的静态方法中将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
。
我做错了什么?
【问题讨论】:
runner
的类型需要继承自AppDomainPoshRunner
。
那么,您是说当您从 C# 控制台应用程序调用 RunScriptInAppDomain()
时,它运行良好,但是当您从 PowerShell 调用它时,它会抛出该异常?
@Scott,runner
的类型是AppDomainPoshRunner
。
@svick 正如斯科特所说,跑步者的类型是 AppDomainPoshRunner。
@Scott,正确, RunScriptInAppDomain() 在 C# 控制台应用程序中运行良好,但在 powershell 应用程序中运行良好。两者的源代码都在 github 中。
【参考方案1】:
我遇到了同样的问题:我创建了只执行权限(可以拥有的最低权限)的沙箱,以便在非常受限的环境中执行不受信任的代码。在 C# 应用程序中一切正常,但在起点是创建 .NET COM 对象的 vbs 脚本时不起作用(相同的强制转换异常)。我认为 PowerShell 也使用 COM。我找到了使用 AppDomain.DoCallBack 的解决方法,它可以避免从 appdomain 获取代理。这是代码。 如果您找到更好的选择,请发布。在 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;
【讨论】:
【参考方案2】:这听起来很像加载上下文问题。类型标识不仅仅是物理程序集文件;它还与加载方式和位置有关。这是 Suzanne Cook 的一篇旧博文,您可能需要阅读 15 遍才能开始理解您的问题。
选择绑定上下文
http://blogs.msdn.com/b/suzcook/archive/2003/05/29/57143.aspx
在你说“但它可以在控制台应用程序中工作”之前,请记住,从 powershell 运行它时,在调用应用程序域的上下文、探测路径、身份等方面,你有一个完全不同的问题。
祝你好运!
【讨论】:
感谢那篇文章。即使它不能直接解决这个问题,但如果你对 AppDomains 搞砸了,它肯定很有用。 放入 GAC 是一种快速解决方法,因为 GAC 的程序集总是在 Load 上下文中结束。一旦你解开一个远程实例,AppDomainPoshRunner 就会被加载到调用 appdomain 的不同上下文中,而不是被调用者中的上下文。这给了他们不同的身份。如果您想避免将整个实现放在 GAC 中,我会在共享程序集中创建一个 IScriptRunner 接口,然后在 GAC 中创建一个。将具体 impl 保留在本地非 gac 程序集中,但它也需要强命名,否则将无法加载 GAC 共享程序集。以上是关于从 PowerShell 调用时无法在 dll 中转换透明代理,但在 C# 控制台应用程序中成功的主要内容,如果未能解决你的问题,请参考以下文章