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
32// Open a key with the specified permissions (KEY_READ/KEY_WRITE).
33// "access" is to explicitly use the 32- or 64-bit branch.
34QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, const wchar_t *subKey,
35 REGSAM permissions, REGSAM access)
36{
37 if (RegOpenKeyExW(parentHandle, subKey,
38 0, permissions | access, &m_key) != ERROR_SUCCESS) {
39 m_key = nullptr;
40 }
41}
42
43QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, const QString &subKey,
44 REGSAM permissions, REGSAM access)
45 : QWinRegistryKey(parentHandle, nullTerminate(subKey), permissions, access)
46{
47}
48
49QWinRegistryKey::~QWinRegistryKey()
50{
51 close();
52}
53
54void QWinRegistryKey::close()
55{
56 if (isValid()) {
57 RegCloseKey(m_key);
58 m_key = nullptr;
59 }
60}
61
62QString QWinRegistryKey::name() const
63{
64 if (!isValid())
65 return {};
66
67 // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_key_name_information
68 constexpr int kKeyNameInformation = 3;
69 struct KeyNameInformation { ULONG length = 0; WCHAR name[1] = {}; };
70
71 // Resolve name of key iteratively by first computing the needed size,
72 // and then querying the name, accounting for the possibility that the
73 // name length changes behind our back a few times.
74 DWORD keyInformationSize = 0;
75 for (int i = 0; i < 5; ++i) {
76 QByteArray buffer(keyInformationSize, 0u);
77 auto *keyNameInformation = reinterpret_cast<KeyNameInformation *>(buffer.data());
78 switch (NtQueryKey(m_key, kKeyNameInformation, keyNameInformation,
79 keyInformationSize, &keyInformationSize)) {
80 case STATUS_BUFFER_TOO_SMALL:
81 case STATUS_BUFFER_OVERFLOW:
82 continue;
83 case STATUS_SUCCESS: {
84 return QString::fromWCharArray(keyNameInformation->name,
85 keyNameInformation->length / sizeof(wchar_t));
86 }
87 default:
88 return {};
89 }
90 }
91
92 return {};
93}
94
95QVariant QWinRegistryKey::value(const wchar_t *subKey) const
96{
97 // NOTE: Empty value name is allowed in Windows registry, it means the default
98 // or unnamed value of a key, you can read/write/delete such value normally.
99
100 if (!isValid())
101 return {};
102
103 // Get the size and type of the value.
104 DWORD dataType = REG_NONE;
105 DWORD dataSize = 0;
106 LONG ret = RegQueryValueExW(m_key, subKey, nullptr, &dataType, nullptr, &dataSize);
107 if (ret != ERROR_SUCCESS)
108 return {};
109
110 // Workaround for rare cases where the trailing '\0' is missing.
111 if (dataType == REG_SZ || dataType == REG_EXPAND_SZ)
112 dataSize += 2;
113 else if (dataType == REG_MULTI_SZ)
114 dataSize += 4;
115
116 // Get the value.
117 QVarLengthArray<unsigned char> data(dataSize);
118 std::fill(data.data(), data.data() + dataSize, 0u);
119
120 ret = RegQueryValueExW(m_key, subKey, nullptr, nullptr, data.data(), &dataSize);
121 if (ret != ERROR_SUCCESS)
122 return {};
123
124 switch (dataType) {
125 case REG_SZ:
126 case REG_EXPAND_SZ: {
127 if (dataSize > 0) {
128 return QString::fromWCharArray(
129 reinterpret_cast<const wchar_t *>(data.constData()));
130 }
131 return QString();
132 }
133
134 case REG_MULTI_SZ: {
135 if (dataSize > 0) {
136 QStringList list = {};
137 int i = 0;
138 while (true) {
139 const QString str = QString::fromWCharArray(
140 reinterpret_cast<const wchar_t *>(data.constData()) + i);
141 i += str.length() + 1;
142 if (str.isEmpty())
143 break;
144 list.append(str);
145 }
146 return list;
147 }
148 return QStringList();
149 }
150
151 case REG_NONE: // No specific type, treat as binary data.
152 case REG_BINARY: {
153 if (dataSize > 0) {
154 return QString::fromWCharArray(
155 reinterpret_cast<const wchar_t *>(data.constData()), data.size() / 2);
156 }
157 return QString();
158 }
159
160 case REG_DWORD: // Same as REG_DWORD_LITTLE_ENDIAN
161 return qFromLittleEndian<quint32>(data.constData());
162
163 case REG_DWORD_BIG_ENDIAN:
164 return qFromBigEndian<quint32>(data.constData());
165
166 case REG_QWORD: // Same as REG_QWORD_LITTLE_ENDIAN
167 return qFromLittleEndian<quint64>(data.constData());
168
169 default:
170 break;
171 }
172
173 return {};
174}
175
176QVariant QWinRegistryKey::value(const QString &subKey) const
177{
178 return value(nullTerminate(subKey));
179}
180
181// Returns the value of the specified subKey as a string, obtained using
182// qvariant_cast from the underlying QVariant. If that value is not a string,
183// or the subKey has no value, a string whose isNull() is true is returned.
184// Otherwise, the resulting string (which may be empty) is returned.
185QString QWinRegistryKey::stringValue(const wchar_t *subKey) const
186{
187 if (auto v = value<QString>(subKey))
188 return std::move(*v);
189 return QString();
190}
191
192QString QWinRegistryKey::stringValue(const QString &subKey) const
193{
194 return stringValue(nullTerminate(subKey));
195}
196
197QWinRegistryNotifier::QWinRegistryNotifier(HKEY parentHandle, const wchar_t *subKey,
198 QObject *parent)
199 : QWinRegistryNotifier(QWinRegistryKey(parentHandle, subKey), parent)
200{
201}
202
203QWinRegistryNotifier::QWinRegistryNotifier(HKEY parentHandle, const QString &subKey,
204 QObject *parent)
205 : QWinRegistryNotifier(QWinRegistryKey(parentHandle, subKey), parent)
206{
207}
208
209QWinRegistryNotifier::QWinRegistryNotifier(QWinRegistryKey &&key, QObject *parent)
210 : QObject(parent), m_key(std::move(key))
211{
212 if (!m_key.isValid())
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.handle(), 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
241#ifndef QT_NO_DEBUG_STREAM
242QDebug operator<<(QDebug debug, const QWinRegistryKey &key)
243{
244 QDebugStateSaver saver(debug);
245 debug.nospace();
246 debug << "QWinRegistryKey(";
247 if (key.isValid())
248 debug << key.name();
249 debug << ')';
250 return debug;
251}
252#endif
253
254QT_END_NAMESPACE
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
static const wchar_t * nullTerminate(const QString &s)