为什么closures使用AllocConsole启动的控制台会导致我的整个应用程序退出? 我可以改变这种行为吗?
我想要发生的是控制台窗口刚刚消失,或者更好的是它隐藏了,但我希望我的应用程序继续运行。 那可能吗? 我希望能够使用Console.WriteLine并将控制台用作输出窗口。 我希望能够隐藏和显示它,而且我不希望整个应用程序因为控制台closures而死亡。
编辑
码:
internal class SomeClass { [DllImport("kernel32")] private static extern bool AllocConsole(); private static void Main() { AllocConsole(); while(true) continue; } }
编辑2
我试着在这里接受的解决scheme[ 捕获控制台退出C# ],根据这个问题的意见中的build议。 该示例代码被错误的DLLImport需要是“kernel32.dll”或“kernel32”,而不是“Kernel32”。 进行更改之后,当我单击控制台窗口上的X时,我正在向CTRL_CLOSE_EVENT的处理程序发送消息。 但是,调用FreeConsole和/或返回true不会阻止应用程序终止。
啊,是的,这是使用Windows控制台子系统的一个警告。 当用户closures控制台窗口时(无论控制台是如何分配的), 连接到控制台的所有进程都将终止 。 对于控制台应用程序(即专门针对控制台子系统的应用程序,而不是标准的Windows应用程序),这种行为显然是有意义的,但在像您这样的情况下,这可能是一个主要的难题。
我所知道的唯一的解决方法是使用SetConsoleCtrlHandler
函数 ,它允许您注册Ctrl + C和Ctrl + Break信号的处理函数,以及像closures控制台窗口,用户注销等系统事件,或系统closures。 该文件说,如果你只关心忽略这些事件,你可以传递null
作为第一个参数。 例如:
[DllImport("kernel32")] static extern bool SetConsoleCtrlHandler(HandlerRoutine HandlerRoutine, bool Add); delegate bool HandlerRoutine(uint dwControlType); static void Main() { AllocConsole(); SetConsoleCtrlHandler(null, true); while (true) continue; }
这对于Ctrl + C和Ctrl + Break信号是完美的(否则会导致你的应用程序终止),但是对于你询问的CTRL_CLOSE_EVENT
,系统生成的CTRL_CLOSE_EVENT
当用户closures控制台窗口时。
老实说,我不知道如何预防。 即使SDK中的示例实际上也不允许您忽略CTRL_CLOSE_EVENT
。 我在一个小testing应用程序中试过,当你closures窗口并打印消息时,它会发出嘟嘟声 ,但是这个过程仍然会被终止。
也许更令人担忧的是,文档让我觉得不可能阻止这个:
当用户closures控制台,注销或closures系统时,系统会生成
CTRL_CLOSE_EVENT
,CTRL_LOGOFF_EVENT
和CTRL_SHUTDOWN_EVENT
信号,以便进程在终止前有机会进行清理。 控制台function或任何调用控制台function的C运行时function,在处理前面提到的三个信号中的任何一个信号期间可能无法可靠地工作。 原因是在执行进程信号处理程序之前可能已经调用了部分或全部内部控制台清理例程。
最后一句话引起了我的注意。 如果控制台子系统在响应用户试图closures窗口时立即开始清理,则在事实之后可能无法停止。
(至less现在你明白了这个问题,也许别人可以拿出一个解决scheme!)
不幸的是,没有什么可以做的,以真正改变这种行为。
控制台窗口是“特殊的”,因为它们由另一个进程托pipe,不允许子分类。 这限制了你修改行为的能力。
据我所知,你的两个select是:
1.完全禁用closuresbutton。 你可以用下面的代码片段来做到这一点:
HWND hwnd = :: GetConsoleWindow(); 如果(hwnd!= NULL) { HMENU hMenu = :: GetSystemMenu(hwnd,FALSE); if(hMenu!= NULL)DeleteMenu(hMenu,SC_CLOSE,MF_BYCOMMAND); }
2.停止使用控制台,并实现自己的文本输出解决scheme。
选项#2是更复杂的选项,但会为您提供最好的控制。 我发现了CodeProject上的一篇文章,它使用丰富的编辑控件来实现类似控制台的应用程序来显示文本(丰富的编辑控件可以像控制台一样传输文本,所以它们非常适合这种应用程序)。
closures使用AllocConsole
或AttachConsole
获取的控制台窗口时,关联的进程将退出。 这是没有逃脱的。
在Windows Vista之前,closures控制台窗口会向用户显示一个确认对话,询问用户是否应该终止该进程,但是Windows Vista和更高版本不提供任何这样的对话,并且进程终止。
解决此问题的一个可能的解决scheme是完全避免AttachConsole并通过其他方式实现所需的function。
例如,在OP描述的情况下,需要使用Console
窗口来使用Console
静态类在控制台上输出一些文本。
这可以通过进程间通信很容易地实现。 例如,一个控制台应用程序可以被开发为充当回声服务器
namespace EchoServer { public class PipeServer { public static void Main() { var pipeServer = new NamedPipeServerStream(@"Com.MyDomain.EchoServer.PipeServer", PipeDirection.In); pipeServer.WaitForConnection(); StreamReader reader = new StreamReader(pipeServer); try { int i = 0; while (i >= 0) { i = reader.Read(); if (i >= 0) { Console.Write(Convert.ToChar(i)); } } } catch (IOException) { //error handling code here } finally { pipeServer.Close(); } } } }
然后代替将控制台分配/附加到当前应用程序,可以从应用程序内启动回显服务器,并且可以将Console's
输出streamredirect到pipe道服务器。
class Program { private static NamedPipeClientStream _pipeClient; static void Main(string[] args) { //Current application is a Win32 application without any console window var processStartInfo = new ProcessStartInfo("echoserver.exe"); Process serverProcess = new Process {StartInfo = processStartInfo}; serverProcess.Start(); _pipeClient = new NamedPipeClientStream(".", @"Com.MyDomain.EchoServer.PipeServer", PipeDirection.Out, PipeOptions.None); _pipeClient.Connect(); StreamWriter writer = new StreamWriter(_pipeClient) {AutoFlush = true}; Console.SetOut(writer); Console.WriteLine("Testing"); //Do rest of the work. //Also detect that the server has terminated (serverProcess.HasExited) and then close the _pipeClient //Also remember to terminate the server process when current process exits, serverProcess.Kill(); while (true) continue; } }
这只是可能的解决scheme之一。 本质上,解决方法是将控制台窗口分配给自己的进程,以便在不影响父进程的情况下终止。
我想在我想从程序控制的控制台上显示IronPython脚本的输出。 所以我做了类似的,因为它已经在这里提出 – 但如果有多个脚本,我将需要多个控制台窗口,并根据http://msdn.microsoft.com/en-us/library/windows/desktop/ms681944 (v = vs.85).aspx windows程序不能有多个shell(对你微软!)。
这是交易断路器。 我不得不放弃解决scheme,现在我将IronPython的输出redirect到一个自写的窗口类,它模拟一个shell。 见http://www.codeproject.com/Articles/335909/Embedding-a-Console-in-aC-Application
使用FreeConsole而不是AllocConsole
定义..
[DllImport("kernel32.dll", SetLastError = true,ExactSpelling = true)] static extern bool FreeConsole();
用法..
public void ExecuteCommandSync(object command, String PATH,bool redirect) { try { //AllocConsole(); FreeConsole(); ProcessStartInfo pw = new ProcessStartInfo(); pw.FileName = @"cmd.exe"; pw.UseShellExecute = false; pw.RedirectStandardInput = redirect; pr.StartInfo = pw; pr.Start(); pr.StandardInput.WriteLine(@"cd "+ PATH); pr.StandardInput.WriteLine(@""+command); }