从 PowerShell 调用时无法在 dll 中转换透明代理,但在 C# 控制台应用程序中成功

发布于 2024-12-06 19:00:26 字数 2373 浏览 1 评论 0原文

我正在尝试创建一个开源库来生成一个新的AppDomain并且在其中运行 PowerShell 脚本。我有一个静态方法,它采用 powershell 文件的名称和 AppDomain 的名称。从 C# 控制台应用调用该方法会成功执行,但从 PowerShell 调用时不会成功。

我知道 dll 正在第二个应用程序域中加载,因为 融合日志中的此条目

类声明和构造函数如下所示。

public class AppDomainPoshRunner : MarshalByRefObject{

    public AppDomainPoshRunner (){
        Console.WriteLine("Made it here.");
    }
}

当我调用 CreateInstanceFromAndUnwrap 时,构造函数中的该消息将得到输出 无论我是从 C# 控制台应用程序还是从 PowerShell 应用程序运行 dll。

当我在下面的静态方法中将 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 中运行该程序时,我收到 InvalidCastException ,并显示消息 无法将透明代理转换为类型 JustAProgrammer.ADPR.AppDomainPoshRunner

我在做什么错误的?

I'm attempting to create an open source library that spawn a new AppDomain and runs a PowerShell script in it. I have a static method that takes the name of the powershell file and the name of the AppDomain. The method executes successfully when called from a C# console app, but not PowerShell.

I know the dll is being loaded in the second app domain because of this entry in the fusionlog.

The class declaraton and constructor looks like this.

public class AppDomainPoshRunner : MarshalByRefObject{

    public AppDomainPoshRunner (){
        Console.WriteLine("Made it here.");
    }
}

That message in the constructor gets output when I call CreateInstanceFromAndUnwrap whether I run the dll from a C# console app or from the PowerShell app.

The failure occurs when I cast the value returned by CreateInstanceFromAndUnwrap to AppDomainPoshRunner in the static method below.

    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);
        }
    }

When running that in PowerShell I get an InvalidCastExcception with the message Unable to cast transparent proxy to type JustAProgrammer.ADPR.AppDomainPoshRunner.

What am I doing wrong?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

戏蝶舞 2024-12-13 19:00:26

我遇到了同样的问题:我创建了具有“仅执行”权限(最小权限)的沙箱,以便在非常受限的环境中执行不受信任的代码。所有这些在 C# 应用程序中都工作得很好,但当起点是创建 .NET COM 对象的 vbs 脚本时则不起作用(相同的强制转换异常)。我认为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; }
        }
    }

I had the same problem: I created sandbox with Execute only permissions (the min one can have) to execute untrusted code in very restricted environment. All worked great in C# application, but did not work (the same cast exception) when starting point was vbs script creating .NET COM object. I think PowerShell also uses COM. I found workaround using AppDomain.DoCallBack, which avoids getting proxy from the appdomain. This is the code.
If you find a better option, please post. Registering in GAC is not a good solution for me...

    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; }
        }
    }
冷夜 2024-12-13 19:00:26

这听起来很像加载上下文问题。类型标识不仅仅与物理程序集文件有关;它还与物理程序集文件有关。它还与它的加载方式和位置有关。这是 Suzanne Cook 的一篇旧博客文章,您可能需要阅读十五遍才能开始理解您的问题。

选择绑定上下文

https://learn.microsoft.com/en-us/archive/blogs/suzcook/choosing-a-binding-context

在你说“但它可以在控制台中工作”之前app,”记住,当从 powershell 运行它时,关于调用 appdomain 的上下文、探测路径、身份等,你会遇到完全不同的情况。

祝你好运!

It sounds very much like a loading context issue. Type identity isn't just about the physical assembly file; it's also about how and where it was loaded. Here's an old blog post from Suzanne Cook that you'll probably have to read fifteen times until you can begin to make sense of your problem.

Choosing a Binding Context

https://learn.microsoft.com/en-us/archive/blogs/suzcook/choosing-a-binding-context

Before you say "but it works in a console app," remember that when running it from powershell, you have an entirely different kettle of fish as regards the calling appdomain's context, probing paths, identity etc.

Good luck!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文