2013-08-17 76 views
9

Tôi có một số vấn đề với việc xử lý CTRL +C sự kiện, trong một Win32 C++ console chương trình.Xử lý tổ hợp phím CTRL + C trên Win32

Về cơ bản chương trình của tôi trông như thế này: (dựa trên câu hỏi này khá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; 
} 

Vấn đề là các mã ngẫu nhiên không được thực hiện ở tất cả.

Sau khi thực hiện hàm xử lý, quá trình được chấm dứt, nhưng không thực thi mã sau vòng lặp chính. Chuyện gì vậy?

EDIT: theo yêu cầu, đây là một trường hợp thử nghiệm tối thiểu tương tự như chương trình của tôi: http://pastebin.com/6rLK6BU2

tôi không nhận được "thử nghiệm dọn dẹp-hướng dẫn" chuỗi trong đầu ra của tôi.

Tôi không biết điều này có quan trọng hay không, tôi đang biên soạn với MinGW.


EDIT 2: Sự cố với chương trình trường hợp thử nghiệm là sử dụng chức năng Sleep(). Nếu không có nó, chương trình sẽ hoạt động như mong đợi.

Trong Win32, trình xử lý hàm chạy trong một chuỗi khác, vì vậy khi trình xử lý/luồng kết thúc thực thi, luồng chính đang ngủ. Có lẽ đây là nguyên nhân của quá trình gián đoạn?

+3

Trả về 'SetConsoleCtrlHandler' là gì? – Caesar

+0

Giá trị trả về của SetConsoleCtrlHandler là gì? Bạn không kiểm tra lỗi đó, btw – lpapp

+1

Bạn có chắc chắn chức năng 'ConsoleHandler' chạy không? Nếu bạn chạy chương trình trong trình gỡ rối và thiết lập một điểm ngắt trong nó, nó sẽ dừng lại ở điểm ngắt? –

Trả lời

7

Các mã sau đây làm việc cho tôi:

#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

Theo the documentation, khi xử lý (được tuyên bố sai, BTW) nhận được một CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, hoặc CTRL_SHUTDOWN_EVENT tín hiệu, quá trình này bị chấm dứt sau khi xử lý thoát. Để làm những gì bạn đang cố gắng, bạn có nghĩa vụ phải di chuyển mã dọn dẹp của bạn vào bộ xử lý.

+0

Có vẻ như chương trình của tôi không nhận được các sự kiện này, nhưng chỉ có 'CTRL_C_EVENT'. Thử nghiệm ngay bây giờ với một 'switch' trong hàm xử lý của tôi, và nhấn CTRL + C từ giao diện điều khiển. – eang

8

Tùy thuộc vào yêu cầu cụ thể của bạn, bạn có một số tùy chọn. Nếu bạn chỉ đơn giản là muốn bỏ qua Ctrl +C bạn có thể gọi SetConsoleCtrlHandler qua NULL như tham số HandlerRoutine:

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

    // do work 

    return 0; 
} 

này loại bỏ tất cả các bộ xử lý tín hiệu. Để chấm dứt ứng dụng này, bạn phải triển khai logic tùy chỉnh để xác định thời điểm tắt.

Nếu bạn muốn xử lý Ctrl +C bạn có hai lựa chọn: Thiết lập một handler cho tín hiệu hoặc thông qua các đầu vào bàn phím trên để xử lý bàn phím thông thường.

Thiết lập trình xử lý tương tự như mã ở trên, nhưng thay vì chuyển NULL làm trình xử lý bạn cung cấp triển khai của riêng bạn.

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

Sản lượng của ứng dụng này là:

Starting 
[Ctrl]+C 
Ending 

Lưu ý rằng mã ngẫu nhiên được thực thi, không phụ thuộc vào mã bên trong chính while -loop. Trình xử lý tín hiệu tạo thành một danh sách liên kết, trong đó các hàm xử lý được gọi trên cơ sở được đăng ký lần cuối, được gọi là đầu tiên cho đến khi một trong các trình xử lý trả về TRUE. Nếu không có trình xử lý nào trả về TRUE, thì trình xử lý mặc định sẽ được gọi. Trình xử lý mặc định cho bảng điều khiển gọi ExitProcess khi xử lý Ctrl + C.

Nếu bạn muốn ngăn chặn bất kỳ tiền xử lý và xử lý Ctrl +C như là đầu vào bàn phím thông thường thay vào đó bạn phải thay đổi chế độ điều khiển bằng cách gọi 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; 
} 

Khi cờ ENABLE_PROCESSED_INPUT được lấy ra Ctrl +C không còn được xử lý bởi hệ thống và truyền cho giao diện điều khiển như đầu vào bàn phím thông thường. Nó có thể được đọc bằng cách sử dụng ReadConsoleInput hoặc ReadFile.

Tuyên bố từ chối: Phần trên đã được thử nghiệm trên Windows 8 64 bit, được biên dịch cho cấu hình 32 và 64 bit, Bản phát hành và Gỡ lỗi.

Các vấn đề liên quan