Skip to content

Commit

Permalink
Windows: Block system shutdown until we are properly shutdown
Browse files Browse the repository at this point in the history
(cherry picked from commit ade5977)
  • Loading branch information
TheOneRing committed Nov 22, 2021
1 parent 410337b commit 8d1c822
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 18 deletions.
1 change: 1 addition & 0 deletions changelog/unreleased/8979
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ We now ensure that we receive the window messages dispatched by the system.
https://github.com/owncloud/client/issues/8979
https://github.com/owncloud/client/pull/9142
https://github.com/owncloud/client/pull/9220
https://github.com/owncloud/client/pull/9227
5 changes: 5 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ include(ECMAddAppIcon)

find_package(Qt5 REQUIRED COMPONENTS Widgets)

#if (WIN32)
# find_package(Qt5 REQUIRED COMPONENTS WinExtras)
#endif()

add_subdirectory(updater)

set(client_UI_SRCS
Expand Down Expand Up @@ -169,6 +173,7 @@ elseif( WIN32 )
target_sources(owncloudCore PRIVATE
folderwatcher_win.cpp
navigationpanehelper.cpp)
# target_link_libraries(owncloudCore PUBLIC Qt5::WinExtras)
elseif(UNIX AND NOT APPLE )
## handle DBUS for Fdo notifications
find_package(Qt5 COMPONENTS DBus)
Expand Down
72 changes: 54 additions & 18 deletions src/gui/guiutility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@
#include <QIcon>

#ifdef Q_OS_WIN
#include <QThread>
#include <QTimer>
#include <QMetaMethod>

#include <chrono>
#include <thread>

using namespace std::chrono;
using namespace std::chrono_literals;

#include "version.h"
#endif

#include "theme.h"
Expand All @@ -38,47 +45,76 @@ Q_LOGGING_CATEGORY(lcUtility, "gui.utility", QtInfoMsg)
namespace {

#ifdef Q_OS_WIN
void watchWM()
// TODO: 2.11 move to the new Platform class
struct
{
HANDLE windowMessageWatcherEvent = CreateEventW(nullptr, true, false, L"watchWMEvent");
bool windowMessageWatcherRun = true;
} watchWMCtx;

void startShutdownWatcher()
{
// Qt only receives window message if a window was displayed at least once
// create an invisible window to handle WM_ENDSESSION
QThread::create([] {
// We also block a system shutdown until we are properly shutdown our selfs
// In the unlikely case that we require more than 5s Windows will require a fullscreen message
// with our icon, title and the reason why we are blocking the shutdown.
new std::thread([] {
WNDCLASS wc = {};
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = L"ocWindowMessageWatcher";
#if MIRALL_VERSION_MINOR > 9
// TODO: for now we won't display a proper icon
#error "Please add the QtWinExtras dependency"
wc.hIcon = QtWin::toHICON(Theme::instance()->applicationIcon().pixmap(64, 64));
#endif
wc.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -> LRESULT {
// qDebug() << MSG { hwnd, msg, wParam, lParam, 0, {} };
if (msg == WM_QUERYENDSESSION) {
qCDebug(OCC::lcUtility) << "Received WM_QUERYENDSESSION";
return 1;
} else if (msg == WM_ENDSESSION) {
qCInfo(OCC::lcUtility) << "Received WM_ENDSESSION quitting";
QTimer::singleShot(0, qApp, &QApplication::quit);
qCDebug(OCC::lcUtility) << "Received WM_ENDSESSION quitting";
QMetaObject::invokeMethod(qApp, &QApplication::quit);
QElapsedTimer shutdownTimer;
if (lParam == ENDSESSION_LOGOFF) {
// block the windows shutdown until we are done
const QString description = QApplication::translate("Utility", "Shutting down %1").arg(Theme::instance()->appNameGUI());
OC_ASSERT(ShutdownBlockReasonCreate(hwnd, reinterpret_cast<const wchar_t *>(description.utf16())));
}
WaitForSingleObject(watchWMCtx.windowMessageWatcherEvent, INFINITE);
if (lParam == ENDSESSION_LOGOFF) {
OC_ASSERT(ShutdownBlockReasonDestroy(hwnd));
}
qCInfo(OCC::lcUtility) << "WM_ENDSESSION successfully shut down" << shutdownTimer.elapsed();
watchWMCtx.windowMessageWatcherRun = false;
return 0;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
};

OC_ASSERT(RegisterClass(&wc));

auto window = CreateWindowW(wc.lpszClassName, L"watcher", WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, wc.hInstance, nullptr);
OC_ASSERT_X(window, Utility::formatWinError(GetLastError()).toUtf8().constData());
auto watcherWindow = CreateWindowW(wc.lpszClassName, reinterpret_cast<const wchar_t *>(Theme::instance()->appNameGUI().utf16()),
WS_OVERLAPPED, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr, wc.hInstance, nullptr);
OC_ASSERT_X(watcherWindow, Utility::formatWinError(GetLastError()).toUtf8().constData());

bool run = true;
QObject::connect(qApp, &QApplication::aboutToQuit, [&run] {
run = false;
});
MSG msg;
while (run) {
if (!PeekMessageW(&msg, window, 0, 0, PM_REMOVE)) {
QThread::msleep(100);
while (watchWMCtx.windowMessageWatcherRun) {
if (!PeekMessageW(&msg, watcherWindow, 0, 0, PM_REMOVE)) {
std::this_thread::sleep_for(100ms);
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
})->start();
});

qAddPostRoutine([] {
qCDebug(OCC::lcUtility) << "app closed";
SetEvent(watchWMCtx.windowMessageWatcherEvent);
});
}
Q_COREAPP_STARTUP_FUNCTION(watchWM);
Q_COREAPP_STARTUP_FUNCTION(startShutdownWatcher);
#endif
}

Expand Down

0 comments on commit 8d1c822

Please sign in to comment.