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 发现这确实是对 SetConsoleTitle
(Console.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 编译选项设置为其他任何内容。 winexe
或 library
都可以完成工作,请注意文件扩展名无关紧要。
【讨论】:
谢谢汉斯。坦率地说,我不太明白为什么我没有在 CoreCLR 源代码中看到它——先看那里,甚至没有看到原生 AppDomain 源代码。愚蠢的我:-) 我没有得到的是其中的“为什么”。授予AllocConsole
- 确保控制台窗口“存在”,但为什么要设置标题。那是没有必要的。无论如何,再次感谢。
这个问题大致相当于“为什么不呢?” :) 非托管控制台模式应用程序也会这样做,控制台窗口应该有适当的标题。 .NET 程序中的 Console.Title。
是的,这是有道理的。我认为从我自己的角度来看,我对整个问题的看法太多了,我使用 ExecuteAssembly("self"),因此标题更改有点“多余”(除了“CodeBase”看起来很丑)。但是,是的,整个“为什么/为什么不”的问题当然没有实际意义:-)以上是关于AppDomain.ExecuteAssembly 设置控制台标题的主要内容,如果未能解决你的问题,请参考以下文章