Skip to content

Commit c63b2e2

Browse files
committed
Merge pull request #4043
d282c1f [Qt] catch Windows shutdown events while client is running (Philip Kaufmann)
2 parents 7cbe636 + d282c1f commit c63b2e2

File tree

4 files changed

+111
-3
lines changed

4 files changed

+111
-3
lines changed

src/qt/Makefile.am

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,8 @@ BITCOIN_QT_H = \
211211
walletframe.h \
212212
walletmodel.h \
213213
walletmodeltransaction.h \
214-
walletview.h
214+
walletview.h \
215+
winshutdownmonitor.h
215216

216217
RES_ICONS = \
217218
res/icons/add.png \
@@ -277,7 +278,8 @@ BITCOIN_QT_CPP = \
277278
rpcconsole.cpp \
278279
splashscreen.cpp \
279280
trafficgraphwidget.cpp \
280-
utilitydialog.cpp
281+
utilitydialog.cpp \
282+
winshutdownmonitor.cpp
281283

282284
if ENABLE_WALLET
283285
BITCOIN_QT_CPP += \

src/qt/bitcoin.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "optionsmodel.h"
1616
#include "splashscreen.h"
1717
#include "utilitydialog.h"
18+
#include "winshutdownmonitor.h"
1819
#ifdef ENABLE_WALLET
1920
#include "paymentserver.h"
2021
#include "walletmodel.h"
@@ -189,6 +190,9 @@ class BitcoinApplication: public QApplication
189190
/// Get process return value
190191
int getReturnValue() { return returnValue; }
191192

193+
/// Get window identifier of QMainWindow (BitcoinGUI)
194+
WId getMainWinId() const;
195+
192196
public slots:
193197
void initializeResult(int retval);
194198
void shutdownResult(int retval);
@@ -444,6 +448,14 @@ void BitcoinApplication::handleRunawayException(const QString &message)
444448
::exit(1);
445449
}
446450

451+
WId BitcoinApplication::getMainWinId() const
452+
{
453+
if (!window)
454+
return 0;
455+
456+
return window->winId();
457+
}
458+
447459
#ifndef BITCOIN_QT_TEST
448460
int main(int argc, char *argv[])
449461
{
@@ -558,10 +570,15 @@ int main(int argc, char *argv[])
558570
/// 9. Main GUI initialization
559571
// Install global event filter that makes sure that long tooltips can be word-wrapped
560572
app.installEventFilter(new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app));
561-
// Install qDebug() message handler to route to debug.log
562573
#if QT_VERSION < 0x050000
574+
// Install qDebug() message handler to route to debug.log
563575
qInstallMsgHandler(DebugMessageHandler);
564576
#else
577+
#if defined(Q_OS_WIN)
578+
// Install global event filter for processing Windows session related Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION)
579+
qApp->installNativeEventFilter(new WinShutdownMonitor());
580+
#endif
581+
// Install qDebug() message handler to route to debug.log
565582
qInstallMessageHandler(DebugMessageHandler);
566583
#endif
567584
// Load GUI settings from QSettings
@@ -577,6 +594,9 @@ int main(int argc, char *argv[])
577594
{
578595
app.createWindow(isaTestNet);
579596
app.requestInitialize();
597+
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
598+
WinShutdownMonitor::registerShutdownBlockReason(QObject::tr("Bitcoin Core did't yet exit safely..."), (HWND)app.getMainWinId());
599+
#endif
580600
app.exec();
581601
app.requestShutdown();
582602
app.exec();

src/qt/winshutdownmonitor.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) 2014 The Bitcoin developers
2+
// Distributed under the MIT/X11 software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include "winshutdownmonitor.h"
6+
7+
#if defined(Q_OS_WIN) && QT_VERSION >= 0x050000
8+
#include "init.h"
9+
10+
#include <windows.h>
11+
12+
#include <QDebug>
13+
14+
// If we don't want a message to be processed by Qt, return true and set result to
15+
// the value that the window procedure should return. Otherwise return false.
16+
bool WinShutdownMonitor::nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult)
17+
{
18+
Q_UNUSED(eventType);
19+
20+
MSG *pMsg = static_cast<MSG *>(pMessage);
21+
22+
switch(pMsg->message)
23+
{
24+
case WM_QUERYENDSESSION:
25+
{
26+
// Initiate a client shutdown after receiving a WM_QUERYENDSESSION and block
27+
// Windows session end until we have finished client shutdown.
28+
StartShutdown();
29+
*pnResult = FALSE;
30+
return true;
31+
}
32+
33+
case WM_ENDSESSION:
34+
{
35+
*pnResult = FALSE;
36+
return true;
37+
}
38+
}
39+
40+
return false;
41+
}
42+
43+
void WinShutdownMonitor::registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId)
44+
{
45+
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
46+
PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)GetProcAddress(GetModuleHandleA("User32.dll"), "ShutdownBlockReasonCreate");
47+
if (shutdownBRCreate == NULL) {
48+
qDebug() << "registerShutdownBlockReason : GetProcAddress for ShutdownBlockReasonCreate failed";
49+
return;
50+
}
51+
52+
if (shutdownBRCreate(mainWinId, strReason.toStdWString().c_str()))
53+
qDebug() << "registerShutdownBlockReason : Successfully registered: " + strReason;
54+
else
55+
qDebug() << "registerShutdownBlockReason : Failed to register: " + strReason;
56+
}
57+
#endif

src/qt/winshutdownmonitor.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) 2014 The Bitcoin developers
2+
// Distributed under the MIT/X11 software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef WINSHUTDOWNMONITOR_H
6+
#define WINSHUTDOWNMONITOR_H
7+
8+
#ifdef WIN32
9+
#include <QByteArray>
10+
#include <QString>
11+
12+
#if QT_VERSION >= 0x050000
13+
#include <windef.h> // for HWND
14+
15+
#include <QAbstractNativeEventFilter>
16+
17+
class WinShutdownMonitor : public QAbstractNativeEventFilter
18+
{
19+
public:
20+
/** Implements QAbstractNativeEventFilter interface for processing Windows messages */
21+
bool nativeEventFilter(const QByteArray &eventType, void *pMessage, long *pnResult);
22+
23+
/** Register the reason for blocking shutdown on Windows to allow clean client exit */
24+
static void registerShutdownBlockReason(const QString& strReason, const HWND& mainWinId);
25+
};
26+
#endif
27+
#endif
28+
29+
#endif // WINSHUTDOWNMONITOR_H

0 commit comments

Comments
 (0)