diff --git a/src/app/applicationexceptionhandler.cpp b/src/app/applicationexceptionhandler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da4fc3c536ae1c1e380254446369a80c519bc8ba --- /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 0000000000000000000000000000000000000000..95b6259c83ed888202f9130e47b2c2d27ac0e4ad --- /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 8e466c90a3defae534e1ac096f9d452c88d9e5f4..f13cb15b019777742deae6f8fb32fe813462186b 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 8b8e37c6ad46b22280efe0377adfdf6020195d43..ca497366859760e702f9741ecad4be9c23d392bd 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 ea14cc15b8000a83e10b5c1bc421caa27b256964..0a2cfdcc575800f80d09a07ccf708a21a88c9dbb 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 3d5212ad26780df56fd873a6c0b1aba538f37cac..a90bde83b6d4ebb635d819e48ea32ae71fe4b347 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 ad9f49ff4bb7d22203f95163cdbf2b4a219293f4..76b5628058ee9eac84450fa49cd2f5f3a3cccc74 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 9145b474f75e5c80afda5a38a455437f33880588..be71c14ccfba1de5b05ffeb19d0b4e338898c280 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 625688a4524403a449574f3daf491d4693637ed3..f1a0dec5fea13f49e66bd6c100a70303776bc68c 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 \