AppDomain.ExecuteAssembly 设置控制台标题
AppDomain.ExecuteAssembly sets console title
我们使用 AppDomain.ExecuteAssembly()
到 "fork" 一个可执行文件本身。可用于在启动时动态更新 app.config(请参阅 this 旧 post)。
显然,调用 AppDomain.ExecuteAssembly()
会将当前控制台的标题 window 设置为正在执行的程序集的 Assembly.CodeBase
值。当函数 returns,即执行恢复到调用 AppDomain 时,标题被恢复。
虽然它没有真正的 "harm",但当您有一个大型批处理文件多次调用此类可执行文件时(控制台标题不断变化),这有点烦人。
这是一个重现示例:
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 编译器编译的可执行文件)。所以这似乎是 (Microsoft) CLR 相关的行为。
任何人都可以 confirm/explain/reproduce 这个吗?您能否在源代码(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 Executable" (/target:winexe
) 并手动 p/invoke AllocConsole
。不过我个人觉得上面的做法比较合理
这在 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);
}
这实现了在控制台 window 中显示使用 /target:exe 编译的程序集的承诺,必要时创建它。可能是您看不到的东西,因为您已经 运行 来自控制台模式应用程序。 SetConsoleTitle()
调用设置了 window 标题。
没有旋钮可以调整,您必须将 /target 编译选项设置为其他任何值。 winexe
或 library
都可以完成工作,请注意文件扩展名无关紧要。
我们使用 AppDomain.ExecuteAssembly()
到 "fork" 一个可执行文件本身。可用于在启动时动态更新 app.config(请参阅 this 旧 post)。
显然,调用 AppDomain.ExecuteAssembly()
会将当前控制台的标题 window 设置为正在执行的程序集的 Assembly.CodeBase
值。当函数 returns,即执行恢复到调用 AppDomain 时,标题被恢复。
虽然它没有真正的 "harm",但当您有一个大型批处理文件多次调用此类可执行文件时(控制台标题不断变化),这有点烦人。
这是一个重现示例:
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 编译器编译的可执行文件)。所以这似乎是 (Microsoft) CLR 相关的行为。
任何人都可以 confirm/explain/reproduce 这个吗?您能否在源代码(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 Executable" (/target:winexe
) 并手动 p/invoke AllocConsole
。不过我个人觉得上面的做法比较合理
这在 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);
}
这实现了在控制台 window 中显示使用 /target:exe 编译的程序集的承诺,必要时创建它。可能是您看不到的东西,因为您已经 运行 来自控制台模式应用程序。 SetConsoleTitle()
调用设置了 window 标题。
没有旋钮可以调整,您必须将 /target 编译选项设置为其他任何值。 winexe
或 library
都可以完成工作,请注意文件扩展名无关紧要。