为什么关闭该开始用AllocConsole引起了我的整个应用程序退出控制台? 我可以改变这种行为

2019-06-17 18:12发布

我希望有发生的是,控制台窗口刚刚消失,或者更好的,这是隐藏的,但我想我的应用程序保持运行。 那可能吗? 我希望能够使用Console.WriteLine,并有控制台作为一个输出窗口。 我希望能够隐藏和显示它,我不希望因为控制台是全封闭的应用只是送死。

编辑

码:

internal class SomeClass {

    [DllImport("kernel32")]
    private static extern bool AllocConsole();

    private static void Main() {
        AllocConsole();
        while(true) continue;
    }
}

编辑2

我试着接受的解决方案在这里[ 捕获控制台退出C# ],每建议在这个问题上的意见。 示例代码被窃听,所述的DllImport需要“KERNEL32.DLL”或“KERNEL32”,而不是“Kernel32中”。 做出这样的转变后,我得到一个消息,我的处理程序CTRL_CLOSE_EVENT当我点击控制台窗口上的X. 但是,调用FreeConsole和/或返回true不会阻止应用程序终止。

Answer 1:

啊,是的,这是一个使用Windows控制台子系统的注意事项之一。 当用户关闭控制台窗口(无论该控制台是如何分配的), 所有这些都连接到控制台的过程被终止 。 这行为使控制台应用程序明显的感觉(即那些专门针对控制台子系统,而不是标准的Windows应用程序),但它可以在你们这样的情况下一个重大的痛苦。

我所知道的唯一的解决方法是使用SetConsoleCtrlHandler功能 ,它允许您注册一个处理函数按Ctrl + CCTRL + BREAK信号,以及如用户关闭控制台窗口系统事件,用户注销,或关闭系统。 该文件说,如果你只是在无视这些事件感兴趣,可以通过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 + CCTRL + BREAK信号(这将造成,否则您的应用程序终止为好),但它不会为一个你问一下,这是工作CTRL_CLOSE_EVENT ,由系统自动生成当用户关闭控制台窗口。

老实说,我不知道如何来防止。 即使在SDK中的样本实际上并没有让你忽略CTRL_CLOSE_EVENT 。 我试图在一个小的测试应用程序,而当你关闭该窗口,并打印消息,但过程还是遭到终止,它会发出蜂鸣声

或许更令人担忧的,文档让我觉得这是不可能的,以防止这种情况:

系统生成CTRL_CLOSE_EVENTCTRL_LOGOFF_EVENTCTRL_SHUTDOWN_EVENT当用户关闭控制台,注销,或者系统关闭以使得过程有机会终止之前清理信号。 控制台的功能,或调用控制台的功能,也可以不属于任何之前提到的三个信号的处理过程中可靠地工作的任何C运行时功能。 其原因是,部分或全部内部控制台清理程序可能已在执行过程中的信号处理程序之前调用。

就这么映入我眼帘的是最后一句。 如果控制台子系统立即开始响应用户试图关闭窗口后自己清理,它可能无法在事后加以制止。

(至少现在你明白这个问题。也许别人可以用一个解决方案一起去!)



Answer 2:

不幸的是没有什么可以做,以真正改变这种行为。

控制台窗口是在他们被其他进程主办,不允许子类“特殊”。 这可以限制修改其行为的能力。

据我所知,您有两个选项:

1.完全禁用的关闭按钮。 你可以用下面的代码片段做到这一点:

HWND hwnd = ::GetConsoleWindow();
if (hwnd != NULL)
{
   HMENU hMenu = ::GetSystemMenu(hwnd, FALSE);
   if (hMenu != NULL) DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
}

使用2.停止控制台完全,并实现自己的文本输出解决方案。

方案2是更复杂的选项,但会为您提供最大的控制。 我发现了一篇文章CodeProject上实现使用丰富的编辑控件中显示的文本控制台一样的应用(丰富的编辑控件必须流。与控制台文本的能力,因此它们非常适合这种类型的应用程序)。



Answer 3:

上关闭使用得到的控制台窗口AllocConsoleAttachConsole ,相关联的过程将退出。 有没有从那个逃跑。

在Windows Vista之前,关闭控制台窗口会出现一个确认对话框向用户询问他该过程是否应终止或没有,但Windows Vista和更高版本不提供任何这样的对话和过程被终止。

一个可能的解决方案来解决此被完全避免AttachConsole和实现通过其他方式所期望的功能。

例如,在由OP描述的情况,需要输出控制台窗口中使用控制台上的一些文本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输出流可以被重定向到写到管道服务器。

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;
    }
}

这仅仅是一个可能的解决方案之一。 在本质上的解决办法是在控制台窗口来充当自己的进程,以便它可以在不影响父进程终止。



Answer 4:

我想表现的IronPython的脚本的输出中,我想从我的程序来控制的控制台。 所以我做了类似的,因为它已经在这里提出了-但如果有多个脚本,我会需要多个控制台窗口,并根据http://msdn.microsoft.com/en-us/library/windows/desktop/ms681944 (v = vs.85)的.aspx窗口的程序不能有一个以上的壳(你感到羞耻,微软!)。

这是大忌。 我不得不解雇的解决方案,现在我重定向IronPython的输出到自书面窗口类,用来模仿外壳。 见http://www.codeproject.com/Articles/335909/Embedding-a-Console-in-aC-Application



Answer 5:

使用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);


            }


文章来源: Why does closing a console that was started with AllocConsole cause my whole application to exit? Can I change this behavior?