2013-08-17 80 views
9

我有一些問題的CTRL +ç事件處理,在Win32的C++控制檯程序。處理CTRL + C在Win32

基本上我的計劃是這樣的:(在此基礎上其他問題: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

我沒有得到我的輸出中的「測試清理指令」的字符串。

我不知道這是否重要,我正在編譯MinGW


編輯2:測試用例程序的問題是使用Sleep()函數。沒有它,程序按預期工作。

在Win32中,函數處理程序在另一個線程中運行,所以當處理程序/線程結束其執行時,主線程正在休眠。可能這是造成流程中斷的原因?

+3

什麼'SetConsoleCtrlHandler'返回? – Caesar

+0

SetConsoleCtrlHandler的返回值是什麼?你沒有檢查錯誤,btw – lpapp

+1

你確定'ConsoleHandler'函數可以運行嗎?如果你在調試器中運行程序並在其中設置斷點,它會在斷點處停止嗎? –

回答

7

下面的代碼爲我工作:

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

根據the documentation,當處理程序(其被聲明錯誤,順便說一句)接收CTRL_CLOSE_EVENTCTRL_LOGOFF_EVENT,或CTRL_SHUTDOWN_EVENT信號,處理該處理程序後終止退出。要做你正在嘗試的,你應該把你的清理代碼移入處理程序本身。

+0

它看起來像我的程序沒有收到這些事件,但只有一個'CTRL_C_EVENT'。剛剛在我的處理程序函數中用'switch'進行了測試,並從控制檯按下CTRL + C。 – eang

8

根據您的具體要求,您有多種選擇。如果你只是想忽略按Ctrl +ç可以撥打SetConsoleCtrlHandler傳遞NULLHandlerRoutine參數:

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    SetConsoleCtrlHandler(NULL, TRUE); 

    // do work 

    return 0; 
} 

這將刪除所有的信號處理程序。要終止此應用程序,您必須實施自定義邏輯以確定何時關閉。

如果你想處理按Ctrl +ç有兩種選擇:設置的信號處理程序或轉嫁給普通鍵盤操作的鍵盤輸入。

設置處理程序與上面的代碼類似,但不是將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 -loop內部。信號處理程序形成一個鏈接列表,其中處理函數以最後一次註冊,首先調用的方式調用,直到其中一個處理程序返回TRUE。如果沒有任何處理程序返回TRUE,則調用默認處理程序。控制檯的默認處理程序在處理時調用ExitProcessCtrl + C

如果你想防止任何預處理和處理按Ctrl +ç作爲普通鍵盤輸入,而不是你必須通過調用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鍵 + Ç由系統不再處理並傳遞到像普通鍵盤輸入控制檯。可以使用ReadConsoleInputReadFile來閱讀。

免責聲明:上述內容已在Windows 8 64位上進行了測試,編譯爲32位和64位Release和Debug配置。