如何使用 Qt 在 Windows 和 Linux 上捕获 Ctrl+C

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7581343/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 06:23:55  来源:igfitidea点击:

How to catch Ctrl+C on Windows and Linux with Qt

c++windowslinuxqtconsole

提问by Mariy

I have Qt console server application. I wish when somebody presses Ctrl+C to quit my server correctly (calling destructors, etc.). I've read this, however I want this to work on both Linux and Windows. How to do it?

我有 Qt 控制台服务器应用程序。我希望当有人按 Ctrl+C 正确退出我的服务器(调用析构函数等)。我读过这个,但是我希望它在 Linux 和 Windows 上都可以使用。怎么做?

采纳答案by Mariy

I use this class to catch signals in C++ console applications. It is not specific to Qt, however. It uses SetConsoleCtrlHandler() on Windows platforms, and functions provided by <signal.h> on other platforms. The tricky bit is that "signal" is not a cross platform term - Windows and POSIX have different definitions for them. Anyway this class attempts to map them to a common vocabulary. Ctrl^C is one that maps well on both platforms.

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

I hope this can be adapted to your specific situation. Please mind that error checking is minimal and should probably be improved.

我希望这可以适应您的具体情况。请注意错误检查是最小的,可能应该改进。

Usage (main.cpp)

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

回答by dmitriy

That code work on Windows and i think he might work on Linux.

该代码适用于 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);

回答by hosh0425

Hope this works for you.

希望这对你有用。

In your class header:

在您的类标题中:

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

in your constructor add this line:

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

signal(SIGINT,sigHndl);