Browse Source

[Qt] catch Windows shutdown events while client is running

- prevents unsafe shutdowns on Windows, which is known to be
  able to cause problems with wallet.dat
- if a users ends a Windows session, this will initiate a client shutdown
  and show a Windows dialog, that tells the user what is going on (for
  Windows Vista and higher it will even show a reason for blocking the
  Windows session end)
0.10
Philip Kaufmann 11 years ago
parent
commit
d282c1fb64
  1. 6
      src/qt/Makefile.am
  2. 22
      src/qt/bitcoin.cpp
  3. 57
      src/qt/winshutdownmonitor.cpp
  4. 29
      src/qt/winshutdownmonitor.h

6
src/qt/Makefile.am

@ -211,7 +211,8 @@ BITCOIN_QT_H = \
walletframe.h \ walletframe.h \
walletmodel.h \ walletmodel.h \
walletmodeltransaction.h \ walletmodeltransaction.h \
walletview.h walletview.h \
winshutdownmonitor.h
RES_ICONS = \ RES_ICONS = \
res/icons/add.png \ res/icons/add.png \
@ -277,7 +278,8 @@ BITCOIN_QT_CPP = \
rpcconsole.cpp \ rpcconsole.cpp \
splashscreen.cpp \ splashscreen.cpp \
trafficgraphwidget.cpp \ trafficgraphwidget.cpp \
utilitydialog.cpp utilitydialog.cpp \
winshutdownmonitor.cpp
if ENABLE_WALLET if ENABLE_WALLET
BITCOIN_QT_CPP += \ BITCOIN_QT_CPP += \

22
src/qt/bitcoin.cpp

@ -15,6 +15,7 @@
#include "optionsmodel.h" #include "optionsmodel.h"
#include "splashscreen.h" #include "splashscreen.h"
#include "utilitydialog.h" #include "utilitydialog.h"
#include "winshutdownmonitor.h"
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
#include "paymentserver.h" #include "paymentserver.h"
#include "walletmodel.h" #include "walletmodel.h"
@ -189,6 +190,9 @@ public:
/// Get process return value /// Get process return value
int getReturnValue() { return returnValue; } int getReturnValue() { return returnValue; }
/// Get window identifier of QMainWindow (BitcoinGUI)
WId getMainWinId() const;
public slots: public slots:
void initializeResult(int retval); void initializeResult(int retval);
void shutdownResult(int retval); void shutdownResult(int retval);
@ -444,6 +448,14 @@ void BitcoinApplication::handleRunawayException(const QString &message)
::exit(1); ::exit(1);
} }
WId BitcoinApplication::getMainWinId() const
{
if (!window)
return 0;
return window->winId();
}
#ifndef BITCOIN_QT_TEST #ifndef BITCOIN_QT_TEST
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
@ -558,10 +570,15 @@ int main(int argc, char *argv[])
/// 9. Main GUI initialization /// 9. Main GUI initialization
// Install global event filter that makes sure that long tooltips can be word-wrapped // Install global event filter that makes sure that long tooltips can be word-wrapped
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
// Install qDebug() message handler to route to debug.log
#if QT_VERSION < 0x050000 #if QT_VERSION < 0x050000
// Install qDebug() message handler to route to debug.log
qInstallMsgHandler(DebugMessageHandler); qInstallMsgHandler(DebugMessageHandler);
#else #else
#if defined(Q_OS_WIN)
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
qApp->installNativeEventFilter(new WinShutdownMonitor());
#endif
// Install qDebug() message handler to route to debug.log
qInstallMessageHandler(DebugMessageHandler); qInstallMessageHandler(DebugMessageHandler);
#endif #endif
// Load GUI settings from QSettings // Load GUI settings from QSettings
@ -577,6 +594,9 @@ int main(int argc, char *argv[])
{ {
app.createWindow(isaTestNet); app.createWindow(isaTestNet);
app.requestInitialize(); app.requestInitialize();
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("Bitcoin Core did't yet exit safely..."), (HWND)app.getMainWinId());
#endif
app.exec(); app.exec();
app.requestShutdown(); app.requestShutdown();
app.exec(); app.exec();

57
src/qt/winshutdownmonitor.cpp

@ -0,0 +1,57 @@
// Copyright (c) 2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "winshutdownmonitor.h"
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
#include "init.h"
#include <windows.h>
#include <QDebug>
// If we don't want a message to be processed by Qt, return true and set result to
// the value that the window procedure should return. Otherwise return false.
bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult)
{
Q_UNUSED(eventType);
MSG *pMsg = static_cast<MSG *>(pMessage);
switch(pMsg->message)
{
case WM_QUERYENDSESSION:
{
// Initiate a client shutdown after receiving a WM_QUERYENDSESSION and block
// Windows session end until we have finished client shutdown.
StartShutdown();
*pnResult = FALSE;
return true;
}
case WM_ENDSESSION:
{
*pnResult = FALSE;
return true;
}
}
return false;
}
void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId)
{
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate");
if (shutdownBRCreate == NULL) {
qDebug() << "registerShutdownBlockReason : GetProcAddress for ShutdownBlockReasonCreate failed";
return;
}
if (shutdownBRCreate(mainWinId, strReason.toStdWString().c_str()))
qDebug() << "registerShutdownBlockReason : Successfully registered: " + strReason;
else
qDebug() << "registerShutdownBlockReason : Failed to register: " + strReason;
}
#endif

29
src/qt/winshutdownmonitor.h

@ -0,0 +1,29 @@
// Copyright (c) 2014 The Bitcoin developers
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef WINSHUTDOWNMONITOR_H
#define WINSHUTDOWNMONITOR_H
#ifdef WIN32
#include <QByteArray>
#include <QString>
#if QT_VERSION >= 0x050000
#include <windef.h> // for HWND
#include <QAbstractNativeEventFilter>
class WinShutdownMonitor : public QAbstractNativeEventFilter
{
public:
/** Implements QAbstractNativeEventFilter interface for processing Windows messages */
bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult);
/** Register the reason for blocking shutdown on Windows to allow clean client exit */
static void registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId);
};
#endif
#endif
#endif // WINSHUTDOWNMONITOR_H
Loading…
Cancel
Save