在Win32上处理CTRL+C
Handle CTRL+C on Win32
我在Win32 C++控制台程序中处理CTRL+C事件时遇到一些问题。
基本上,我的程序看起来是这样的:(基于另一个问题:Windows Ctrl-C-清理命令行应用程序中的本地堆栈对象)
bool running;
int main() {
running = true;
SetConsoleCtrlHandler((PHANDLER_ROUTINE) consoleHandler, TRUE);
while (running) {
// do work
...
}
// do cleanup
...
return 0;
}
bool consoleHandler(int signal) {
if (signal == CTRL_C_EVENT) {
running = false;
}
return true;
}
问题是根本没有执行清理代码。
在执行处理程序函数后,进程终止,但不执行主循环后的代码。怎么了?
编辑:根据要求,这是一个类似于我的程序的最小测试用例:http://pastebin.com/6rLK6BU2
我的输出中没有"测试清理指令"字符串。
我不知道这是否重要,我正在使用MinGW进行编译。
编辑2:测试用例程序的问题是使用了Sleep()
函数。如果没有它,程序将按预期运行。
在Win32中,函数处理程序在另一个线程中运行,因此当处理程序/线程结束执行时,主线程处于休眠状态。可能这就是流程中断的原因?
以下代码适用于我:
#include <windows.h>
#include <stdio.h>
BOOL WINAPI consoleHandler(DWORD signal) {
if (signal == CTRL_C_EVENT)
printf("Ctrl-C handledn"); // do cleanup
return TRUE;
}
int main()
{
running = TRUE;
if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {
printf("nERROR: Could not set control handler");
return 1;
}
while (1) { /* do work */ }
return 0;
}
根据您的具体要求,您有许多选项。如果您只想忽略Ctrl+C,您可以调用SetConsoleCtrlHandler
,将NULL
作为HandlerRoutine
参数:
int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(NULL, TRUE);
// do work
return 0;
}
这将删除所有信号处理程序。要终止此应用程序,您必须实现自定义逻辑来确定何时关闭。
如果您想处理Ctrl+C,您有两个选项:设置信号处理程序或将键盘输入传递到常规键盘处理。
设置一个处理程序与上面的代码类似,但您提供自己的实现,而不是将NULL
作为处理程序传递。
#include <windows.h>
#include <stdio.h>
volatile bool isRunnung = true;
BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
switch (dwCtrlType)
{
case CTRL_C_EVENT:
printf("[Ctrl]+Cn");
isRunnung = false;
// Signal is handled - don't pass it on to the next handler
return TRUE;
default:
// Pass signal on to the next handler
return FALSE;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
printf("Startingn");
while ( isRunnung ) {
Sleep(0);
}
printf("Endingn");
return 0;
}
此应用程序的输出为:
Starting
[Ctrl]+C
Ending
请注意,无论主while
循环中的代码是什么,都会执行清理代码。信号处理程序形成一个链表,其中处理程序函数在最后注册、第一次调用的基础上被调用,直到其中一个处理程序返回TRUE
。如果没有一个处理程序返回TRUE,则调用默认处理程序。控制台的默认处理程序在处理Ctrl+C时调用ExitProcess
。
如果您想阻止任何预处理并将Ctrl+C处理为常规键盘输入,则必须通过调用SetConsoleMode
来更改控制台模式。
#include <windows.h>
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
DWORD dwMode = 0x0;
GetConsoleMode( GetStdHandle(STD_INPUT_HANDLE), &dwMode );
// Remove ENABLE_PROCESSED_INPUT flag
dwMode &= ~ENABLE_PROCESSED_INPUT;
SetConsoleMode( GetStdHandle(STD_INPUT_HANDLE), dwMode );
while ( true ) {
// Ctrl+C can be read using ReadConsoleInput, etc.
}
return 0;
}
一旦ENABLE_PROCESSED_INPUT
标志被移除,Ctrl+C将不再由系统处理,并像常规键盘输入一样传递到控制台。可以使用ReadConsoleInput
或ReadFile
读取。
免责声明:以上内容已在Windows 8 64位上测试,编译为32和64位,发布和调试配置
根据文档,当处理程序(被声明为错误的BTW)接收到CTRL_CLOSE_EVENT
、CTRL_LOGOFF_EVENT
或CTRL_SHUTDOWN_EVENT
信号时,处理程序退出后进程终止。要执行您正在尝试的操作,您应该将清理代码移动到处理程序本身中。
实际上Win32对POSIX信号进行了一些模拟,所以您只需执行以下操作,即可在Windows和POSIX(Linux、*BSD等)之间移植:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
static void sigHandler(int sig) {
printf("Signal %d received, exitingn", sig);
exit(0);
};
int main() {
signal(SIGINT, sigHandler);
#ifdef SIGBREAK
signal(SIGBREAK, sigHandler); // handles Ctrl-Break on Win32
#endif
signal(SIGABRT, sigHandler);
signal(SIGTERM, sigHandler);
printf("Press Ctrl-C to exit ...n");
for (;;) {
getchar();
}
return 0;
}
尝试直接从windows命令行运行控制台应用程序,而不使用C++IDE。我注意到,如果我从code::BLocks C++IDE运行,您的代码将无法工作。。。但如果我从命令行运行它,它就会工作。。。。
(此外,请注意,对于Codeblock,您需要从项目/bin/debug目录中的Codeblock程序目录("C:\program Files\Codeblocks*.dll")中复制所有dll,以便从命令行运行控制台应用程序…)
- 警告处理为错误这里有什么问题
- 在C#中处理C++指针而不使用unsafe的最佳方法
- 处理多个异常集合的C++方法
- 找不到成员对象:没有名为get_event()的成员,也处理多态性和向量
- 使用流处理接收到的数据
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- 基于多个条件处理地图中的所有元素
- 如何用数字处理log(0)
- SSL上的`curl_easy_send`和`curl_asy_recv`:如何处理`CURLE_AGAIN`
- 错误处理.将系统错误代码映射到泛型
- C++CTRL+C处理程序有未定义的行为
- Qt 键按事件处理程序仅在按下 ctrl、alt 或 shift 键时做出反应
- 我的控制台处理程序不处理 CTRL+C,即使设置正确
- ZeroMQ:如何在Windows上处理Ctrl-C
- "Ctrl + F6"的默认处理在自定义 MDI 应用程序中不起作用
- 在Win32上处理CTRL+C
- Qt eventFilter的CTRL+TAB快捷方式被进一步处理,即使返回true
- 库覆盖信号处理程序,但我需要在CTRL+C上清理
- 如何在命令行界面中处理ctrl中断信号