我们使用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")
解决了这个问题,但我仍然很好奇。)
由于汉斯已经找到了明确的理由,这确实是对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。
正如汉斯所说,如上例所示,你也可以编译为" Windows可执行文件" (/target:winexe
)并手动p / invoke AllocConsole
。但我个人认为上述方法更合理。
答案 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编译选项设置为其他任何内容。 winexe
或library
完成任务,请注意文件扩展名无关紧要。