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