AppDomain.ExecuteAssembly 设置控制台标题

AppDomain.ExecuteAssembly sets console title

我们使用 AppDomain.ExecuteAssembly() 到 "fork" 一个可执行文件本身。可用于在启动时动态更新 app.config(请参阅 this 旧 post)。

显然,调用 AppDomain.ExecuteAssembly() 会将当前控制台的标题 window 设置为正在执行的程序集的 Assembly.CodeBase 值。当函数 returns,即执行恢复到调用 AppDomain 时,标题被恢复。

虽然它没有真正的 "harm",但当您有一个大型批处理文件多次调用此类可执行文件时(控制台标题不断变化),这有点烦人。

这是一个重现示例:

using System;

public class Foo 
{
    public static int Main(string[] args) 
    {
        if (AppDomain.CurrentDomain.FriendlyName.Equals("Test")) 
        {
            Console.WriteLine("Inside");
            // While "in here", the console title is something
            // like: "file:///C:/Sources/Foo.exe".
            Console.ReadKey();
            return 0;
        }

        var domain = AppDomain.CreateDomain("Test", null,
            AppDomain.CurrentDomain.SetupInformation);
        return domain.ExecuteAssembly(typeof(Foo).Assembly.Location, (string[])null);
    }
}

我找不到任何关于此的文档或可能导致此问题的代码。实际功能可能会在 CLR 中 _nExecuteAssembly() ,它不包含在开源 CoreCLR 中,因为它不支持 AppDomains。此外,SSCLI 源代码 似乎 没有相关代码(尽管我不确定,也无法检查 CLR 2.x 是否也可以看到该行为) .

更新:

任何人都可以 confirm/explain/reproduce 这个吗?您能否在源代码(SSCLI、CoreCLR 等)中找到此行为的踪迹?

解决方法更新

(注意:我有解决方法,分别使用 AppDomain.SetData("key", Console.Title)Console.Title = AppDomain.GetData("key"),但我仍然很好奇。)

由于 Hans 已经找到明确的原因,这确实是对 SetConsoleTitleConsole.Title 的原生基础)的显式调用,我想明确说明我的解决方法:

using System;

public class Foo 
{
    public static int Main(string[] args) 
    {
        if (AppDomain.CurrentDomain.FriendlyName.Equals("Test")) 
        {
            Console.Title = (string)AppDomain.CurrentDomain.GetData("foo:original-title");
            Console.WriteLine("Inside");
            Console.ReadKey();
            return 0;
        }

        var domain = AppDomain.CreateDomain("Test", null, AppDomain.CurrentDomain.SetupInformation);
        domain.SetData("foo:original-title", Console.Title);
        return domain.ExecuteAssembly(typeof(Foo).Assembly.Location, (string[])null);
    }
}

在实践中,您可能想要做更多的错误检查,并尽可能防止来自 Console.Title 的异常导致应用程序退出,但是 YMMV。

正如 Hans 所说,如上例所示,您也可以编译为 "Windows Executable" (/target:winexe) 并手动 p/invoke AllocConsole。不过我个人觉得上面的做法比较合理

这在 CLR 的 CoreCLR 源代码中可见,src/vm/appdomainnative.cpp, AppDomainNative::ExecuteAssembly function:

if (pAssembly->GetManifestFile()->GetSubsystem() == IMAGE_SUBSYSTEM_WINDOWS_CUI)
{
    {
        GCX_COOP();
        Security::CheckBeforeAllocConsole(pDomain, pAssembly);
    }
    bCreatedConsole = AllocConsole();
    StackSString codebase;
    pAssembly->GetManifestFile()->GetCodeBase(codebase);
    SetConsoleTitle(codebase);
}

这实现了在控制台 window 中显示使用 /target:exe 编译的程序集的承诺,必要时创建它。可能是您看不到的东西,因为您已经 运行 来自控制台模式应用程序。 SetConsoleTitle() 调用设置了 window 标题。

没有旋钮可以调整,您必须将 /target 编译选项设置为其他任何值。 winexelibrary 都可以完成工作,请注意文件扩展名无关紧要。