GNU/Linux >> Tutoriels Linux >  >> Linux

Comment attraper Ctrl + C sur Windows et Linux avec Qt

J'utilise cette classe pour capturer les signaux dans les applications console C++. Cependant, ce n'est pas spécifique à Qt. Il utilise SetConsoleCtrlHandler() sur les plates-formes Windows et les fonctions fournies par sur d'autres plates-formes. Le problème est que "signal" n'est pas un terme multiplateforme - Windows et POSIX ont des définitions différentes pour eux. Quoi qu'il en soit, cette classe tente de les associer à un vocabulaire commun. Ctrl^C est celui qui correspond bien aux deux plates-formes.

J'espère que cela pourra être adapté à votre situation spécifique. Veuillez noter que la vérification des erreurs est minime et devrait probablement être améliorée.

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

Linux
  1. Flatpak sur Linux :qu'est-ce que c'est et comment installer des applications avec ?

  2. Comment accéder aux systèmes de fichiers Linux dans Windows 10 et WSL 2

  3. Installer Linux Mint avec Windows 8 | Double démarrage Windows 8 et Linux Mint 12

  4. Comment travailler avec des utilisateurs et des groupes sous Linux

  5. Comment puis-je enregistrer le serveur Linux avec le serveur DNS Windows

Comment sauver votre système Windows ou Linux avec Rescatux

Comment créer une clé USB multiboot sous Linux et Windows

Comment exécuter un logiciel Windows sur Linux avec des bouteilles

Comment exécuter des applications Linux sur Windows 10 et 11 à l'aide de WSL

Comment faire un double démarrage Antergos Linux et Windows UEFI

Comment exécuter des applications d'interface graphique Linux sur Windows 10 avec WSL et WSLg