You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不破坏沙箱的前提下让AppDomain的StackTrace包含文件名和行号

受限AppDomain沙箱中异常栈追踪无法显示行号的问题

我在受限制的AppDomain沙箱中运行代码,即便PDB文件可用,该AppDomain内抛出的异常栈追踪也不包含行号信息。尝试访问栈追踪的代码已拥有完全信任:程序集已签名,作为强名称程序集加载到该应用域中,因此我原本预期栈追踪会包含文件名和行号。但我不能将该AppDomain标记为完全信任,否则沙箱就失去了意义,请问如何才能让栈追踪包含文件名和行号信息?

更新

我已更新代码,展示了使用Assembly.LoadFile加载外部代码的逻辑。我最初的问题用了单个程序集,在我看来它和我的实际应用表现出的行为一致。但@simon-mourier给出的答案虽然能解决这个简化代码的问题,却不适用于我的实际应用,因此我更新了代码以反映真实场景。

以下示例代码复现了问题,共涉及两个程序集:

  • 当前运行的(父)程序集,已签名且拥有完全信任
  • 子代码程序集,未签名,不可授予信任权限

父程序集代码

// 执行的已签名程序集
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security.Permissions;
using System.Security.Policy;

namespace Parent
{
    public class Worker : MarshalByRefObject
    {
        private static string childpath = Path.Combine(Path.GetDirectoryName(typeof(Worker).Assembly.Location), "Child.dll");

        private static void Main()
        {
            var w = new Worker();
            w.TestExceptionStacktrace();

            var adSandbox = GetInternetSandbox();
            var handle = Activator.CreateInstanceFrom(
                adSandbox,
                typeof(Worker).Assembly.ManifestModule.FullyQualifiedName,
                typeof(Worker).FullName);
            w = (Worker)handle.Unwrap();
            w.TestExceptionStacktrace();
        }

        public void TestExceptionStacktrace()
        {
            TestInner();
        }

        private void TestInner()
        {
            var ass = Assembly.LoadFile(childpath);

            var playMethod = ass.GetTypes()[0].GetMethod("Play");

            try
            {
                playMethod.Invoke(null, Array.Empty<object>());
            }
            catch (Exception e)
            {
                var s = e.ToString();
                Console.WriteLine("栈追踪{0}包含子程集的行号信息:",
                    s.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
                        .Single(x => x.Contains("Play()")).Contains("line")
                        ? ""
                        : "不");
                Console.WriteLine($"   {s}");
            }
        }

        // ------------ 辅助方法 ---------------------------------------
        private static AppDomain GetInternetSandbox()
        {
            // 为所有程序集创建权限集
            var hostEvidence = new Evidence();
            hostEvidence.AddHostEvidence(new Zone(
                System.Security.SecurityZone.Internet));
            var pset =
                System.Security.SecurityManager.GetStandardSandbox(hostEvidence);

            // 给权限集添加额外权限
            pset.AddPermission(new FileIOPermission(
                FileIOPermissionAccess.PathDiscovery, typeof(Worker).Assembly.Location)
            );
            pset.AddPermission(new FileIOPermission(
                FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, Path.GetDirectoryName(childpath))
            );

            // 指定沙箱使用的文件夹
            var ads = new AppDomainSetup();
            ads.ApplicationBase = System.IO.Directory.GetCurrentDirectory();

            var fullTrustAssemblies = new[]
            {
                typeof(Worker).Assembly.Evidence.GetHostEvidence<StrongName>(),
            };

            // 创建沙箱应用域
            return AppDomain.CreateDomain("Sandbox", hostEvidence, ads, pset, fullTrustAssemblies);
        }
    }
}

子程序集代码

// 子程序集(未签名)
using System;

namespace Child
{
    public class Child
    {
        public static void Play()
        {
            var ad = AppDomain.CurrentDomain;
            Console.WriteLine("\r\n应用域 '{0}': IsFullyTrusted = {1}",
                ad.FriendlyName, ad.IsFullyTrusted);

            Console.WriteLine("   当前程序集 {1} 的IsFullyTrusted = {0}",
                typeof(Child).Assembly.IsFullyTrusted,
                typeof(Child).Assembly);

            Console.WriteLine("   mscorlib 的IsFullyTrusted = {0}",
                typeof(int).Assembly.IsFullyTrusted);

            throw new Exception("示例异常");
        }
    }
}

运行输出

代码运行输出如下:

应用域 'Parent.exe': IsFullyTrusted = True
   当前程序集 Child, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 的IsFullyTrusted = True
   mscorlib 的IsFullyTrusted = True
栈追踪包含子程序集的行号信息:
   System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.Exception: 示例异常
   在 Child.Child.Play() 位置 C:\Users\Bouke\Developer\SandboxStacktrace\Child\Child.cs:行号 20
   (...)

应用域 'Sandbox': IsFullyTrusted = False
   当前程序集 Child, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null 的IsFullyTrusted = False
   mscorlib 的IsFullyTrusted = True
栈追踪不包含子程序集的行号信息:
   System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.Exception: 示例异常
   在 Child.Child.Play()
   (...)

补充说明

目前我测试发现使用Assembly.Load(byte[], byte[], SecurityContextSource.CurrentAssembly)可以实现显示行号的需求,但会破坏沙箱隔离(加载的程序集会获得完全信任);而使用Assembly.Load(byte[], byte[], SecurityContextSource.CurrentAppDomain)则无法生效。


内容的提问来源于stack exchange,提问作者Bouke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.28 20:45:04