在Win32上处理CTRL + C.

时间:2013-08-17 17:02:28

标签: c++ winapi console-application

Win32 C ++ 控制台程序中处理 CTRL + C 事件时遇到一些问题。

基本上我的程序看起来像这样:(基于另一个问题:Windows Ctrl-C - Cleaning up local stack objects in command line app

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

我的输出中没有得到“test cleanup-instruction”字符串。

我不知道这是否重要,我正在使用 MinGW 进行编译。


编辑2:测试用例程序的问题是使用Sleep()函数。没有它,程序按预期工作。

在Win32中,函数处理程序在另一个线程中运行,因此当处理程序/线程结束其执行时,主线程正在休眠。可能这是过程中断的原因?

4 个答案:

答案 0 :(得分:10)

以下代码适用于我:

#include <windows.h> 
#include <stdio.h> 

BOOL WINAPI consoleHandler(DWORD signal) {

    if (signal == CTRL_C_EVENT)
        printf("Ctrl-C handled\n"); // 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;
}

答案 1 :(得分:10)

根据the documentation,当处理程序(声明为错误,BTW)收到CTRL_CLOSE_EVENTCTRL_LOGOFF_EVENTCTRL_SHUTDOWN_EVENT信号时,该过程在处理程序退出。要执行您正在尝试的操作,您应该将清理代码移动到处理程序本身。

答案 2 :(得分:9)

根据您的具体要求,您有多种选择。如果您只是想忽略 Ctrl + C ,可以将SetConsoleCtrlHandler作为HandlerRoutine参数调用ExitProcess

NULL

这将删除所有信号处理程序。要终止此应用程序,您必须实现自定义逻辑以确定何时关闭。

如果你想处理 Ctrl + C ,你有两个选择:设置信号处理程序或将键盘输入传递给常规键盘处理。

设置处理程序与上面的代码类似,但不是将int _tmain(int argc, _TCHAR* argv[]) { SetConsoleCtrlHandler(NULL, TRUE); // do work return 0; } 作为处理程序传递,而是提供自己的实现。

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]+C\n");
        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("Starting\n");
    while ( isRunnung ) {
        Sleep(0);
    }
    printf("Ending\n");

   return 0;
}

请注意,无论主Starting [Ctrl]+C Ending 循环内的代码如何,都会执行清理代码。信号处理程序形成一个链表,其中处理程序函数在最后注册的第一次调用的基础上调用,直到其中一个处理程序返回while。如果没有处理程序返回TRUE,则调用默认处理程序。处理 Ctrl + C 时,控制台的默认处理程序调用{​​{3}}。

如果要阻止任何预处理并将 Ctrl + C 作为常规键盘输入,则必须通过调用SetConsoleMode来更改控制台模式。< / p>

TRUE

删除#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; } 标志后 Ctrl + C 不再由系统处理并像常规键盘输入一样传递到控制台。可以使用ReadConsoleInputReadFile来阅读。

免责声明:以上内容已经在Windows 8 64位上进行了测试,针对32位和64位,发布和调试配置进行了编译。

答案 3 :(得分:0)

实际上,Win32 对 POSIX 信号有一些模拟,因此您只需执行以下操作,即可在 Windows 和 POSIX(Linux、*BSD 等)之间移植:

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>

static void sigHandler(int sig) {
    printf("Signal %d received, exiting\n", 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;
}