【问题标题】:AppDomain.ExecuteAssembly sets console titleAppDomain.ExecuteAssembly 设置控制台标题
【发布时间】: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。但我个人认为上述方法更合理。

【问题讨论】:

    标签: .net clr appdomain


    【解决方案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”看起来很丑)。但是,是的,整个“为什么/为什么不”的问题当然没有实际意义:-)
    猜你喜欢
    • 2011-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多