Skip to content

Commit

Permalink
Windows QPA/QPlatformServices::openUrl(): Add work around for anchors…
Browse files Browse the repository at this point in the history
… 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 <[email protected]>
  • Loading branch information
FriedemannKleint authored and owolff committed Aug 26, 2022
1 parent b9053b5 commit 7e89e6b
Showing 1 changed file with 61 additions and 6 deletions.
67 changes: 61 additions & 6 deletions src/plugins/platforms/windows/qwindowsservices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <QtCore/private/qwinregistry_p.h>

#include <shlobj.h>
#include <shlwapi.h>
#include <intshcut.h>

QT_BEGIN_NAMESPACE
Expand All @@ -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();
}
}
Expand All @@ -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<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()
Expand All @@ -51,15 +103,17 @@ 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();

const auto result = reinterpret_cast<quintptr>(thread.result());

// 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;
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 7e89e6b

Please sign in to comment.