【发布时间】: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。但我个人认为上述方法更合理。
【问题讨论】: