AppDomain.ExecuteAssembly设置控制台标题

时间:2015-07-24 17:32:48

标签: .net clr appdomain

我们使用AppDomain.ExecuteAssembly()来" fork"来自自己的可执行文件这可用于在启动时动态更新app.config(请参阅this旧帖子)。

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

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

这是一个重复的例子:

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也可以看到这种行为)。

更新

  • Windows应用程序(编译器开关/target:winexe表现出此行为。要测试使用此示例:

    // Compile with "csc.exe /target:winexe foo.cs" 
    using System;
    using System.Runtime.InteropServices;
    
    public class Foo 
    {
        [DllImport("Kernel32.dll")]
        public static extern void AllocConsole();
    
        public static int Main(string[] args) 
        {
            AllocConsole();
    
            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);
        }
    }
    
  • 使用Mono而不是Microsoft CLR,也会出现此行为(即使运行使用Microsoft编译器编译的可执行文件)。所以这似乎是(微软)与CLR相关的行为。

有人可以确认/解释/复制吗?你能在源代码(SSCLI,CoreCLR等)中找到这种行为的痕迹吗?

解决方法更新

(注意:我分别使用AppDomain.SetData("key", Console.Title)Console.Title = AppDomain.GetData("key")解决了这个问题,但我仍然很好奇。)

由于汉斯已经找到了明确的理由,这确实是对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。

正如汉斯所说,如上例所示,你也可以编译为" Windows可执行文件" (/target:winexe)并手动p / invoke AllocConsole。但我个人认为上述方法更合理。

1 个答案:

答案 0 :(得分:3)

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

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

无需调整旋钮,您必须将/ target编译选项设置为其他任何内容。 winexelibrary完成任务,请注意文件扩展名无关紧要。