Windows QPA/QPlatformServices::openUrl(): Add work around for anchors on local file URLs
Retrieve the browser executable and pass the URL as command line argument in this case to work around ShellExecute() failing to handle anchors. Pick-to: 6.4 6.3 6.2 5.15 Fixes: QTBUG-14460 Change-Id: I44ccd1126d5db6a387b36729837edc045908a670 Reviewed-by: André de la Rocha <andre.rocha@qt.io>bb10
parent
b9053b55f8
commit
7e89e6bfe8
|
|
@ -14,6 +14,7 @@
|
|||
#include <QtCore/private/qwinregistry_p.h>
|
||||
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
#include <intshcut.h>
|
||||
|
||||
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 @@ public:
|
|||
|
||||
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<const wchar_t *>(browser.utf16()),
|
||||
reinterpret_cast<const wchar_t *>(urlS.utf16()));
|
||||
thread.start();
|
||||
thread.wait();
|
||||
|
||||
const auto result = reinterpret_cast<quintptr>(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<const wchar_t *>(nativeFilePath.utf16()));
|
||||
QWindowsShellExecuteThread thread(nullptr,
|
||||
reinterpret_cast<const wchar_t *>(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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue