当打开的控制台窗口关闭时,如何防止我的程序关闭?

Posted

技术标签:

【中文标题】当打开的控制台窗口关闭时,如何防止我的程序关闭?【英文标题】:How can I prevent my program from closing when a open console window is closed? 【发布时间】:2013-11-27 02:15:31 【问题描述】:

我正在尝试从我的主程序 (Win32) 打开控制台。我找到了一些代码,它可以工作,但我不明白。我遇到的问题是,当我在控制台上单击 X 时,它也会关闭程序。

我大概有这个:

int APIENTRY WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) 

  // create the main program window, classes registered, etc...
  hwnd = CreateWindowEx(0, csClassName, "theNewTimer", WS_POPUP | WS_CLIPCHILDREN, 300, 0, WINDOW_WIDTH, WINDOW_HEIGHT, HWND_DESKTOP, NULL, hThisInstance, NULL);
  ShowWindow (hwnd, nFunsterStil);

  // and now the console
  AllocConsole();
  HANDLE handle_out = GetStdHandle(STD_OUTPUT_HANDLE);
  int hCrt = _open_osfhandle((long) handle_out, _O_TEXT);
  FILE* hf_out = _fdopen(hCrt, "w");
  setvbuf(hf_out, NULL, _IONBF, 1);
  *stdout = *hf_out;

  HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);
  hCrt = _open_osfhandle((long) handle_in, _O_TEXT);
  FILE* hf_in = _fdopen(hCrt, "r");
  setvbuf(hf_in, NULL, _IONBF, 128);
  *stdin = *hf_in;


// and then the message loop concluding

我用谷歌搜索了一些,但不知道我在读什么。

【问题讨论】:

或许here的答案能帮上忙。 这个类似,但又不一样。我在 MSDN 上阅读并看到我可以将进程 ID 关闭到控制台,而不是父应用程序。问题是我不知道如何获取控制台进程。 BOOL ConsoleCtrlHandler(DWORD fdwCtrlType) //ExitProcess(); // 无法确定进程 ID 开关 (fdwCtrlType) case CTRL_CLOSE_EVENT: return TRUE;情况 CTRL_LOGOFF_EVENT:返回 FALSE;情况 CTRL_SHUTDOWN_EVENT:返回 FALSE; 返回假; ^ 无法确定如何格式化。那有点浓缩,但我不知道从那里去哪里。虽然它已成功连接。 【参考方案1】:

您可以做的一件事是禁用控制台窗口上的关闭按钮:

HWND hwnd = ::GetConsoleWindow();
if (hwnd != NULL)

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

【讨论】:

效果很好,可以防止意外关闭整个程序,并且可以在我只想关闭控制台时使用 FreeConsole()。【参考方案2】:

我认为这是不可能的。从 HandlerRoutine 的 MSDN 文档中,有这句话。

CTRL_CLOSE_EVENTCTRL_LOGOFF_EVENTCTRL_SHUTDOWN_EVENT 信号使进程有机会在终止之前进行清理。

我读到这句话是说 CTRL_CLOSE_EVENT 是建议性的,并且无论如何该进程都会退出。我的猜测是,当系统发送 CTRL_CLOSE_EVENT 时,它会启动一个计时器。允许进程继续运行一段时间,但最终,操作系统会单方面杀死进程。

这是我注册的处理程序

BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType) 
   switch (dwCtrlType) 
       case CTRL_C_EVENT:
       case CTRL_CLOSE_EVENT:
           return TRUE; // breakpoint set here

       default:
           return FALSE;
   

这是我在调用AllocConsole 后注册处理程序的方式:

BOOL result = SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE /* Add */);
assert(result);

我在标记为//breakpoint set here 的行上设置了一个断点。然后,我在 Visual Studio 调试器下运行该过程。当控制台窗口聚焦时,我按下了 Ctrl+C。我的断点被击中,我能够单步执行我的处理程序并返回到KernelBase.dll!CtrlRoutine 等等。当我让进程恢复正常执行时,进程继续运行。

但是,当我关闭控制台窗口时,我的处理程序确实被调用了,但我无法跟踪它的执行很远。我能够单步执行几次,但随后该过程就退出了。 Visual Studio 报告“程序 '[10644] Win32Project.exe' 已退出,代码为 -1073741510 (0xc000013a)。”

0xc000013a 是 STATUS_CONTROL_C_EXIT:

c:\Tools\ERR>Err.exe 0xc000013a
# for hex 0xc000013a / decimal -1073741510 :
  STATUS_CONTROL_C_EXIT                                         ntstatus.h
# Application Exit by CTRL+C
# The application terminated as a result of a CTRL+C.
# 1 matches found for "0xc000013a"

【讨论】:

很好,我的代码现在看起来就像那样。那么当 CTRL_CLOSE_EVENT 被捕获时,我应该怎么做?那部分正在工作。测试只返回 TRUE,仍然关闭程序,与 FALSE 相同。我用谷歌搜索了 ExitProcess() 并找不到要传递给它的参数。我尝试了 255、0 和 -1,结果都一样。在返回 TRUE 之前,我还在控制台处理程序中调用 FreeConsole() 进行了测试;同样的结果。有任何想法吗?谢谢。 查看最新版本的答案。我认为这不能用来阻止进程退出。顺便说一句,没有可以传递给ExitProcess 的特殊值来阻止它退出。如果您不想通过ExitProcess 退出进程,请不要调用ExitProcess 感谢您的帮助,我认为您是对的。就算现在想起来……也是有道理的。 无法阻止操作系统终止您的进程。一旦您返回,它将立即结束您的进程,如果返回 TRUE,或者继续调用处理程序,直到一个返回 TRUE(结束进程)或者如果返回 FALSE,它达到默认值(ExitProcess)。但是,您可以通过设置全局“we_are_closing”变量并在返回前休眠几秒钟来获得一些时间来清理。您正在运行的线程应该检测到变量更改并尽快清理,因为如果处理程序没有返回,那么在操作系统终止进程之前您只有 5-10 秒的时间。 此处警告:assert 语句仅在调试中验证,在发布时编译时它们被完全删除

以上是关于当打开的控制台窗口关闭时,如何防止我的程序关闭?的主要内容,如果未能解决你的问题,请参考以下文章

防止控制台窗口在 Visual Studio C/C++ 控制台应用程序上关闭

如何防止程序完成执行时关闭命令窗口

当用户在弹出窗口之外点击时,防止 JQuery Mobile 关闭弹出窗口

使用 jquery 防止 yii 模态弹出窗口关闭

如何防止我的窗口显示屏幕保护程序或关闭我的应用程序?

如何防止 IntelliJ 自动关闭我的编辑器选项卡?