From 7e7a3623214ae468fd94acce9ae1b68523f0cf50 Mon Sep 17 00:00:00 2001 From: Lukas Holecek <hluk@email.cz> Date: Wed, 8 Mar 2017 13:39:33 +0100 Subject: [PATCH] Handle exceptions Fixes #494 --- src/app/applicationexceptionhandler.cpp | 47 ++++++++++++++++ src/app/applicationexceptionhandler.h | 72 +++++++++++++++++++++++++ src/common/messagehandlerforqt.cpp | 20 ++++++- src/main.cpp | 22 ++++++-- src/platform/dummy/dummyplatform.cpp | 10 ++-- src/platform/mac/macplatform.mm | 9 ++-- src/platform/win/winplatform.cpp | 3 +- src/platform/x11/x11platform.cpp | 9 ++-- src/src.pro | 2 + 9 files changed, 177 insertions(+), 17 deletions(-) create mode 100644 src/app/applicationexceptionhandler.cpp create mode 100644 src/app/applicationexceptionhandler.h diff --git a/src/app/applicationexceptionhandler.cpp b/src/app/applicationexceptionhandler.cpp new file mode 100644 index 000000000..da4fc3c53 --- /dev/null +++ b/src/app/applicationexceptionhandler.cpp @@ -0,0 +1,47 @@ +/* + Copyright (c) 2017, Lukas Holecek <hluk@email.cz> + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "applicationexceptionhandler.h" + +#include "common/log.h" + +#include <QCoreApplication> +#include <QFile> +#include <QMetaObject> + +void logException(const char *what) +{ + QFile f; + f.open(stderr, QIODevice::WriteOnly); + f.write(what ? what : "Unknown exception"); + f.write("\n"); + f.close(); + + log( QString("Exception: ") + what, LogError ); +} + +void detail::ApplicationExceptionHandlerBase::exit(int exitCode) +{ + QMetaObject::invokeMethod(this, "exitSlot", Q_ARG(int, exitCode)); +} + +void detail::ApplicationExceptionHandlerBase::exitSlot(int exitCode) +{ + qApp->exit(exitCode); +} diff --git a/src/app/applicationexceptionhandler.h b/src/app/applicationexceptionhandler.h new file mode 100644 index 000000000..95b6259c8 --- /dev/null +++ b/src/app/applicationexceptionhandler.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2017, Lukas Holecek <hluk@email.cz> + + This file is part of CopyQ. + + CopyQ is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + CopyQ is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with CopyQ. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef APPLICATIONEXCEPTIONHANDLER_H +#define APPLICATIONEXCEPTIONHANDLER_H + +#include <QObject> + +#include <exception> + +class QCoreApplication; +class QEvent; + +void logException(const char *what = nullptr); + +namespace detail { + +class ApplicationExceptionHandlerBase : public QObject +{ + Q_OBJECT + +protected: + /// Exit application (thread-safe). + void exit(int exitCode); + +private slots: + void exitSlot(int exitCode); +}; + +} // namespace detail + +template <typename QtApplication> +class ApplicationExceptionHandler : public detail::ApplicationExceptionHandlerBase, public QtApplication +{ +public: + ApplicationExceptionHandler(int &argc, char **argv) + : QtApplication(argc, argv) + { + } + + bool notify(QObject *receiver, QEvent *event) override + { + try { + return QtApplication::notify(receiver, event); + } catch (const std::exception &e) { + logException(e.what()); + } catch (...) { + logException(); + } + + detail::ApplicationExceptionHandlerBase::exit(1); + return true; + } +}; + +#endif // APPLICATIONEXCEPTIONHANDLER_H diff --git a/src/common/messagehandlerforqt.cpp b/src/common/messagehandlerforqt.cpp index 8e466c90a..f13cb15b0 100644 --- a/src/common/messagehandlerforqt.cpp +++ b/src/common/messagehandlerforqt.cpp @@ -24,8 +24,26 @@ #include <QString> #include <QtGlobal> +#include <exception> + namespace { +class ExceptionQtFatal : public std::exception { +public: + explicit ExceptionQtFatal(const QByteArray &message) + : m_message(message) + { + } + + const char* what() const noexcept override + { + return m_message.constData(); + } + +private: + QByteArray m_message; +}; + void messageHandler(QtMsgType type, const QString &message) { switch (type) { @@ -45,7 +63,7 @@ void messageHandler(QtMsgType type, const QString &message) break; case QtFatalMsg: log("QtFatal: " + message, LogError); - abort(); + throw ExceptionQtFatal( message.toUtf8() ); } } diff --git a/src/main.cpp b/src/main.cpp index 8b8e37c6a..ca4973668 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,6 +18,7 @@ */ #include "app/app.h" +#include "app/applicationexceptionhandler.h" #include "app/clipboardclient.h" #include "app/clipboardmonitor.h" #include "app/clipboardserver.h" @@ -35,6 +36,8 @@ # include "tests/tests.h" #endif // HAS_TESTS +#include <exception> + Q_DECLARE_METATYPE(QByteArray*) namespace { @@ -158,9 +161,7 @@ QString getSessionName(const QStringList &arguments, int *skipArguments) return getTextData( qgetenv("COPYQ_SESSION_NAME") ); } -} // namespace - -int main(int argc, char **argv) +int startApplication(int argc, char **argv) { installMessageHandlerForQt(); @@ -212,3 +213,18 @@ int main(int argc, char **argv) // then run this process as client. return startClient(argc, argv, skipArguments, sessionName); } + +} // namespace + +int main(int argc, char **argv) +{ + try { + return startApplication(argc, argv); + } catch (const std::exception &e) { + logException(e.what()); + throw; + } catch (...) { + logException(); + throw; + } +} diff --git a/src/platform/dummy/dummyplatform.cpp b/src/platform/dummy/dummyplatform.cpp index ea14cc15b..0a2cfdcc5 100644 --- a/src/platform/dummy/dummyplatform.cpp +++ b/src/platform/dummy/dummyplatform.cpp @@ -21,6 +21,8 @@ #include "dummyclipboard.h" +#include "app/applicationexceptionhandler.h" + #include <QApplication> #include <QCoreApplication> #include <QDir> @@ -33,22 +35,22 @@ PlatformPtr createPlatformNativeInterface() QCoreApplication *DummyPlatform::createConsoleApplication(int &argc, char **argv) { - return new QCoreApplication(argc, argv); + return new ApplicationExceptionHandler<QCoreApplication>(argc, argv); } QApplication *DummyPlatform::createServerApplication(int &argc, char **argv) { - return new QApplication(argc, argv); + return new ApplicationExceptionHandler<QApplication>(argc, argv); } QApplication *DummyPlatform::createMonitorApplication(int &argc, char **argv) { - return new QApplication(argc, argv); + return new ApplicationExceptionHandler<QApplication>(argc, argv); } QCoreApplication *DummyPlatform::createClientApplication(int &argc, char **argv) { - return new QCoreApplication(argc, argv); + return new ApplicationExceptionHandler<QCoreApplication>(argc, argv); } PlatformClipboardPtr DummyPlatform::clipboard() diff --git a/src/platform/mac/macplatform.mm b/src/platform/mac/macplatform.mm index 3d5212ad2..a90bde83b 100644 --- a/src/platform/mac/macplatform.mm +++ b/src/platform/mac/macplatform.mm @@ -19,6 +19,7 @@ #include "macplatform.h" +#include "app/applicationexceptionhandler.h" #include "common/log.h" #include "copyqpasteboardmime.h" #include "foregroundbackgroundfilter.h" @@ -151,13 +152,13 @@ MacPlatform::MacPlatform() QCoreApplication *MacPlatform::createConsoleApplication(int &argc, char **argv) { - return new QCoreApplication(argc, argv); + return new ApplicationExceptionHandler<QCoreApplication>(argc, argv); } QApplication *MacPlatform::createServerApplication(int &argc, char **argv) { MacActivity activity(MacActivity::Background, "CopyQ Server"); - QApplication *app = new ClipboardApplication(argc, argv); + QApplication *app = new ApplicationExceptionHandler<ClipboardApplication>(argc, argv); // Switch the app to foreground when in foreground ForegroundBackgroundFilter::installFilter(app); @@ -168,13 +169,13 @@ QApplication *MacPlatform::createServerApplication(int &argc, char **argv) QApplication *MacPlatform::createMonitorApplication(int &argc, char **argv) { MacActivity activity(MacActivity::Background, "CopyQ clipboard monitor"); - return new ClipboardApplication(argc, argv); + return new ApplicationExceptionHandler<ClipboardApplication>(argc, argv); } QCoreApplication *MacPlatform::createClientApplication(int &argc, char **argv) { MacActivity activity(MacActivity::User, "CopyQ Client"); - return new QCoreApplication(argc, argv); + return new ApplicationExceptionHandler<QCoreApplication>(argc, argv); } PlatformClipboardPtr MacPlatform::clipboard() diff --git a/src/platform/win/winplatform.cpp b/src/platform/win/winplatform.cpp index ad9f49ff4..76b562805 100644 --- a/src/platform/win/winplatform.cpp +++ b/src/platform/win/winplatform.cpp @@ -17,6 +17,7 @@ along with CopyQ. If not, see <http://www.gnu.org/licenses/>. */ +#include "app/applicationexceptionhandler.h" #include "common/log.h" #include "common/settings.h" @@ -167,7 +168,7 @@ void installControlHandler() template <typename Application> Application *createApplication(int &argc, char **argv) { - Application *app = new Application(argc, argv); + Application *app = new ApplicationExceptionHandler<Application>(argc, argv); installControlHandler(); setBinaryFor(0); setBinaryFor(1); diff --git a/src/platform/x11/x11platform.cpp b/src/platform/x11/x11platform.cpp index 9145b474f..be71c14cc 100644 --- a/src/platform/x11/x11platform.cpp +++ b/src/platform/x11/x11platform.cpp @@ -19,6 +19,7 @@ #include "x11platform.h" +#include "app/applicationexceptionhandler.h" #include "common/common.h" #include <QApplication> @@ -205,23 +206,23 @@ void X11Platform::setAutostartEnabled(bool enable) QCoreApplication *X11Platform::createConsoleApplication(int &argc, char **argv) { - return new QCoreApplication(argc, argv); + return new ApplicationExceptionHandler<QCoreApplication>(argc, argv); } QApplication *X11Platform::createServerApplication(int &argc, char **argv) { old_xio_errhandler = XSetIOErrorHandler(copyq_xio_errhandler); - return new QApplication(argc, argv); + return new ApplicationExceptionHandler<QApplication>(argc, argv); } QApplication *X11Platform::createMonitorApplication(int &argc, char **argv) { - return new QApplication(argc, argv); + return new ApplicationExceptionHandler<QApplication>(argc, argv); } QCoreApplication *X11Platform::createClientApplication(int &argc, char **argv) { - return new QCoreApplication(argc, argv); + return new ApplicationExceptionHandler<QCoreApplication>(argc, argv); } PlatformClipboardPtr X11Platform::clipboard() diff --git a/src/src.pro b/src/src.pro index 625688a45..f1a0dec5f 100644 --- a/src/src.pro +++ b/src/src.pro @@ -28,6 +28,7 @@ FORMS += \ ui/logdialog.ui HEADERS += \ app/app.h \ + app/applicationexceptionhandler.h \ app/clipboardclient.h \ app/clipboardmonitor.h \ app/clipboardserver.h \ @@ -127,6 +128,7 @@ HEADERS += \ gui/menuitems.h SOURCES += \ app/app.cpp \ + app/applicationexceptionhandler.cpp \ app/clipboardclient.cpp \ app/clipboardmonitor.cpp \ app/clipboardserver.cpp \ -- GitLab