【问题标题】:How to catch Ctrl+C on Windows and Linux with Qt如何使用 Qt 在 Windows 和 Linux 上捕获 Ctrl+C
【发布时间】:2011-11-26 17:40:57
【问题描述】:

我有 Qt 控制台服务器应用程序。我希望有人按 Ctrl+C 正确退出我的服务器(调用析构函数等)。我已经阅读了this,但是我希望它可以在 Linux 和 Windows 上运行。怎么办?

【问题讨论】:

    标签: c++ windows linux qt console


    【解决方案1】:

    我使用这个类来捕获 C++ 控制台应用程序中的信号。然而,它并不特定于 Qt。它在 Windows 平台上使用 SetConsoleCtrlHandler(),在其他平台上使用 提供的功能。棘手的一点是“信号”不是一个跨平台术语——Windows 和 POSIX 对它们有不同的定义。无论如何,这门课试图将它们映射到一个共同的词汇表。 Ctrl^C 在两个平台上都能很好地映射。

    我希望这可以适应您的具体情况。请注意,错误检查很少,应该会有所改进。

    用法(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】:

      希望这对你有用。

      在你的班级标题中:

      #include <signal.h>
      class sth:{
          ...
          ...
        static void sigHandl(int sig){
         if(sig==2){
          QCoreApplication::quit();
          //exit(EXIT_FAILURE);
         }
        }
      };
      

      在你的构造函数中添加这一行:

      signal(SIGINT,sigHndl);
      

      【讨论】:

        【解决方案3】:

        该代码适用于 Windows,我认为他可能适用于 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);
        

        【讨论】:

        • 感谢您发布此问题的答案!在 Stack Overflow 上不鼓励仅使用代码的答案,因为没有上下文的代码转储无法解释解决方案的工作方式或原因,这使得原始发布者(或任何未来的读者)难以理解其背后的逻辑。请编辑您的问题并包含对您的代码的解释,以便其他人可以从您的回答中受益。谢谢!
        • 此答案适用于 Qt GUI 应用程序,而 OP 明确要求 Qt 控制台应用程序。 OP 希望捕获终止信号并对其进行处理,而不是为 GUI 应用程序设置快捷方式处理程序。请参阅 Unix signal 了解它在 Linux 上的工作原理。
        猜你喜欢
        • 2010-11-11
        • 1970-01-01
        • 1970-01-01
        • 2016-06-11
        • 2016-12-13
        • 2010-11-23
        • 2012-06-14
        • 2010-10-23
        • 1970-01-01
        相关资源
        最近更新 更多