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