AppDomain.ExecuteAssembly 设置控制台标题

Posted

技术标签:

【中文标题】AppDomain.ExecuteAssembly 设置控制台标题【英文标题】:AppDomain.ExecuteAssembly sets console title 【发布时间】:2015-07-24 17:32:48 【问题描述】:

我们使用AppDomain.ExecuteAssembly() 从自身“派生”一个可执行文件。这可用于在启动时动态更新 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") 的解决方法,但我仍然很好奇。)

由于 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 可执行文件”(/target:winexe) 并手动 p/invoke AllocConsole。但我个人认为上述方法更合理。

【问题讨论】:

【参考方案1】:

这在 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 都可以完成工作,请注意文件扩展名无关紧要。

【讨论】:

谢谢汉斯。坦率地说,我不太明白为什么我没有在 CoreCLR 源代码中看到它——先看那里,甚至没有看到原生 AppDomain 源代码。愚蠢的我:-) 我没有得到的是其中的“为什么”。授予AllocConsole - 确保控制台窗口“存在”,但为什么要设置标题。那是没有必要的。无论如何,再次感谢。 这个问题大致相当于“为什么不呢?” :) 非托管控制台模式应用程序也会这样做,控制台窗口应该有适当的标题。 .NET 程序中的 Console.Title。 是的,这是有道理的。我认为从我自己的角度来看,我对整个问题的看法太多了,我使用 ExecuteAssembly("self"),因此标题更改有点“多余”(除了“CodeBase”看起来很丑)。但是,是的,整个“为什么/为什么不”的问题当然没有实际意义:-)

以上是关于AppDomain.ExecuteAssembly 设置控制台标题的主要内容,如果未能解决你的问题,请参考以下文章