diff options
author | Vladimir Belyavsky <belyavskyv@gmail.com> | 2022-05-14 17:23:17 +0300 |
---|---|---|
committer | Vladimir Belyavsky <belyavskyv@gmail.com> | 2022-06-03 09:34:19 +0300 |
commit | 66bcb66a51b523fdb6dabefa6366abed17f3b06f (patch) | |
tree | 703cdbac71088c77afb9197ba20da19b6ca96cdd /src/plugins | |
parent | 4a8611a902cc85ca6f93394f9e18d343f41e4112 (diff) |
Windows: fix crash on closing an app when native file dialog is opened
The crash was provoked by QThread::terminate() called
for QWindowsDialogThread from QWindowsDialogHelperBase destructor.
It's still not clear why terminating the thread here causes a crash,
but normally we should avoid terminating a thread anyway.
Current changes make several improvements to avoid terminating the
thread. The main problem was that QWindowsDialogThread::run() was never
returned. That's because QWindowsNativeFileDialogBase::close() was not
called on QWindowsDialogHelperBase destruction. The second problem
was that QWindowsNativeFileDialogBase::close() may still not close
native file dialog because it was not able to find HWND for IFileDialog
instance in some circumstances, so make this by more robust way.
Fixes: QTBUG-93298
Pick-to: 6.3
Change-Id: I55c8cf664ae2cf7c41c8cce43a6bb88a2680bf14
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@qt.io>
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdialoghelpers.cpp | 60 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdialoghelpers.h | 2 |
2 files changed, 25 insertions, 37 deletions
diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index bfd2608648..1aa62b3ce7 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -106,6 +106,22 @@ void eatMouseMove() qCDebug(lcQpaDialogs) << __FUNCTION__ << "triggered=" << (msg.message == WM_MOUSEMOVE); } +HWND getHWND(IFileDialog *fileDialog) +{ + IOleWindow *oleWindow = nullptr; + if (FAILED(fileDialog->QueryInterface(IID_IOleWindow, reinterpret_cast<void **>(&oleWindow)))) { + qCWarning(lcQpaDialogs, "Native file dialog: unable to query IID_IOleWindow interface."); + return HWND(0); + } + + HWND result(0); + if (FAILED(oleWindow->GetWindow(&result))) + qCWarning(lcQpaDialogs, "Native file dialog: unable to get dialog's window."); + + oleWindow->Release(); + return result; +} + } // namespace QWindowsDialogs /*! @@ -178,6 +194,13 @@ private: */ template <class BaseClass> +QWindowsDialogHelperBase<BaseClass>::~QWindowsDialogHelperBase() +{ + hide(); + cleanupThread(); +} + +template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::cleanupThread() { if (m_thread) { // Thread may be running if the dialog failed to close. @@ -303,41 +326,6 @@ void QWindowsDialogHelperBase<BaseClass>::stopTimer() } } -// Find a file dialog window created by IFileDialog by process id, window -// title and class, which starts with a hash '#'. - -struct FindDialogContext -{ - explicit FindDialogContext(const QString &titleIn) - : title(qStringToWCharArray(titleIn)), processId(GetCurrentProcessId()), hwnd(nullptr) {} - - const QScopedArrayPointer<wchar_t> title; - const DWORD processId; - HWND hwnd; // contains the HWND of the window found. -}; - -static BOOL QT_WIN_CALLBACK findDialogEnumWindowsProc(HWND hwnd, LPARAM lParam) -{ - auto *context = reinterpret_cast<FindDialogContext *>(lParam); - DWORD winPid = 0; - GetWindowThreadProcessId(hwnd, &winPid); - if (winPid != context->processId) - return TRUE; - wchar_t buf[256]; - if (!RealGetWindowClass(hwnd, buf, sizeof(buf)/sizeof(wchar_t)) || buf[0] != L'#') - return TRUE; - if (!GetWindowTextW(hwnd, buf, sizeof(buf)/sizeof(wchar_t)) || wcscmp(buf, context->title.data()) != 0) - return TRUE; - context->hwnd = hwnd; - return FALSE; -} - -static inline HWND findDialogWindow(const QString &title) -{ - FindDialogContext context(title); - EnumWindows(findDialogEnumWindowsProc, reinterpret_cast<LPARAM>(&context)); - return context.hwnd; -} template <class BaseClass> void QWindowsDialogHelperBase<BaseClass>::hide() @@ -1233,7 +1221,7 @@ void QWindowsNativeFileDialogBase::close() m_fileDialog->Close(S_OK); // IFileDialog::Close() does not work unless invoked from a callback. // Try to find the window and send it a WM_CLOSE in addition. - const HWND hwnd = findDialogWindow(m_title); + const HWND hwnd = QWindowsDialogs::getHWND(m_fileDialog); qCDebug(lcQpaDialogs) << __FUNCTION__ << "closing" << hwnd; if (hwnd && IsWindowVisible(hwnd)) PostMessageW(hwnd, WM_CLOSE, 0, 0); diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.h b/src/plugins/platforms/windows/qwindowsdialoghelpers.h index de272289c0..64f40bc3a9 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.h +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.h @@ -31,7 +31,7 @@ class QWindowsDialogHelperBase : public BaseClass Q_DISABLE_COPY_MOVE(QWindowsDialogHelperBase) public: using QWindowsNativeDialogBasePtr = QSharedPointer<QWindowsNativeDialogBase>; - ~QWindowsDialogHelperBase() { cleanupThread(); } + ~QWindowsDialogHelperBase(); void exec() override; bool show(Qt::WindowFlags windowFlags, |