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