2011-09-28 19 views
9

Tôi có ứng dụng máy chủ bàn điều khiển Qt. Tôi muốn khi ai đó nhấn Ctrl + C để thoát khỏi máy chủ của tôi một cách chính xác (gọi destructors, vv). Tôi đã đọc this, tuy nhiên tôi muốn điều này hoạt động trên cả Linux và Windows. Làm thế nào để làm nó?Cách nắm bắt Ctrl + C trên Windows và Linux với Qt

Trả lời

19

Tôi sử dụng lớp này để bắt tín hiệu trong các ứng dụng giao diện điều khiển C++. Nó không phải là cụ thể để Qt, tuy nhiên. Nó sử dụng SetConsoleCtrlHandler() trên nền tảng Windows và các chức năng được cung cấp bởi <signal.h> trên các nền tảng khác. Bit khó hiểu là "tín hiệu" không phải là một thuật ngữ nền tảng chéo - Windows và POSIX có các định nghĩa khác nhau cho chúng. Dù sao lớp này cố gắng để ánh xạ chúng vào một từ vựng chung. Ctrl^C là bản đồ tốt trên cả hai nền tảng.

Tôi hy vọng điều này có thể được điều chỉnh theo tình huống cụ thể của bạn. Hãy nhớ rằng kiểm tra lỗi là tối thiểu và có lẽ nên được cải thiện.

sử dụng (main.cpp)

#include "SignalHandler.h" 

class Application : public SignalHandler 
{ 
public: 
    Application() : SignalHandler(SignalHandler::SIG_INT), myThread(NULL) {} 

    int Application::main(int argc, char *argv[]) 
    { 
     // Main program instructions here (e.g. start a thread) 
     myThread = new Thread(...); 
     myThread->start(); 
     myThread->join(); 
     delete myThread; 
     return 0; 
    } 

    bool handleSignal(int signal) 
    { 
     std::cout << "Handling signal " << signal << std::endl; 
     if (_myThread && _myThread->isRunning()) 
     { 
      _myThread->stop(); 
      // The thread is going to stop soon, so don't propagate this signal further 
      return true; 
     } 
     // Let the signal propagate as though we had not been there 
     return false; 
    } 
private: 
    Thread* myThread; 
}; 

int main(int argc, char* argv[]) 
{ 
    Application app; 
    return app.main(argc, argv); 
} 

SignalHandler.h

class SignalHandler 
{ 
public: 
    SignalHandler(int mask = DEFAULT_SIGNALS); 
    virtual ~SignalHandler(); 

    enum SIGNALS 
    { 
     SIG_UNHANDLED = 0, // Physical signal not supported by this class 
     SIG_NOOP  = 1, // The application is requested to do a no-op (only a target that platform-specific signals map to when they can't be raised anyway) 
     SIG_INT   = 2, // Control+C (should terminate but consider that it's a normal way to do so; can delay a bit) 
     SIG_TERM  = 4, // Control+Break (should terminate now without regarding the consquences) 
     SIG_CLOSE  = 8, // Container window closed (should perform normal termination, like Ctrl^C) [Windows only; on Linux it maps to SIG_TERM] 
     SIG_RELOAD  = 16, // Reload the configuration [Linux only, physical signal is SIGHUP; on Windows it maps to SIG_NOOP] 
     DEFAULT_SIGNALS = SIG_INT | SIG_TERM | SIG_CLOSE, 
    }; 
    static const int numSignals = 6; 

    virtual bool handleSignal(int signal) = 0; 

private: 
    int _mask; 
}; 

SignalHandler.cpp

#include "SignalHandler.h" 
#include <assert.h> 

#ifndef _WIN32 

#include <signal.h> 

#else 

#include <windows.h> 

#endif //!_WIN32 

// There can be only ONE SignalHandler per process 
SignalHandler* g_handler(NULL); 

#ifdef _WIN32 

BOOL WINAPI WIN32_handleFunc(DWORD); 
int WIN32_physicalToLogical(DWORD); 
DWORD WIN32_logicalToPhysical(int); 
std::set<int> g_registry; 

#else //_WIN32 

void POSIX_handleFunc(int); 
int POSIX_physicalToLogical(int); 
int POSIX_logicalToPhysical(int); 

#endif //_WIN32 

SignalHandler::SignalHandler(int mask) : _mask(mask) 
{ 
    assert(g_handler == NULL); 
    g_handler = this; 

#ifdef _WIN32 
    SetConsoleCtrlHandler(WIN32_handleFunc, TRUE); 
#endif //_WIN32 

    for (int i=0;i<numSignals;i++) 
    { 
     int logical = 0x1 << i; 
     if (_mask & logical) 
     { 
#ifdef _WIN32 
      g_registry.insert(logical); 
#else 
      int sig = POSIX_logicalToPhysical(logical); 
      bool failed = signal(sig, POSIX_handleFunc) == SIG_ERR; 
      assert(!failed); 
      (void)failed; // Silence the warning in non _DEBUG; TODO: something better 

#endif //_WIN32 
     } 
    } 

} 

SignalHandler::~SignalHandler() 
{ 
#ifdef _WIN32 
    SetConsoleCtrlHandler(WIN32_handleFunc, FALSE); 
#else 
    for (int i=0;i<numSignals;i++) 
    { 
     int logical = 0x1 << i; 
     if (_mask & logical) 
     { 
      signal(POSIX_logicalToPhysical(logical), SIG_DFL); 
     } 
    } 
#endif //_WIN32 
} 


#ifdef _WIN32 
DWORD WIN32_logicalToPhysical(int signal) 
{ 
    switch (signal) 
    { 
    case SignalHandler::SIG_INT: return CTRL_C_EVENT; 
    case SignalHandler::SIG_TERM: return CTRL_BREAK_EVENT; 
    case SignalHandler::SIG_CLOSE: return CTRL_CLOSE_EVENT; 
    default: 
     return ~(unsigned int)0; // SIG_ERR = -1 
    } 
} 
#else 
int POSIX_logicalToPhysical(int signal) 
{ 
    switch (signal) 
    { 
    case SignalHandler::SIG_INT: return SIGINT; 
    case SignalHandler::SIG_TERM: return SIGTERM; 
    // In case the client asks for a SIG_CLOSE handler, accept and 
    // bind it to a SIGTERM. Anyway the signal will never be raised 
    case SignalHandler::SIG_CLOSE: return SIGTERM; 
    case SignalHandler::SIG_RELOAD: return SIGHUP; 
    default: 
     return -1; // SIG_ERR = -1 
    } 
} 
#endif //_WIN32 


#ifdef _WIN32 
int WIN32_physicalToLogical(DWORD signal) 
{ 
    switch (signal) 
    { 
    case CTRL_C_EVENT: return SignalHandler::SIG_INT; 
    case CTRL_BREAK_EVENT: return SignalHandler::SIG_TERM; 
    case CTRL_CLOSE_EVENT: return SignalHandler::SIG_CLOSE; 
    default: 
     return SignalHandler::SIG_UNHANDLED; 
    } 
} 
#else 
int POSIX_physicalToLogical(int signal) 
{ 
    switch (signal) 
    { 
    case SIGINT: return SignalHandler::SIG_INT; 
    case SIGTERM: return SignalHandler::SIG_TERM; 
    case SIGHUP: return SignalHandler::SIG_RELOAD; 
    default: 
     return SignalHandler::SIG_UNHANDLED; 
    } 
} 
#endif //_WIN32 



#ifdef _WIN32 
BOOL WINAPI WIN32_handleFunc(DWORD signal) 
{ 
    if (g_handler) 
    { 
     int signo = WIN32_physicalToLogical(signal); 
     // The std::set is thread-safe in const reading access and we never 
     // write to it after the program has started so we don't need to 
     // protect this search by a mutex 
     std::set<int>::const_iterator found = g_registry.find(signo); 
     if (signo != -1 && found != g_registry.end()) 
     { 
      return g_handler->handleSignal(signo) ? TRUE : FALSE; 
     } 
     else 
     { 
      return FALSE; 
     } 
    } 
    else 
    { 
     return FALSE; 
    } 
} 
#else 
void POSIX_handleFunc(int signal) 
{ 
    if (g_handler) 
    { 
     int signo = POSIX_physicalToLogical(signal); 
     g_handler->handleSignal(signo); 
    } 
} 
#endif //_WIN32 
-2

Đó công việc mã trên Windows và tôi nghĩ anh ấy có thể làm việc trên Linux .

ui->setupUi(this); 
QAction *ctrlp =new QAction("plus",this), *ctrlm = new QAction("minus",this); 
ctrlp->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Plus)); 
ctrlm->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Minus)); 
connect(ctrlp, SIGNAL(triggered()), this, SLOT(on_pushButton_4_clicked())); 
connect(ctrlm, SIGNAL(triggered()), this, SLOT(on_pushButton_5_clicked())); 

connect(ui->pushButton_2,SIGNAL(clicked()),SLOT(close())); 
ui->textEdit->addAction(ctrlp); 
ui->textEdit->addAction(ctrlm); 
+1

Cảm ơn bạn đã đăng câu trả lời cho câu hỏi này! Các câu trả lời chỉ mã không được khuyến khích trên Stack Overflow, vì một đoạn mã không có ngữ cảnh không giải thích cách thức hoặc lý do giải pháp sẽ hoạt động, gây khó khăn cho poster gốc (hoặc bất kỳ người đọc nào trong tương lai) hiểu logic đằng sau nó. Vui lòng chỉnh sửa câu hỏi của bạn và đưa ra giải thích về mã của bạn để người khác có thể hưởng lợi từ câu trả lời của bạn. Cảm ơn! –

+0

Câu trả lời này dành cho các ứng dụng GUI Qt, trong khi OP yêu cầu một ứng dụng giao diện điều khiển Qt rõ ràng. OP muốn nắm bắt tín hiệu chấm dứt và xử lý nó, không thiết lập trình xử lý phím tắt cho ứng dụng GUI. Xem [Unix signal] (https://en.wikipedia.org/wiki/Unix_signal) để biết cách nó hoạt động trên Linux. –

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