mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-24 10:16:00 +03:00
Tell Windows to wait during shutdown by creating a ShutdownBlockReason.
Huge thanks to paolo-sz for bringing this to my attention, for the various patches he tried to submit and for testing. See issue #1984 for complete history. Closes #1535.
This commit is contained in:
parent
6c1740f78f
commit
6644fe0714
4 changed files with 106 additions and 5 deletions
|
@ -37,6 +37,7 @@
|
|||
#ifdef Q_OS_WIN
|
||||
#include <Windows.h>
|
||||
#include <QSharedMemory>
|
||||
#include <QSessionManager>
|
||||
#endif // Q_OS_WIN
|
||||
#ifdef Q_OS_MAC
|
||||
#include <QFileOpenEvent>
|
||||
|
@ -77,7 +78,10 @@ Application::Application(const QString &id, int &argc, char **argv)
|
|||
#ifndef DISABLE_GUI
|
||||
setStyleSheet("QStatusBar::item { border-width: 0; }");
|
||||
setQuitOnLastWindowClosed(false);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
connect(this, SIGNAL(commitDataRequest(QSessionManager &)), this, SLOT(shutdownCleanup(QSessionManager &)), Qt::DirectConnection);
|
||||
#endif // Q_OS_WIN
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
connect(this, SIGNAL(messageReceived(const QString &)), SLOT(processMessage(const QString &)));
|
||||
connect(this, SIGNAL(aboutToQuit()), SLOT(cleanup()));
|
||||
|
@ -286,11 +290,69 @@ void Application::initializeTranslation()
|
|||
#endif
|
||||
}
|
||||
|
||||
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
||||
void Application::shutdownCleanup(QSessionManager &manager)
|
||||
{
|
||||
// This is only needed for a special case on Windows XP.
|
||||
// (but is called for every Windows version)
|
||||
// If a process takes too much time to exit during OS
|
||||
// shutdown, the OS presents a dialog to the user.
|
||||
// That dialog tells the user that qbt is blocking the
|
||||
// shutdown, it shows a progress bar and it offers
|
||||
// a "Terminate Now" button for the user. However,
|
||||
// after the progress bar has reached 100% another button
|
||||
// is offered to the user reading "Cancel". With this the
|
||||
// user can cancel the **OS** shutdown. If we don't do
|
||||
// the cleanup by handling the commitDataRequest() signal
|
||||
// and the user clicks "Cancel", it will result in qbt being
|
||||
// killed and the shutdown proceeding instead. Apparently
|
||||
// aboutToQuit() is emitted too late in the shutdown process.
|
||||
cleanup();
|
||||
|
||||
// According to the qt docs we shouldn't call quit() inside a slot.
|
||||
// aboutToQuit() is never emitted if the user hits "Cancel" in
|
||||
// the above dialog.
|
||||
QTimer::singleShot(0, qApp, SLOT(quit()));
|
||||
}
|
||||
#endif
|
||||
|
||||
void Application::cleanup()
|
||||
{
|
||||
#ifndef DISABLE_GUI
|
||||
delete m_window;
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
// cleanup() can be called multiple times during shutdown. We only need it once.
|
||||
static bool alreadyDone = false;
|
||||
|
||||
if (alreadyDone)
|
||||
return;
|
||||
alreadyDone = true;
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
// Hide the window and not leave it on screen as
|
||||
// unresponsive. Also for Windows take the WinId
|
||||
// after it's hidden, because hide() may cause a
|
||||
// WinId change.
|
||||
m_window->hide();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
typedef BOOL (WINAPI *PSHUTDOWNBRCREATE)(HWND, LPCWSTR);
|
||||
PSHUTDOWNBRCREATE shutdownBRCreate = (PSHUTDOWNBRCREATE)::GetProcAddress(::GetModuleHandleW(L"User32.dll"), "ShutdownBlockReasonCreate");
|
||||
// Only available on Vista+
|
||||
if (shutdownBRCreate)
|
||||
shutdownBRCreate((HWND)m_window->effectiveWinId(), tr("Saving torrent progress...").toStdWString().c_str());
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
// Do manual cleanup in MainWindow to force widgets
|
||||
// to save their Preferences, stop all timers and
|
||||
// delete as many widgets as possible to leave only
|
||||
// a 'shell' MainWindow.
|
||||
// We need a valid window handle for Windows Vista+
|
||||
// otherwise the system shutdown will continue even
|
||||
// though we created a ShutdownBlockReason
|
||||
m_window->cleanup();
|
||||
|
||||
#endif // DISABLE_GUI
|
||||
|
||||
#ifndef DISABLE_WEBUI
|
||||
delete m_webui;
|
||||
#endif
|
||||
|
@ -298,4 +360,15 @@ void Application::cleanup()
|
|||
TorrentPersistentData::drop();
|
||||
Preferences::drop();
|
||||
Logger::drop();
|
||||
#ifndef DISABLE_GUI
|
||||
#ifdef Q_OS_WIN
|
||||
typedef BOOL (WINAPI *PSHUTDOWNBRDESTROY)(HWND);
|
||||
PSHUTDOWNBRDESTROY shutdownBRDestroy = (PSHUTDOWNBRDESTROY)::GetProcAddress(::GetModuleHandleW(L"User32.dll"), "ShutdownBlockReasonDestroy");
|
||||
// Only available on Vista+
|
||||
if (shutdownBRDestroy)
|
||||
shutdownBRDestroy((HWND)m_window->effectiveWinId());
|
||||
#endif // Q_OS_WIN
|
||||
delete m_window;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
@ -38,6 +38,13 @@
|
|||
#include "qtsingleapplication.h"
|
||||
typedef QtSingleApplication BaseApplication;
|
||||
class MainWindow;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSessionManager;
|
||||
QT_END_NAMESPACE
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
#else
|
||||
#include "qtsinglecoreapplication.h"
|
||||
typedef QtSingleCoreApplication BaseApplication;
|
||||
|
@ -71,6 +78,9 @@ protected:
|
|||
private slots:
|
||||
void processMessage(const QString &message);
|
||||
void cleanup();
|
||||
#if (!defined(DISABLE_GUI) && defined(Q_OS_WIN))
|
||||
void shutdownCleanup(QSessionManager &manager);
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool m_running;
|
||||
|
|
|
@ -369,8 +369,6 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
// Save window size, columns size
|
||||
writeSettings();
|
||||
#ifdef Q_OS_MAC
|
||||
// Workaround to avoid bug http://bugreports.qt.nokia.com/browse/QTBUG-7305
|
||||
setUnifiedTitleAndToolBarOnMac(false);
|
||||
|
@ -592,6 +590,25 @@ void MainWindow::writeSettings()
|
|||
properties->saveSettings();
|
||||
}
|
||||
|
||||
void MainWindow::cleanup()
|
||||
{
|
||||
writeSettings();
|
||||
|
||||
delete executable_watcher;
|
||||
guiUpdater->stop();
|
||||
if (systrayCreator)
|
||||
systrayCreator->stop();
|
||||
if (preventTimer)
|
||||
preventTimer->stop();
|
||||
programUpdateTimer.stop();
|
||||
delete search_filter;
|
||||
delete searchFilterAct;
|
||||
delete tabs; // this seems enough to also delete all contained widgets
|
||||
delete status_bar;
|
||||
delete m_pwr;
|
||||
delete toolbarMenu;
|
||||
}
|
||||
|
||||
void MainWindow::readSettings()
|
||||
{
|
||||
const Preferences* const pref = Preferences::instance();
|
||||
|
|
|
@ -86,6 +86,7 @@ public slots:
|
|||
void updateAltSpeedsBtn(bool alternative);
|
||||
void updateNbTorrents();
|
||||
void activate();
|
||||
void cleanup();
|
||||
|
||||
protected slots:
|
||||
// GUI related slots
|
||||
|
|
Loading…
Reference in a new issue