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