簡體   English   中英

如何在同一個程序中崩潰時自動重啟Qt應用程序?

[英]How to auto restart a Qt application when it crashes, within the same program?

是否有任何相對“標准”的設計來自動重啟Qt應用程序,當它崩潰異常時?

特定於Windows,我是否必須使用任何Windows服務?
或者,如果我必須單獨編寫另一個程序,那么該怎么做?

以下是使用可以充當監視器或業務邏輯的單個應用程序的方法。 它類似於Jon Harper的答案,除了代碼,而不是散文:)

注意

  1. 監視器不應該實例化QApplicationQGuiApplication :它沒有UI。 否則,冗余運行過程指示器將出現在某些平台上(即OS X,Win 10)。

  2. 通過在被調用進程中設置環境變量來實現監視/業務邏輯選擇。

  3. 通過命令行參數傳遞監視器/業務邏輯選擇是有問題的,因為需要過濾掉命令行開關 - 這樣做可以移植而不會遇到極端情況是很棘手的。

  4. 監視進程轉發業務邏輯進程的控制台I / O以及返回代碼。

// https://github.com/KubaO/stackoverflown/tree/master/questions/appmonitor-37524491
#include <QtWidgets>
#include <cstdlib>
#if defined(Q_OS_WIN32)
#include <windows.h>
#else
static void DebugBreak() { abort(); }
#endif

static int businessLogicMain(int &argc, char **argv) {
   QApplication app{argc, argv};
   qDebug() << __FUNCTION__ << app.arguments();
   QWidget w;
   QHBoxLayout layout{&w};
   QPushButton crash{"Crash"};  // purposefully crash for testing
   QPushButton quit{"Quit"};    // graceful exit, which doesn't need restart
   layout.addWidget(&crash);
   layout.addWidget(&quit);
   w.show();

   QObject::connect(&crash, &QPushButton::clicked, DebugBreak);
   QObject::connect(&quit, &QPushButton::clicked, &QCoreApplication::quit);
   return app.exec();
}

static char const kRunLogic[] = "run__business__logic";
static char const kRunLogicValue[] = "run__business__logic";

#if defined(Q_OS_WIN32)
static QString getWindowsCommandLineArguments() {
   const wchar_t *args = GetCommandLine();
   bool oddBackslash = false, quoted = false, whitespace = false;
   // skip the executable name according to Windows command line parsing rules
   while (auto c = *args) {
      if (c == L'\\')
         oddBackslash ^= 1;
      else if (c == L'"')
         quoted ^= !oddBackslash;
      else if (c == L' ' || c == L'\t')
         whitespace = !quoted;
      else if (whitespace)
         break;
      else
         oddBackslash = false;
      args++;
   }
   return QString::fromRawData(reinterpret_cast<const QChar*>(args), lstrlen(args));
}
#endif

static int monitorMain(int &argc, char **argv) {
#if !defined(Q_OS_WIN32)
   QStringList args;
   args.reserve(argc-1);
   for (int i = 1; i < argc; ++i)
     args << QString::fromLocal8Bit(argv[i]);
#endif
   QCoreApplication app{argc, argv};
   QProcess proc;
   auto onFinished = [&](int retcode, QProcess::ExitStatus status) {
      qDebug() << status;
      if (status == QProcess::CrashExit)
         proc.start();      // restart the app if the app crashed
      else
         app.exit(retcode); // no restart required
   };
   QObject::connect(&proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), onFinished);

   auto env = QProcessEnvironment::systemEnvironment();
   env.insert(kRunLogic, kRunLogicValue);
   proc.setProgram(app.applicationFilePath()); // logic and monitor are the same executable
#if defined(Q_OS_WIN32)
   SetErrorMode(SEM_NOGPFAULTERRORBOX);        // disable Windows error reporting
   proc.setNativeArguments(getWindowsCommandLineArguments()); // pass command line arguments natively
   env.insert("QT_LOGGING_TO_CONSOLE", "1");   // ensure that the debug output gets passed along
#else
   proc.setArguments(args);
#endif
   proc.setProcessEnvironment(env);
   proc.setProcessChannelMode(QProcess::ForwardedChannels);
   proc.start();
   return app.exec();
}

int main(int argc, char **argv) {
   if (qgetenv(kRunLogic) != kRunLogicValue)
      return monitorMain(argc, argv);
   else
      return qunsetenv(kRunLogic), businessLogicMain(argc, argv);
}

如果應用程序崩潰,那就完成了。

您的監視器想法很好,可以使用QProcess實現。 使用“monitor”來引導您的實際應用程序。 為此,請使用QProcess成員實現監視對象。 在偽代碼中:

 class MonitorObject : public QObject
 {
     ...
 public Q_SLOTS:
     void onStarted();
     void onFinished(int, QProcess::ExitStatus);
     ...
 private:
     QProcess m_process;
 }

然后在main

  • 在堆棧上創建QCoreApplication和監視對象。
  • 將排隊的信號發送到監視器對象,以便它知道主事件循環何時開始。 你可以使用QMetaObject::invokeQt::QueuedConnection來實現這個Qt::QueuedConnection

     int main(...) { QCoreApplication app; MonitorObject monitor; ... // other initialization code here QMetaObject::invoke(&monitor, "onStarted", Qt::QueuedConnection); return app.exec(); } 

在您的MonitorObject

  • QProcessfinished信號連接到onFinished
  • 調用MonitorObject::onStarted ,啟動該進程。
  • QProcess::finished信號觸發時,重新啟動有問題的程序或退出,具體取決於發出信號中的exitCode參數。

我不知道在崩潰時重啟應用程序的任何標准Qt方法。 但是有一個很好的課程,這使得編寫監督/監督課程變得非常容易。 它被稱為QProcess。

你可以像這樣開始這個過程:

monitorClass::startProcess(QString commandLine) // e.g. "c:\mytestapp.exe param1 param2"
{
    mp_Process = new QProcess(this);
    mp_Process->start(commandLine);
    mp_Process->waitForStarted();

    // Start a timer
    mp_Timer->start(1000);
}

然后當計時器到期時(每秒 - 或其他)

void monitorClass::TimerExpired(void)
{
    switch (mp_Process->state())
    {
        default:
        case QProcess::NotRunning:
        {
            qDebug("Process has stopped un-expectedly\n");

            // Tell the supervisor that the process has terminated
            // restart the process
            startProcess("c:\mytestapp.exe param1 param2"); // just an example
            break;
        }
        case QProcess::Starting:
        case QProcess::Running:
        {
            qDebug("Process is running ok\n");
            break;
        }
    }
}

注意

這實際上是偽代碼,它不是一個可編譯的例子 - 它只是向您展示使用QProcess執行此操作的難易程度...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM