Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwinregistry.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 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:data-parser
4
5#define UMDF_USING_NTSTATUS // Avoid ntstatus redefinitions
6
8#include <QtCore/qvarlengtharray.h>
9#include <QtCore/qdebug.h>
10#include <QtCore/qendian.h>
11#include <QtCore/qlist.h>
12#include <QtCore/qwineventnotifier.h>
13#include <QtCore/private/qsystemerror_p.h>
14
15#include <ntstatus.h>
16
17// User mode version of ZwQueryKey, as per:
18// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwquerykey
19extern "C" NTSTATUS NTSYSCALLAPI NTAPI NtQueryKey(HANDLE KeyHandle, int KeyInformationClass,
20 PVOID KeyInformation, ULONG Length, PULONG ResultLength);
21
22QT_BEGIN_NAMESPACE
23
24using namespace Qt::StringLiterals;
25
26static const wchar_t *nullTerminate(const QString &s)
27{
28 // ### port to QString::nullTerminate() if this stays around for longer
29 return reinterpret_cast<const wchar_t*>(s.utf16());
30}
31
32QWinRegistryKey::QWinRegistryKey(QObject *parent)
33 : QObject(parent)
34{
35}
36
37// Open a key with the specified permissions (KEY_READ/KEY_WRITE).
38// "access" is to explicitly use the 32- or 64-bit branch.
39QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, const wchar_t *subKey,
40 REGSAM permissions, REGSAM access,
41 QObject *parent)
42 : QObject(parent)
43{
44 if (RegOpenKeyExW(parentHandle, subKey,
45 0, permissions | access, &m_key) != ERROR_SUCCESS) {
46 m_key = nullptr;
47 }
48}
49
50QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, const QString &subKey,
51 REGSAM permissions, REGSAM access, QObject *parent)
52 : QWinRegistryKey(parentHandle, nullTerminate(subKey), permissions, access, parent)
53{
54}
55
56QWinRegistryKey::~QWinRegistryKey()
57{
58 close();
59}
60
61void QWinRegistryKey::close()
62{
63 if (isValid()) {
64 RegCloseKey(m_key);
65 m_key = nullptr;
66 }
67}
68
69QString QWinRegistryKey::name() const
70{
71 if (!isValid())
72 return {};
73
74 // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_key_name_information
75 constexpr int kKeyNameInformation = 3;
76 struct KeyNameInformation { ULONG length = 0; WCHAR name[1] = {}; };
77
78 // Resolve name of key iteratively by first computing the needed size,
79 // and then querying the name, accounting for the possibility that the
80 // name length changes behind our back a few times.
81 DWORD keyInformationSize = 0;
82 for (int i = 0; i < 5; ++i) {
83 QByteArray buffer(keyInformationSize, 0u);
84 auto *keyNameInformation = reinterpret_cast<KeyNameInformation *>(buffer.data());
85 switch (NtQueryKey(m_key, kKeyNameInformation, keyNameInformation,
86 keyInformationSize, &keyInformationSize)) {
87 case STATUS_BUFFER_TOO_SMALL:
88 case STATUS_BUFFER_OVERFLOW:
89 continue;
90 case STATUS_SUCCESS: {
91 return QString::fromWCharArray(keyNameInformation->name,
92 keyNameInformation->length / sizeof(wchar_t));
93 }
94 default:
95 return {};
96 }
97 }
98
99 return {};
100}
101
102QVariant QWinRegistryKey::value(const wchar_t *subKey) const
103{
104 // NOTE: Empty value name is allowed in Windows registry, it means the default
105 // or unnamed value of a key, you can read/write/delete such value normally.
106
107 if (!isValid())
108 return {};
109
110 // Get the size and type of the value.
111 DWORD dataType = REG_NONE;
112 DWORD dataSize = 0;
113 LONG ret = RegQueryValueExW(m_key, subKey, nullptr, &dataType, nullptr, &dataSize);
114 if (ret != ERROR_SUCCESS)
115 return {};
116
117 // Workaround for rare cases where the trailing '\0' is missing.
118 if (dataType == REG_SZ || dataType == REG_EXPAND_SZ)
119 dataSize += 2;
120 else if (dataType == REG_MULTI_SZ)
121 dataSize += 4;
122
123 // Get the value.
124 QVarLengthArray<unsigned char> data(dataSize);
125 std::fill(data.data(), data.data() + dataSize, 0u);
126
127 ret = RegQueryValueExW(m_key, subKey, nullptr, nullptr, data.data(), &dataSize);
128 if (ret != ERROR_SUCCESS)
129 return {};
130
131 switch (dataType) {
132 case REG_SZ:
133 case REG_EXPAND_SZ: {
134 if (dataSize > 0) {
135 return QString::fromWCharArray(
136 reinterpret_cast<const wchar_t *>(data.constData()));
137 }
138 return QString();
139 }
140
141 case REG_MULTI_SZ: {
142 if (dataSize > 0) {
143 QStringList list = {};
144 int i = 0;
145 while (true) {
146 const QString str = QString::fromWCharArray(
147 reinterpret_cast<const wchar_t *>(data.constData()) + i);
148 i += str.length() + 1;
149 if (str.isEmpty())
150 break;
151 list.append(str);
152 }
153 return list;
154 }
155 return QStringList();
156 }
157
158 case REG_NONE: // No specific type, treat as binary data.
159 case REG_BINARY: {
160 if (dataSize > 0) {
161 return QString::fromWCharArray(
162 reinterpret_cast<const wchar_t *>(data.constData()), data.size() / 2);
163 }
164 return QString();
165 }
166
167 case REG_DWORD: // Same as REG_DWORD_LITTLE_ENDIAN
168 return qFromLittleEndian<quint32>(data.constData());
169
170 case REG_DWORD_BIG_ENDIAN:
171 return qFromBigEndian<quint32>(data.constData());
172
173 case REG_QWORD: // Same as REG_QWORD_LITTLE_ENDIAN
174 return qFromLittleEndian<quint64>(data.constData());
175
176 default:
177 break;
178 }
179
180 return {};
181}
182
183QVariant QWinRegistryKey::value(const QString &subKey) const
184{
185 return value(nullTerminate(subKey));
186}
187
188// Returns the value of the specified subKey as a string, obtained using
189// qvariant_cast from the underlying QVariant. If that value is not a string,
190// or the subKey has no value, a string whose isNull() is true is returned.
191// Otherwise, the resulting string (which may be empty) is returned.
192QString QWinRegistryKey::stringValue(const wchar_t *subKey) const
193{
194 if (auto v = value<QString>(subKey))
195 return std::move(*v);
196 return QString();
197}
198
199QString QWinRegistryKey::stringValue(const QString &subKey) const
200{
201 return stringValue(nullTerminate(subKey));
202}
203
204void QWinRegistryKey::connectNotify(const QMetaMethod &signal)
205{
206 if (signal != QMetaMethod::fromSignal(&QWinRegistryKey::valueChanged))
207 return;
208
209 if (!isValid())
210 return;
211
212 if (m_keyChangedEvent)
213 return;
214
215 m_keyChangedEvent.reset(CreateEvent(nullptr, false, false, nullptr));
216 auto *notifier = new QWinEventNotifier(m_keyChangedEvent.get(), this);
217
218 auto registerForNotification = [this] {
219 constexpr auto changeFilter =
220 REG_NOTIFY_CHANGE_NAME
221 | REG_NOTIFY_CHANGE_ATTRIBUTES
222 | REG_NOTIFY_CHANGE_LAST_SET
223 | REG_NOTIFY_CHANGE_SECURITY;
224
225 if (auto status = RegNotifyChangeKeyValue(m_key, true, changeFilter,
226 m_keyChangedEvent.get(), true); status != ERROR_SUCCESS) {
227 qWarning() << "Failed to register notification for registry key"
228 << this << "due to" << QSystemError::windowsString(status);
229 }
230 };
231
232 QObject::connect(notifier, &QWinEventNotifier::activated, this,
233 [this, registerForNotification] {
234 emit valueChanged();
235 registerForNotification();
236 });
237
238 registerForNotification();
239
240 return QObject::connectNotify(signal);
241}
242
243#ifndef QT_NO_DEBUG_STREAM
244QDebug operator<<(QDebug debug, const QWinRegistryKey &key)
245{
246 QDebugStateSaver saver(debug);
247 debug.nospace();
248 debug << "QWinRegistryKey(";
249 if (key.isValid())
250 debug << key.name();
251 debug << ')';
252 return debug;
253}
254#endif
255
256QT_END_NAMESPACE
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
static const wchar_t * nullTerminate(const QString &s)