diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index fbad934a053..f2bd90f4bd1 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -14,6 +14,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -25,12 +26,17 @@ enum { debug = 0 }; class QWindowsShellExecuteThread : public QThread { public: - explicit QWindowsShellExecuteThread(const wchar_t *path) : m_path(path) { } + explicit QWindowsShellExecuteThread(const wchar_t *operation, const wchar_t *file, + const wchar_t *parameters) + : m_operation(operation) + , m_file(file) + , m_parameters(parameters) { } void run() override { if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { - m_result = ShellExecute(nullptr, nullptr, m_path, nullptr, nullptr, SW_SHOWNORMAL); + m_result = ShellExecute(nullptr, m_operation, m_file, m_parameters, nullptr, + SW_SHOWNORMAL); CoUninitialize(); } } @@ -39,9 +45,55 @@ class QWindowsShellExecuteThread : public QThread private: HINSTANCE m_result = nullptr; - const wchar_t *m_path; + const wchar_t *m_operation; + const wchar_t *m_file; + const wchar_t *m_parameters; }; +static QString msgShellExecuteFailed(const QUrl &url, quintptr code) +{ + QString result; + QTextStream(&result) <<"ShellExecute '" << url.toString() << "' failed (error " << code << ")."; + return result; +} + +// Retrieve the web browser and open the URL. This should be used for URLs with +// fragments which don't work when using ShellExecute() directly (QTBUG-14460, +// QTBUG-55300). +static bool openWebBrowser(const QUrl &url) +{ + WCHAR browserExecutable[MAX_PATH] = {}; + const wchar_t operation[] = L"open"; + DWORD browserExecutableSize = MAX_PATH; + if (FAILED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, L"http", operation, + browserExecutable, &browserExecutableSize))) { + return false; + } + QString browser = QString::fromWCharArray(browserExecutable, browserExecutableSize - 1); + // Workaround for "old" MS Edge entries. Instead of LaunchWinApp.exe we can just use msedge.exe + if (browser.contains("LaunchWinApp.exe"_L1, Qt::CaseInsensitive)) + browser = "msedge.exe"_L1; + const QString urlS = url.toString(QUrl::FullyEncoded); + + // Run ShellExecute() in a thread since it may spin the event loop. + // Prevent it from interfering with processing of posted events (QTBUG-85676). + QWindowsShellExecuteThread thread(operation, + reinterpret_cast(browser.utf16()), + reinterpret_cast(urlS.utf16())); + thread.start(); + thread.wait(); + + const auto result = reinterpret_cast(thread.result()); + if (debug) + qDebug() << __FUNCTION__ << urlS << QString::fromWCharArray(browserExecutable) << result; + // ShellExecute returns a value greater than 32 if successful + if (result <= 32) { + qWarning("%s", qPrintable(msgShellExecuteFailed(url, result))); + return false; + } + return true; +} + static inline bool shellExecute(const QUrl &url) { const QString nativeFilePath = url.isLocalFile() && !url.hasFragment() && !url.hasQuery() @@ -51,7 +103,9 @@ static inline bool shellExecute(const QUrl &url) // Run ShellExecute() in a thread since it may spin the event loop. // Prevent it from interfering with processing of posted events (QTBUG-85676). - QWindowsShellExecuteThread thread(reinterpret_cast(nativeFilePath.utf16())); + QWindowsShellExecuteThread thread(nullptr, + reinterpret_cast(nativeFilePath.utf16()), + nullptr); thread.start(); thread.wait(); @@ -59,7 +113,7 @@ static inline bool shellExecute(const QUrl &url) // ShellExecute returns a value greater than 32 if successful if (result <= 32) { - qWarning("ShellExecute '%ls' failed (error %zu).", qUtf16Printable(url.toString()), result); + qWarning("%s", qPrintable(msgShellExecuteFailed(url, result))); return false; } return true; @@ -136,7 +190,8 @@ bool QWindowsServices::openUrl(const QUrl &url) const QString scheme = url.scheme(); if (scheme == u"mailto" && launchMail(url)) return true; - return shellExecute(url); + return url.isLocalFile() && url.hasFragment() + ? openWebBrowser(url) : shellExecute(url); } bool QWindowsServices::openDocument(const QUrl &url)