1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qsettings.h"
41
42 #include "qsettings_p.h"
43 #include "qvector.h"
44 #include "qmap.h"
45 #include "qdebug.h"
46 #include <qt_windows.h>
47
48 // See "Accessing an Alternate Registry View" at:
49 // http://msdn.microsoft.com/en-us/library/aa384129%28VS.85%29.aspx
50 #ifndef KEY_WOW64_64KEY
51 // Access a 32-bit key from either a 32-bit or 64-bit application.
52 # define KEY_WOW64_64KEY 0x0100
53 #endif
54
55 #ifndef KEY_WOW64_32KEY
56 // Access a 64-bit key from either a 32-bit or 64-bit application.
57 # define KEY_WOW64_32KEY 0x0200
58 #endif
59
60 QT_BEGIN_NAMESPACE
61
62 /* Keys are stored in QStrings. If the variable name starts with 'u', this is a "user"
63 key, ie. "foo/bar/alpha/beta". If the variable name starts with 'r', this is a "registry"
64 key, ie. "\foo\bar\alpha\beta". */
65
66 /*******************************************************************************
67 ** Some convenience functions
68 */
69
70 /*
71 We don't use KEY_ALL_ACCESS because it gives more rights than what we
72 need. See task 199061.
73 */
74 static const REGSAM registryPermissions = KEY_READ | KEY_WRITE;
75
keyPath(const QString & rKey)76 static QString keyPath(const QString &rKey)
77 {
78 int idx = rKey.lastIndexOf(QLatin1Char('\\'));
79 if (idx == -1)
80 return QString();
81 return rKey.left(idx + 1);
82 }
83
keyName(const QString & rKey)84 static QString keyName(const QString &rKey)
85 {
86 int idx = rKey.lastIndexOf(QLatin1Char('\\'));
87
88 QString res;
89 if (idx == -1)
90 res = rKey;
91 else
92 res = rKey.mid(idx + 1);
93
94 if (res == QLatin1String("Default") || res == QLatin1String("."))
95 res = QLatin1String("");
96
97 return res;
98 }
99
escapedKey(QString uKey)100 static QString escapedKey(QString uKey)
101 {
102 QChar *data = uKey.data();
103 int l = uKey.length();
104 for (int i = 0; i < l; ++i) {
105 ushort &ucs = data[i].unicode();
106 if (ucs == '\\')
107 ucs = '/';
108 else if (ucs == '/')
109 ucs = '\\';
110 }
111 return uKey;
112 }
113
unescapedKey(QString rKey)114 static QString unescapedKey(QString rKey)
115 {
116 return escapedKey(rKey);
117 }
118
119 typedef QMap<QString, QString> NameSet;
120
mergeKeySets(NameSet * dest,const NameSet & src)121 static void mergeKeySets(NameSet *dest, const NameSet &src)
122 {
123 NameSet::const_iterator it = src.constBegin();
124 for (; it != src.constEnd(); ++it)
125 dest->insert(unescapedKey(it.key()), QString());
126 }
127
mergeKeySets(NameSet * dest,const QStringList & src)128 static void mergeKeySets(NameSet *dest, const QStringList &src)
129 {
130 QStringList::const_iterator it = src.constBegin();
131 for (; it != src.constEnd(); ++it)
132 dest->insert(unescapedKey(*it), QString());
133 }
134
135 /*******************************************************************************
136 ** Wrappers for the insane windows registry API
137 */
138
139 // ### Qt 6: Use new helpers from qwinregistry.cpp (once bootstrap builds are obsolete)
140
141 // Open a key with the specified "perms".
142 // "access" is to explicitly use the 32- or 64-bit branch.
openKey(HKEY parentHandle,REGSAM perms,const QString & rSubKey,REGSAM access=0)143 static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access = 0)
144 {
145 HKEY resultHandle = 0;
146 LONG res = RegOpenKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()),
147 0, perms | access, &resultHandle);
148
149 if (res == ERROR_SUCCESS)
150 return resultHandle;
151
152 return 0;
153 }
154
155 // Open a key with the specified "perms", create it if it does not exist.
156 // "access" is to explicitly use the 32- or 64-bit branch.
createOrOpenKey(HKEY parentHandle,REGSAM perms,const QString & rSubKey,REGSAM access=0)157 static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey, REGSAM access = 0)
158 {
159 // try to open it
160 HKEY resultHandle = openKey(parentHandle, perms, rSubKey, access);
161 if (resultHandle != 0)
162 return resultHandle;
163
164 // try to create it
165 LONG res = RegCreateKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), 0, 0,
166 REG_OPTION_NON_VOLATILE, perms | access, 0, &resultHandle, 0);
167
168 if (res == ERROR_SUCCESS)
169 return resultHandle;
170
171 //qErrnoWarning(int(res), "QSettings: Failed to create subkey \"%ls\"",
172 // qUtf16Printable(rSubKey));
173
174 return 0;
175 }
176
177 // Open or create a key in read-write mode if possible, otherwise read-only.
178 // "access" is to explicitly use the 32- or 64-bit branch.
createOrOpenKey(HKEY parentHandle,const QString & rSubKey,bool * readOnly,REGSAM access=0)179 static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *readOnly, REGSAM access = 0)
180 {
181 // try to open or create it read/write
182 HKEY resultHandle = createOrOpenKey(parentHandle, registryPermissions, rSubKey, access);
183 if (resultHandle != 0) {
184 if (readOnly != 0)
185 *readOnly = false;
186 return resultHandle;
187 }
188
189 // try to open or create it read/only
190 resultHandle = createOrOpenKey(parentHandle, KEY_READ, rSubKey, access);
191 if (resultHandle != 0) {
192 if (readOnly != 0)
193 *readOnly = true;
194 return resultHandle;
195 }
196 return 0;
197 }
198
childKeysOrGroups(HKEY parentHandle,QSettingsPrivate::ChildSpec spec)199 static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildSpec spec)
200 {
201 QStringList result;
202 DWORD numKeys;
203 DWORD maxKeySize;
204 DWORD numSubgroups;
205 DWORD maxSubgroupSize;
206
207 // Find the number of keys and subgroups, as well as the max of their lengths.
208 LONG res = RegQueryInfoKey(parentHandle, 0, 0, 0, &numSubgroups, &maxSubgroupSize, 0,
209 &numKeys, &maxKeySize, 0, 0, 0);
210
211 if (res != ERROR_SUCCESS) {
212 qErrnoWarning(int(res), "QSettings: RegQueryInfoKey() failed");
213 return result;
214 }
215
216 ++maxSubgroupSize;
217 ++maxKeySize;
218
219 int n;
220 int m;
221 if (spec == QSettingsPrivate::ChildKeys) {
222 n = numKeys;
223 m = maxKeySize;
224 } else {
225 n = numSubgroups;
226 m = maxSubgroupSize;
227 }
228
229 /* The size does not include the terminating null character. */
230 ++m;
231
232 // Get the list
233 QByteArray buff(m * sizeof(wchar_t), 0);
234 for (int i = 0; i < n; ++i) {
235 QString item;
236 DWORD l = buff.size() / sizeof(wchar_t);
237 if (spec == QSettingsPrivate::ChildKeys) {
238 res = RegEnumValue(parentHandle, i, reinterpret_cast<wchar_t *>(buff.data()), &l, 0, 0, 0, 0);
239 } else {
240 res = RegEnumKeyEx(parentHandle, i, reinterpret_cast<wchar_t *>(buff.data()), &l, 0, 0, 0, 0);
241 }
242 if (res == ERROR_SUCCESS)
243 item = QString::fromWCharArray((const wchar_t *)buff.constData(), l);
244
245 if (res != ERROR_SUCCESS) {
246 qErrnoWarning(int(res), "QSettings: RegEnumValue failed");
247 continue;
248 }
249 if (item.isEmpty())
250 item = QLatin1String(".");
251 result.append(item);
252 }
253 return result;
254 }
255
allKeys(HKEY parentHandle,const QString & rSubKey,NameSet * result,REGSAM access=0)256 static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result, REGSAM access = 0)
257 {
258 HKEY handle = openKey(parentHandle, KEY_READ, rSubKey, access);
259 if (handle == 0)
260 return;
261
262 QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys);
263 QStringList childGroups = childKeysOrGroups(handle, QSettingsPrivate::ChildGroups);
264 RegCloseKey(handle);
265
266 for (int i = 0; i < childKeys.size(); ++i) {
267 QString s = rSubKey;
268 if (!s.isEmpty())
269 s += QLatin1Char('\\');
270 s += childKeys.at(i);
271 result->insert(s, QString());
272 }
273
274 for (int i = 0; i < childGroups.size(); ++i) {
275 QString s = rSubKey;
276 if (!s.isEmpty())
277 s += QLatin1Char('\\');
278 s += childGroups.at(i);
279 allKeys(parentHandle, s, result, access);
280 }
281 }
282
deleteChildGroups(HKEY parentHandle,REGSAM access=0)283 static void deleteChildGroups(HKEY parentHandle, REGSAM access = 0)
284 {
285 QStringList childGroups = childKeysOrGroups(parentHandle, QSettingsPrivate::ChildGroups);
286
287 for (int i = 0; i < childGroups.size(); ++i) {
288 QString group = childGroups.at(i);
289
290 // delete subgroups in group
291 HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group, access);
292 if (childGroupHandle == 0)
293 continue;
294 deleteChildGroups(childGroupHandle, access);
295 RegCloseKey(childGroupHandle);
296
297 // delete group itself
298 LONG res = RegDeleteKey(parentHandle, reinterpret_cast<const wchar_t *>(group.utf16()));
299 if (res != ERROR_SUCCESS) {
300 qErrnoWarning(int(res), "QSettings: RegDeleteKey failed on subkey \"%ls\"",
301 qUtf16Printable(group));
302 return;
303 }
304 }
305 }
306
307 /*******************************************************************************
308 ** class RegistryKey
309 */
310
311 class RegistryKey
312 {
313 public:
314 RegistryKey(HKEY parent_handle = 0, const QString &key = QString(), bool read_only = true, REGSAM access = 0);
315 QString key() const;
316 HKEY handle() const;
317 HKEY parentHandle() const;
318 bool readOnly() const;
319 void close();
320 private:
321 HKEY m_parent_handle;
322 mutable HKEY m_handle;
323 QString m_key;
324 mutable bool m_read_only;
325 REGSAM m_access;
326 };
327
RegistryKey(HKEY parent_handle,const QString & key,bool read_only,REGSAM access)328 RegistryKey::RegistryKey(HKEY parent_handle, const QString &key, bool read_only, REGSAM access)
329 : m_parent_handle(parent_handle),
330 m_handle(0),
331 m_key(key),
332 m_read_only(read_only),
333 m_access(access)
334 {
335 }
336
key() const337 QString RegistryKey::key() const
338 {
339 return m_key;
340 }
341
handle() const342 HKEY RegistryKey::handle() const
343 {
344 if (m_handle != 0)
345 return m_handle;
346
347 if (m_read_only)
348 m_handle = openKey(m_parent_handle, KEY_READ, m_key, m_access);
349 else
350 m_handle = createOrOpenKey(m_parent_handle, m_key, &m_read_only, m_access);
351
352 return m_handle;
353 }
354
parentHandle() const355 HKEY RegistryKey::parentHandle() const
356 {
357 return m_parent_handle;
358 }
359
readOnly() const360 bool RegistryKey::readOnly() const
361 {
362 return m_read_only;
363 }
364
close()365 void RegistryKey::close()
366 {
367 if (m_handle != 0)
368 RegCloseKey(m_handle);
369 m_handle = 0;
370 }
371
372 typedef QVector<RegistryKey> RegistryKeyList;
373
374 /*******************************************************************************
375 ** class QWinSettingsPrivate
376 */
377
378 class QWinSettingsPrivate : public QSettingsPrivate
379 {
380 Q_DISABLE_COPY(QWinSettingsPrivate)
381 public:
382 QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
383 const QString &application, REGSAM access = 0);
384 QWinSettingsPrivate(QString rKey, REGSAM access = 0);
385 ~QWinSettingsPrivate() override;
386
387 void remove(const QString &uKey) override;
388 void set(const QString &uKey, const QVariant &value) override;
389 bool get(const QString &uKey, QVariant *value) const override;
390 QStringList children(const QString &uKey, ChildSpec spec) const override;
391 void clear() override;
392 void sync() override;
393 void flush() override;
394 bool isWritable() const override;
395 HKEY writeHandle() const;
396 bool readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const;
397 QString fileName() const override;
398
399 private:
400 RegistryKeyList regList; // list of registry locations to search for keys
401 bool deleteWriteHandleOnExit;
402 REGSAM access;
403 };
404
QWinSettingsPrivate(QSettings::Scope scope,const QString & organization,const QString & application,REGSAM access)405 QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
406 const QString &application, REGSAM access)
407 : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application),
408 access(access)
409 {
410 deleteWriteHandleOnExit = false;
411
412 if (!organization.isEmpty()) {
413 QString prefix = QLatin1String("Software\\") + organization;
414 QString orgPrefix = prefix + QLatin1String("\\OrganizationDefaults");
415 QString appPrefix = prefix + QLatin1Char('\\') + application;
416
417 if (scope == QSettings::UserScope) {
418 if (!application.isEmpty())
419 regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty(), access));
420
421 regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty(), access));
422 }
423
424 if (!application.isEmpty())
425 regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty(), access));
426
427 regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty(), access));
428 }
429
430 if (regList.isEmpty())
431 setStatus(QSettings::AccessError);
432 }
433
QWinSettingsPrivate(QString rPath,REGSAM access)434 QWinSettingsPrivate::QWinSettingsPrivate(QString rPath, REGSAM access)
435 : QSettingsPrivate(QSettings::NativeFormat),
436 access(access)
437 {
438 deleteWriteHandleOnExit = false;
439
440 if (rPath.startsWith(QLatin1Char('\\')))
441 rPath.remove(0, 1);
442
443 int keyLength;
444 HKEY keyName;
445
446 if (rPath.startsWith(QLatin1String("HKEY_CURRENT_USER"))) {
447 keyLength = 17;
448 keyName = HKEY_CURRENT_USER;
449 } else if (rPath.startsWith(QLatin1String("HKCU"))) {
450 keyLength = 4;
451 keyName = HKEY_CURRENT_USER;
452 } else if (rPath.startsWith(QLatin1String("HKEY_LOCAL_MACHINE"))) {
453 keyLength = 18;
454 keyName = HKEY_LOCAL_MACHINE;
455 } else if (rPath.startsWith(QLatin1String("HKLM"))) {
456 keyLength = 4;
457 keyName = HKEY_LOCAL_MACHINE;
458 } else if (rPath.startsWith(QLatin1String("HKEY_CLASSES_ROOT"))) {
459 keyLength = 17;
460 keyName = HKEY_CLASSES_ROOT;
461 } else if (rPath.startsWith(QLatin1String("HKCR"))) {
462 keyLength = 4;
463 keyName = HKEY_CLASSES_ROOT;
464 } else if (rPath.startsWith(QLatin1String("HKEY_USERS"))) {
465 keyLength = 10;
466 keyName = HKEY_USERS;
467 } else if (rPath.startsWith(QLatin1String("HKU"))) {
468 keyLength = 3;
469 keyName = HKEY_USERS;
470 } else {
471 return;
472 }
473
474 if (rPath.length() == keyLength)
475 regList.append(RegistryKey(keyName, QString(), false, access));
476 else if (rPath[keyLength] == QLatin1Char('\\'))
477 regList.append(RegistryKey(keyName, rPath.mid(keyLength+1), false, access));
478 }
479
readKey(HKEY parentHandle,const QString & rSubKey,QVariant * value) const480 bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const
481 {
482 QString rSubkeyName = keyName(rSubKey);
483 QString rSubkeyPath = keyPath(rSubKey);
484
485 // open a handle on the subkey
486 HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath, access);
487 if (handle == 0)
488 return false;
489
490 // get the size and type of the value
491 DWORD dataType;
492 DWORD dataSize;
493 LONG res = RegQueryValueEx(handle, reinterpret_cast<const wchar_t *>(rSubkeyName.utf16()), 0, &dataType, 0, &dataSize);
494 if (res != ERROR_SUCCESS) {
495 RegCloseKey(handle);
496 return false;
497 }
498
499 // workaround for rare cases where trailing '\0' are missing in registry
500 if (dataType == REG_SZ || dataType == REG_EXPAND_SZ)
501 dataSize += 2;
502 else if (dataType == REG_MULTI_SZ)
503 dataSize += 4;
504
505 // get the value
506 QByteArray data(dataSize, 0);
507 res = RegQueryValueEx(handle, reinterpret_cast<const wchar_t *>(rSubkeyName.utf16()), 0, 0,
508 reinterpret_cast<unsigned char*>(data.data()), &dataSize);
509 if (res != ERROR_SUCCESS) {
510 RegCloseKey(handle);
511 return false;
512 }
513
514 switch (dataType) {
515 case REG_EXPAND_SZ:
516 case REG_SZ: {
517 QString s;
518 if (dataSize) {
519 s = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData()));
520 }
521 if (value != 0)
522 *value = stringToVariant(s);
523 break;
524 }
525
526 case REG_MULTI_SZ: {
527 QStringList l;
528 if (dataSize) {
529 int i = 0;
530 for (;;) {
531 QString s = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData()) + i);
532 i += s.length() + 1;
533
534 if (s.isEmpty())
535 break;
536 l.append(s);
537 }
538 }
539 if (value != 0)
540 *value = stringListToVariantList(l);
541 break;
542 }
543
544 case REG_NONE:
545 case REG_BINARY: {
546 QString s;
547 if (dataSize) {
548 s = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.constData()), data.size() / 2);
549 }
550 if (value != 0)
551 *value = stringToVariant(s);
552 break;
553 }
554
555 case REG_DWORD_BIG_ENDIAN:
556 case REG_DWORD: {
557 Q_ASSERT(data.size() == sizeof(int));
558 int i;
559 memcpy(reinterpret_cast<char*>(&i), data.constData(), sizeof(int));
560 if (value != 0)
561 *value = i;
562 break;
563 }
564
565 case REG_QWORD: {
566 Q_ASSERT(data.size() == sizeof(qint64));
567 qint64 i;
568 memcpy(reinterpret_cast<char*>(&i), data.constData(), sizeof(qint64));
569 if (value != 0)
570 *value = i;
571 break;
572 }
573
574 default:
575 qWarning("QSettings: Unknown data %d type in Windows registry", static_cast<int>(dataType));
576 if (value != 0)
577 *value = QVariant();
578 break;
579 }
580
581 RegCloseKey(handle);
582 return true;
583 }
584
writeHandle() const585 HKEY QWinSettingsPrivate::writeHandle() const
586 {
587 if (regList.isEmpty())
588 return 0;
589 const RegistryKey &key = regList.at(0);
590 if (key.handle() == 0 || key.readOnly())
591 return 0;
592 return key.handle();
593 }
594
~QWinSettingsPrivate()595 QWinSettingsPrivate::~QWinSettingsPrivate()
596 {
597 if (deleteWriteHandleOnExit && writeHandle() != 0) {
598 QString emptyKey;
599 DWORD res = RegDeleteKey(writeHandle(), reinterpret_cast<const wchar_t *>(emptyKey.utf16()));
600 if (res != ERROR_SUCCESS) {
601 qErrnoWarning(int(res), "QSettings: Failed to delete key \"%ls\"",
602 qUtf16Printable(regList.constFirst().key()));
603 }
604 }
605
606 for (int i = 0; i < regList.size(); ++i)
607 regList[i].close();
608 }
609
remove(const QString & uKey)610 void QWinSettingsPrivate::remove(const QString &uKey)
611 {
612 if (writeHandle() == 0) {
613 setStatus(QSettings::AccessError);
614 return;
615 }
616
617 QString rKey = escapedKey(uKey);
618
619 // try to delete value bar in key foo
620 LONG res;
621 HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey), access);
622 if (handle != 0) {
623 res = RegDeleteValue(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16()));
624 RegCloseKey(handle);
625 }
626
627 // try to delete key foo/bar and all subkeys
628 handle = openKey(writeHandle(), registryPermissions, rKey, access);
629 if (handle != 0) {
630 deleteChildGroups(handle, access);
631
632 if (rKey.isEmpty()) {
633 const QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys);
634
635 for (const QString &group : childKeys) {
636 LONG res = RegDeleteValue(handle, reinterpret_cast<const wchar_t *>(group.utf16()));
637 if (res != ERROR_SUCCESS) {
638 qErrnoWarning(int(res), "QSettings: RegDeleteValue failed on subkey \"%ls\"",
639 qUtf16Printable(group));
640 }
641 }
642 } else {
643 res = RegDeleteKey(writeHandle(), reinterpret_cast<const wchar_t *>(rKey.utf16()));
644
645 if (res != ERROR_SUCCESS) {
646 qErrnoWarning(int(res), "QSettings: RegDeleteKey failed on key \"%ls\"",
647 qUtf16Printable(rKey));
648 }
649 }
650 RegCloseKey(handle);
651 }
652 }
653
set(const QString & uKey,const QVariant & value)654 void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value)
655 {
656 if (writeHandle() == 0) {
657 setStatus(QSettings::AccessError);
658 return;
659 }
660
661 QString rKey = escapedKey(uKey);
662
663 HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey), access);
664 if (handle == 0) {
665 setStatus(QSettings::AccessError);
666 return;
667 }
668
669 DWORD type;
670 QByteArray regValueBuff;
671
672 // Determine the type
673 switch (value.type()) {
674 case QVariant::List:
675 case QVariant::StringList: {
676 // If none of the elements contains '\0', we can use REG_MULTI_SZ, the
677 // native registry string list type. Otherwise we use REG_BINARY.
678 type = REG_MULTI_SZ;
679 QStringList l = variantListToStringList(value.toList());
680 QStringList::const_iterator it = l.constBegin();
681 for (; it != l.constEnd(); ++it) {
682 if ((*it).length() == 0 || it->contains(QChar::Null)) {
683 type = REG_BINARY;
684 break;
685 }
686 }
687
688 if (type == REG_BINARY) {
689 QString s = variantToString(value);
690 regValueBuff = QByteArray(reinterpret_cast<const char*>(s.utf16()), s.length() * 2);
691 } else {
692 QStringList::const_iterator it = l.constBegin();
693 for (; it != l.constEnd(); ++it) {
694 const QString &s = *it;
695 regValueBuff += QByteArray(reinterpret_cast<const char*>(s.utf16()), (s.length() + 1) * 2);
696 }
697 regValueBuff.append((char)0);
698 regValueBuff.append((char)0);
699 }
700 break;
701 }
702
703 case QVariant::Int:
704 case QVariant::UInt: {
705 type = REG_DWORD;
706 qint32 i = value.toInt();
707 regValueBuff = QByteArray(reinterpret_cast<const char*>(&i), sizeof(qint32));
708 break;
709 }
710
711 case QVariant::LongLong:
712 case QVariant::ULongLong: {
713 type = REG_QWORD;
714 qint64 i = value.toLongLong();
715 regValueBuff = QByteArray(reinterpret_cast<const char*>(&i), sizeof(qint64));
716 break;
717 }
718
719 case QVariant::ByteArray:
720 Q_FALLTHROUGH();
721
722 default: {
723 // If the string does not contain '\0', we can use REG_SZ, the native registry
724 // string type. Otherwise we use REG_BINARY.
725 QString s = variantToString(value);
726 type = s.contains(QChar::Null) ? REG_BINARY : REG_SZ;
727 int length = s.length();
728 if (type == REG_SZ)
729 ++length;
730 regValueBuff = QByteArray(reinterpret_cast<const char *>(s.utf16()),
731 int(sizeof(wchar_t)) * length);
732 break;
733 }
734 }
735
736 // set the value
737 LONG res = RegSetValueEx(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16()), 0, type,
738 reinterpret_cast<const unsigned char*>(regValueBuff.constData()),
739 regValueBuff.size());
740
741 if (res == ERROR_SUCCESS) {
742 deleteWriteHandleOnExit = false;
743 } else {
744 qErrnoWarning(int(res), "QSettings: failed to set subkey \"%ls\"",
745 qUtf16Printable(rKey));
746 setStatus(QSettings::AccessError);
747 }
748
749 RegCloseKey(handle);
750 }
751
get(const QString & uKey,QVariant * value) const752 bool QWinSettingsPrivate::get(const QString &uKey, QVariant *value) const
753 {
754 QString rKey = escapedKey(uKey);
755
756 for (const RegistryKey &r : regList) {
757 HKEY handle = r.handle();
758 if (handle != 0 && readKey(handle, rKey, value))
759 return true;
760
761 if (!fallbacks)
762 return false;
763 }
764
765 return false;
766 }
767
children(const QString & uKey,ChildSpec spec) const768 QStringList QWinSettingsPrivate::children(const QString &uKey, ChildSpec spec) const
769 {
770 NameSet result;
771 QString rKey = escapedKey(uKey);
772
773 for (const RegistryKey &r : regList) {
774 HKEY parent_handle = r.handle();
775 if (parent_handle == 0)
776 continue;
777 HKEY handle = openKey(parent_handle, KEY_READ, rKey, access);
778 if (handle == 0)
779 continue;
780
781 if (spec == AllKeys) {
782 NameSet keys;
783 allKeys(handle, QLatin1String(""), &keys, access);
784 mergeKeySets(&result, keys);
785 } else { // ChildGroups or ChildKeys
786 QStringList names = childKeysOrGroups(handle, spec);
787 mergeKeySets(&result, names);
788 }
789
790 RegCloseKey(handle);
791
792 if (!fallbacks)
793 return result.keys();
794 }
795
796 return result.keys();
797 }
798
clear()799 void QWinSettingsPrivate::clear()
800 {
801 remove(QString());
802 deleteWriteHandleOnExit = true;
803 }
804
sync()805 void QWinSettingsPrivate::sync()
806 {
807 RegFlushKey(writeHandle());
808 }
809
flush()810 void QWinSettingsPrivate::flush()
811 {
812 // Windows does this for us.
813 }
814
fileName() const815 QString QWinSettingsPrivate::fileName() const
816 {
817 if (regList.isEmpty())
818 return QString();
819
820 const RegistryKey &key = regList.at(0);
821 QString result;
822 if (key.parentHandle() == HKEY_CURRENT_USER)
823 result = QLatin1String("\\HKEY_CURRENT_USER\\");
824 else
825 result = QLatin1String("\\HKEY_LOCAL_MACHINE\\");
826
827 return result + regList.at(0).key();
828 }
829
isWritable() const830 bool QWinSettingsPrivate::isWritable() const
831 {
832 return writeHandle() != 0;
833 }
834
create(QSettings::Format format,QSettings::Scope scope,const QString & organization,const QString & application)835 QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
836 const QString &organization, const QString &application)
837 {
838 switch (format) {
839 case QSettings::NativeFormat:
840 return new QWinSettingsPrivate(scope, organization, application);
841 case QSettings::Registry32Format:
842 return new QWinSettingsPrivate(scope, organization, application, KEY_WOW64_32KEY);
843 case QSettings::Registry64Format:
844 return new QWinSettingsPrivate(scope, organization, application, KEY_WOW64_64KEY);
845 default:
846 break;
847 }
848 return new QConfFileSettingsPrivate(format, scope, organization, application);
849 }
850
create(const QString & fileName,QSettings::Format format)851 QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
852 {
853 switch (format) {
854 case QSettings::NativeFormat:
855 return new QWinSettingsPrivate(fileName);
856 case QSettings::Registry32Format:
857 return new QWinSettingsPrivate(fileName, KEY_WOW64_32KEY);
858 case QSettings::Registry64Format:
859 return new QWinSettingsPrivate(fileName, KEY_WOW64_64KEY);
860 default:
861 break;
862 }
863 return new QConfFileSettingsPrivate(fileName, format);
864 }
865
866 QT_END_NAMESPACE
867