Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qwindowsservices.cpp
Go to the documentation of this file.
1
// Copyright (C) 2016 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:critical reason:execute-external-code
4
5
#
include
"qwindowsservices.h"
6
#
include
<
QtCore
/
qt_windows
.
h
>
7
8
#
include
<
QtCore
/
qurl
.
h
>
9
#
include
<
QtCore
/
qdebug
.
h
>
10
#
include
<
QtCore
/
qdir
.
h
>
11
#
include
<
QtCore
/
qscopedpointer
.
h
>
12
#
include
<
QtCore
/
qthread
.
h
>
13
14
#
include
<
QtCore
/
private
/
qwinregistry_p
.
h
>
15
#
include
<
QtCore
/
private
/
qfunctions_win_p
.
h
>
16
17
#
include
<
shlobj
.
h
>
18
#
include
<
shlwapi
.
h
>
19
#
include
<
intshcut
.
h
>
20
21
QT_BEGIN_NAMESPACE
22
23
using
namespace
Qt::StringLiterals;
24
25
class
QWindowsShellExecuteThread
:
public
QThread
26
{
27
public
:
28
explicit
QWindowsShellExecuteThread
(
const
wchar_t
*operation,
const
wchar_t
*file,
29
const
wchar_t
*parameters)
30
: m_operation(operation)
31
, m_file(file)
32
, m_parameters(parameters) { }
33
34
void
run
()
override
35
{
36
QComHelper comHelper;
37
if
(comHelper.isValid())
38
m_result = ShellExecute(
nullptr
, m_operation, m_file, m_parameters,
nullptr
,
39
SW_SHOWNORMAL);
40
}
41
42
HINSTANCE
result
()
const
{
return
m_result; }
43
44
private
:
45
HINSTANCE m_result =
nullptr
;
46
const
wchar_t
*m_operation;
47
const
wchar_t
*m_file;
48
const
wchar_t
*m_parameters;
49
};
50
51
static
QString
msgShellExecuteFailed
(
const
QUrl &url, quintptr code)
52
{
53
QString result;
54
QTextStream(&result) <<
"ShellExecute '"
<< url.toString() <<
"' failed (error "
<< code <<
")."
;
55
return
result;
56
}
57
58
// Retrieve the web browser and open the URL. This should be used for URLs with
59
// fragments which don't work when using ShellExecute() directly (QTBUG-14460,
60
// QTBUG-55300).
61
static
bool
openWebBrowser
(
const
QUrl &url)
62
{
63
WCHAR browserExecutable[MAX_PATH] = {};
64
const
wchar_t
operation[] = L"open";
65
DWORD browserExecutableSize = MAX_PATH;
66
if
(FAILED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, L"http", operation,
67
browserExecutable, &browserExecutableSize))) {
68
return
false
;
69
}
70
QString browser = QString::fromWCharArray(browserExecutable, browserExecutableSize - 1);
71
// Workaround for "old" MS Edge entries. Instead of LaunchWinApp.exe we can just use msedge.exe
72
if
(browser.contains(
"LaunchWinApp.exe"_L1
, Qt::CaseInsensitive))
73
browser =
"msedge.exe"_L1
;
74
const
QString urlS = url.toString(QUrl::FullyEncoded);
75
76
// Run ShellExecute() in a thread since it may spin the event loop.
77
// Prevent it from interfering with processing of posted events (QTBUG-85676).
78
QWindowsShellExecuteThread
thread
(
operation
,
79
reinterpret_cast
<
const
wchar_t
*>(browser.utf16())
,
80
reinterpret_cast
<
const
wchar_t
*>(urlS.utf16())
)
;
81
thread.start();
82
thread.wait();
83
84
const
auto
result =
reinterpret_cast
<quintptr>(thread.result());
85
qCDebug(lcQpaServices) << urlS << QString::fromWCharArray(browserExecutable) << result;
86
// ShellExecute returns a value greater than 32 if successful
87
if
(result <= 32) {
88
qCWarning(lcQpaServices,
"%s"
, qPrintable(msgShellExecuteFailed(url, result)));
89
return
false
;
90
}
91
return
true
;
92
}
93
94
static
inline
bool
shellExecute
(
const
QUrl &url)
95
{
96
const
QString nativeFilePath = url.isLocalFile() && !url.hasFragment() && !url.hasQuery()
97
? QDir::toNativeSeparators(url.toLocalFile())
98
: url.toString(QUrl::FullyEncoded);
99
100
101
// Run ShellExecute() in a thread since it may spin the event loop.
102
// Prevent it from interfering with processing of posted events (QTBUG-85676).
103
QWindowsShellExecuteThread
thread
(
nullptr
,
104
reinterpret_cast
<
const
wchar_t
*>(nativeFilePath.utf16())
,
105
nullptr
)
;
106
thread.start();
107
thread.wait();
108
109
const
auto
result =
reinterpret_cast
<quintptr>(thread.result());
110
111
// ShellExecute returns a value greater than 32 if successful
112
if
(result <= 32) {
113
qCWarning(lcQpaServices,
"%s"
, qPrintable(msgShellExecuteFailed(url, result)));
114
return
false
;
115
}
116
return
true
;
117
}
118
119
// Retrieve the commandline for the default mail client. It contains a
120
// placeholder %1 for the URL. The default key used below is the
121
// command line for the mailto: shell command.
122
static
inline
QString
mailCommand
()
123
{
124
enum
{ BufferSize =
sizeof
(
wchar_t
) * MAX_PATH };
125
126
const
wchar_t
mailUserKey[] = L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\mailto\\UserChoice";
127
128
// Check if user has set preference, otherwise use default.
129
QString keyName = QWinRegistryKey(HKEY_CURRENT_USER, mailUserKey)
130
.stringValue( L"Progid");
131
const
auto
mailto = keyName.isEmpty() ?
"mailto"_L1
: QLatin1StringView();
132
keyName += mailto +
"\\Shell\\Open\\Command"_L1
;
133
qCDebug(lcQpaServices) <<
"keyName="
<< keyName;
134
const
QString command = QWinRegistryKey(HKEY_CLASSES_ROOT, keyName).stringValue(L"");
135
// QTBUG-57816: As of Windows 10, if there is no mail client installed, an entry like
136
// "rundll32.exe .. url.dll,MailToProtocolHandler %l" is returned. Launching it
137
// silently fails or brings up a broken dialog after a long time, so exclude it and
138
// fall back to ShellExecute() which brings up the URL association dialog.
139
if
(command.isEmpty() || command.contains(u",MailToProtocolHandler"))
140
return
QString();
141
wchar_t
expandedCommand[MAX_PATH] = {0};
142
return
ExpandEnvironmentStrings(
reinterpret_cast
<
const
wchar_t
*>(command.utf16()),
143
expandedCommand, MAX_PATH)
144
? QString::fromWCharArray(expandedCommand) : command;
145
}
146
147
static
inline
bool
launchMail
(
const
QUrl &url)
148
{
149
QString command = mailCommand();
150
if
(command.isEmpty()) {
151
qCWarning(lcQpaServices,
"Cannot launch '%ls': There is no mail program installed."
, qUtf16Printable(url.toString()));
152
return
false
;
153
}
154
// Fix mail launch if no param is expected in this command.
155
if
(command.indexOf(
"%1"_L1
) < 0) {
156
qWarning(lcQpaServices) <<
"The mail command lacks the '%1' parameter."
;
157
return
false
;
158
}
159
//Make sure the path for the process is in quotes
160
const
QChar doubleQuote = u'"';
161
if
(!command.startsWith(doubleQuote)) {
162
const
int
exeIndex = command.indexOf(
".exe "_L1
, 0, Qt::CaseInsensitive);
163
if
(exeIndex != -1) {
164
command.insert(exeIndex + 4, doubleQuote);
165
command.prepend(doubleQuote);
166
}
167
}
168
// Pass the url as the parameter. Should use QProcess::startDetached(),
169
// but that cannot handle a Windows command line [yet].
170
command.replace(
"%1"_L1
, url.toString(QUrl::FullyEncoded));
171
qCDebug(lcQpaServices) <<
"Launching"
<< command;
172
//start the process
173
PROCESS_INFORMATION pi;
174
ZeroMemory(&pi,
sizeof
(pi));
175
STARTUPINFO si;
176
ZeroMemory(&si,
sizeof
(si));
177
si.cb =
sizeof
(si);
178
if
(!CreateProcess(
nullptr
,
reinterpret_cast
<
wchar_t
*>(
const_cast
<ushort *>(command.utf16())),
179
nullptr
,
nullptr
, FALSE, 0,
nullptr
,
nullptr
, &si, &pi)) {
180
qErrnoWarning(
"Unable to launch '%ls'"
, qUtf16Printable(command));
181
return
false
;
182
}
183
CloseHandle(pi.hProcess);
184
CloseHandle(pi.hThread);
185
return
true
;
186
}
187
188
bool
QWindowsServices::openUrl(
const
QUrl &url)
189
{
190
const
QString scheme = url.scheme();
191
if
(scheme == u"mailto" && launchMail(url))
192
return
true
;
193
return
url.isLocalFile() && url.hasFragment()
194
? openWebBrowser(url) : shellExecute(url);
195
}
196
197
bool
QWindowsServices::openDocument(
const
QUrl &url)
198
{
199
return
shellExecute(url);
200
}
201
202
QT_END_NAMESPACE
QWindowsShellExecuteThread
Definition
qwindowsservices.cpp:26
QWindowsShellExecuteThread::result
HINSTANCE result() const
Definition
qwindowsservices.cpp:42
QWindowsShellExecuteThread::QWindowsShellExecuteThread
QWindowsShellExecuteThread(const wchar_t *operation, const wchar_t *file, const wchar_t *parameters)
Definition
qwindowsservices.cpp:28
QWindowsShellExecuteThread::run
void run() override
Definition
qwindowsservices.cpp:34
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
launchMail
static bool launchMail(const QUrl &url)
Definition
qwindowsservices.cpp:147
msgShellExecuteFailed
static QString msgShellExecuteFailed(const QUrl &url, quintptr code)
Definition
qwindowsservices.cpp:51
mailCommand
static QString mailCommand()
Definition
qwindowsservices.cpp:122
shellExecute
static bool shellExecute(const QUrl &url)
Definition
qwindowsservices.cpp:94
openWebBrowser
static bool openWebBrowser(const QUrl &url)
Definition
qwindowsservices.cpp:61
qtbase
src
plugins
platforms
windows
qwindowsservices.cpp
Generated on
for Qt by
1.18.0