.net AppDomain.ExecuteAssembly设置控制台标题

.net AppDomain.ExecuteAssembly设置控制台标题,.net,clr,appdomain,.net,Clr,Appdomain,我们使用AppDomain.ExecuteAssembly()从自身“分叉”一个可执行文件。可用于在启动时动态更新app.config(请参阅旧帖子) 显然,调用AppDomain.ExecuteAssembly()会将当前控制台窗口的标题设置为正在执行的程序集的Assembly.CodeBase值。当函数返回时,即执行恢复到调用AppDomain时,标题将恢复 虽然它没有真正的“危害”,但当您有一个大型批处理文件多次调用此类可执行文件(控制台标题不断变化)时,它有点烦人 下面是一个复制示例:

我们使用AppDomain.ExecuteAssembly()从自身“分叉”一个可执行文件。可用于在启动时动态更新app.config(请参阅旧帖子)

显然,调用
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内部的
\u nexecutesembly()
实现,而CLR不包含在开源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编译器编译的可执行文件时)。因此,这似乎是(Microsoft)CLR相关的行为

有人能证实/解释/复制这一点吗?您能在源代码(SSCLI、CoreCLR等)中找到这种行为的痕迹吗

解决方案更新

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

由于Hans已经找到了明确的原因,这确实是对
SetConsoleTitle
(控制台.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);
    }
}
实际上,您可能希望执行更多的错误检查,并可能防止来自
控制台的异常。Title
导致应用程序退出,但YMMV


正如Hans所说,如上例所示,您还可以编译为“Windows可执行文件”(
/target:winexe
)并手动p/invoke
allocsole
)。但我个人认为上述方法更合理。

这在CLR的CoreCLR源代码中可见:

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


没有要调整的旋钮,您必须将/target compile选项设置为其他任何选项。无论是
winexe
还是
library
都可以完成任务,请注意文件扩展名并不重要。

谢谢。坦率地说,我不太明白为什么我没有在CoreCLR源代码中看到它——首先看了那里,甚至没有看到本机AppDomain源代码。愚蠢的我:-)我不明白其中的“为什么”。已授予
alloconsole
——确保控制台窗口“在那里”,但为什么要设置标题呢。这是没有必要的。无论如何,再次感谢。这个问题大致相当于“为什么不?”:)一个非托管控制台模式的应用程序也可以做到这一点,控制台窗口应该有一个合适的标题。NET程序中的Console.Title。是的,这很有意义。我认为我从自己的角度看问题太多了,我使用了ExecuteAssembly(“self”),因此标题的更改有些“多余”(而且“CodeBase”看起来很难看)。但是是的,整个“为什么/为什么不”的问题当然是没有意义的:——)
if (pAssembly->GetManifestFile()->GetSubsystem() == IMAGE_SUBSYSTEM_WINDOWS_CUI)
{
    {
        GCX_COOP();
        Security::CheckBeforeAllocConsole(pDomain, pAssembly);
    }
    bCreatedConsole = AllocConsole();
    StackSString codebase;
    pAssembly->GetManifestFile()->GetCodeBase(codebase);
    SetConsoleTitle(codebase);
}