2011-09-28 14 views
9

Tengo la aplicación del servidor de la consola Qt. Deseo que alguien presione Ctrl + C para salir de mi servidor correctamente (llamando destructores, etc.). He leído this, sin embargo, quiero que esto funcione tanto en Linux como en Windows. ¿Cómo hacerlo?Cómo capturar Ctrl + C en Windows y Linux con Qt

Respuesta

19

Utilizo esta clase para detectar señales en aplicaciones de consola C++. Sin embargo, no es específico de Qt. Utiliza SetConsoleCtrlHandler() en plataformas Windows y funciones proporcionadas por <signal.h> en otras plataformas. Lo difícil es que "señal" no es un término multiplataforma: Windows y POSIX tienen definiciones diferentes para ellos. De todos modos, esta clase intenta asignarlos a un vocabulario común. Ctrl^C es uno que se correlaciona bien en ambas plataformas.

Espero que esto se pueda adaptar a su situación específica. Tenga en cuenta que la comprobación de errores es mínima y probablemente debería mejorarse.

Uso (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

Ese trabajo de código en Windows y creo que podría funcionar en 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

¡Gracias por enviar una respuesta a esta pregunta! Se desalientan las respuestas de solo código en Stack Overflow, porque un volcado de código sin contexto no explica cómo o por qué la solución funcionará, lo que dificulta que el afiche original (o cualquier lector futuro) comprenda la lógica detrás de esto. Por favor, edite su pregunta e incluya una explicación de su código para que otros puedan beneficiarse de su respuesta. ¡Gracias! –

+0

Esta respuesta es para aplicaciones Qt GUI, mientras que el OP claramente solicita una aplicación de consola Qt. El OP quiere capturar la señal de finalización y manejarla, no configurar un controlador de accesos directos para una aplicación GUI. Consulte [Señal de Unix] (https://en.wikipedia.org/wiki/Unix_signal) para ver cómo funciona en Linux. –

Cuestiones relacionadas