diff options
Diffstat (limited to 'src/corelib/io/qsettings_win.cpp')
-rw-r--r-- | src/corelib/io/qsettings_win.cpp | 957 |
1 files changed, 957 insertions, 0 deletions
diff --git a/src/corelib/io/qsettings_win.cpp b/src/corelib/io/qsettings_win.cpp new file mode 100644 index 0000000..67d2eeb --- /dev/null +++ b/src/corelib/io/qsettings_win.cpp @@ -0,0 +1,957 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsettings.h" + +#ifndef QT_NO_SETTINGS + +#include "qsettings_p.h" +#include "qvector.h" +#include "qmap.h" +#include "qt_windows.h" +#include "qdebug.h" + +QT_BEGIN_NAMESPACE + +/* Keys are stored in QStrings. If the variable name starts with 'u', this is a "user" + key, ie. "foo/bar/alpha/beta". If the variable name starts with 'r', this is a "registry" + key, ie. "\foo\bar\alpha\beta". */ + +/******************************************************************************* +** Some convenience functions +*/ + +/* + We don't use KEY_ALL_ACCESS because it gives more rights than what we + need. See task 199061. + */ +static const REGSAM registryPermissions = KEY_READ | KEY_WRITE; + +static QString keyPath(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + if (idx == -1) + return QString(); + return rKey.left(idx + 1); +} + +static QString keyName(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + + QString res; + if (idx == -1) + res = rKey; + else + res = rKey.mid(idx + 1); + + if (res == QLatin1String("Default") || res == QLatin1String(".")) + res = QLatin1String(""); + + return res; +} + +static QString escapedKey(QString uKey) +{ + QChar *data = uKey.data(); + int l = uKey.length(); + for (int i = 0; i < l; ++i) { + ushort &ucs = data[i].unicode(); + if (ucs == '\\') + ucs = '/'; + else if (ucs == '/') + ucs = '\\'; + } + return uKey; +} + +static QString unescapedKey(QString rKey) +{ + return escapedKey(rKey); +} + +typedef QMap<QString, QString> NameSet; + +static void mergeKeySets(NameSet *dest, const NameSet &src) +{ + NameSet::const_iterator it = src.constBegin(); + for (; it != src.constEnd(); ++it) + dest->insert(unescapedKey(it.key()), QString()); +} + +static void mergeKeySets(NameSet *dest, const QStringList &src) +{ + QStringList::const_iterator it = src.constBegin(); + for (; it != src.constEnd(); ++it) + dest->insert(unescapedKey(*it), QString()); +} + +/******************************************************************************* +** Wrappers for the insane windows registry API +*/ + +static QString errorCodeToString(DWORD errorCode) +{ + QString result; + QT_WA({ + wchar_t *data = 0; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + 0, errorCode, 0, + data, 0, 0); + result = QString::fromUtf16(reinterpret_cast<const ushort *> (data)); + if (data != 0) + LocalFree(data); + }, { + char *data = 0; + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + 0, errorCode, 0, + (char *)&data, 0, 0); + result = QString::fromLocal8Bit(data); + if (data != 0) + LocalFree(data); + }) + if (result.endsWith(QLatin1String("\n"))) + result.truncate(result.length() - 1); + + return result; +} + +// Open a key with the specified perms +static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey) +{ + HKEY resultHandle = 0; + + LONG res; + QT_WA( { + res = RegOpenKeyExW(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), + 0, perms, &resultHandle); + } , { + res = RegOpenKeyExA(parentHandle, rSubKey.toLocal8Bit(), + 0, perms, &resultHandle); + } ); + + if (res == ERROR_SUCCESS) + return resultHandle; + + return 0; +} + +// Open a key with the specified perms, create it if it does not exist +static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey) +{ + // try to open it + HKEY resultHandle = openKey(parentHandle, perms, rSubKey); + if (resultHandle != 0) + return resultHandle; + + // try to create it + LONG res; + QT_WA( { + res = RegCreateKeyExW(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), 0, 0, + REG_OPTION_NON_VOLATILE, perms, 0, &resultHandle, 0); + } , { + res = RegCreateKeyExA(parentHandle, rSubKey.toLocal8Bit(), 0, 0, + REG_OPTION_NON_VOLATILE, perms, 0, &resultHandle, 0); + } ); + + if (res == ERROR_SUCCESS) + return resultHandle; + + //qWarning("QSettings: Failed to create subkey \"%s\": %s", + // rSubKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + + return 0; +} + +// Open or create a key in read-write mode if possible, otherwise read-only +static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *readOnly) +{ + // try to open or create it read/write + HKEY resultHandle = createOrOpenKey(parentHandle, registryPermissions, rSubKey); + if (resultHandle != 0) { + if (readOnly != 0) + *readOnly = false; + return resultHandle; + } + + // try to open or create it read/only + resultHandle = createOrOpenKey(parentHandle, KEY_READ, rSubKey); + if (resultHandle != 0) { + if (readOnly != 0) + *readOnly = true; + return resultHandle; + } + return 0; +} + +static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildSpec spec) +{ + QStringList result; + LONG res; + DWORD numKeys; + DWORD maxKeySize; + DWORD numSubgroups; + DWORD maxSubgroupSize; + + // Find the number of keys and subgroups, as well as the max of their lengths. + QT_WA( { + res = RegQueryInfoKeyW(parentHandle, 0, 0, 0, &numSubgroups, &maxSubgroupSize, 0, + &numKeys, &maxKeySize, 0, 0, 0); + }, { + res = RegQueryInfoKeyA(parentHandle, 0, 0, 0, &numSubgroups, &maxSubgroupSize, 0, + &numKeys, &maxKeySize, 0, 0, 0); + } ); + + if (res != ERROR_SUCCESS) { + qWarning("QSettings: RegQueryInfoKey() failed: %s", errorCodeToString(res).toLatin1().data()); + return result; + } + + ++maxSubgroupSize; + ++maxKeySize; + + int n; + int m; + if (spec == QSettingsPrivate::ChildKeys) { + n = numKeys; + m = maxKeySize; + } else { + n = numSubgroups; + m = maxSubgroupSize; + } + + /* Windows NT/2000/XP: The size does not include the terminating null character. + Windows Me/98/95: The size includes the terminating null character. */ + ++m; + + // Get the list + QByteArray buff(m*sizeof(ushort), 0); + for (int i = 0; i < n; ++i) { + QString item; + QT_WA( { + DWORD l = buff.size() / sizeof(ushort); + if (spec == QSettingsPrivate::ChildKeys) { + res = RegEnumValueW(parentHandle, i, + reinterpret_cast<wchar_t *>(buff.data()), + &l, 0, 0, 0, 0); + } else { + res = RegEnumKeyExW(parentHandle, i, + reinterpret_cast<wchar_t *>(buff.data()), + &l, 0, 0, 0, 0); + } + if (res == ERROR_SUCCESS) + item = QString::fromUtf16(reinterpret_cast<ushort*>(buff.data()), l); + }, { + DWORD l = buff.size(); + if (spec == QSettingsPrivate::ChildKeys) + res = RegEnumValueA(parentHandle, i, buff.data(), &l, 0, 0, 0, 0); + else + res = RegEnumKeyExA(parentHandle, i, buff.data(), &l, 0, 0, 0, 0); + if (res == ERROR_SUCCESS) + item = QString::fromLocal8Bit(buff.data(), l); + } ); + + if (res != ERROR_SUCCESS) { + qWarning("QSettings: RegEnumValue failed: %s", errorCodeToString(res).toLatin1().data()); + continue; + } + if (item.isEmpty()) + item = QLatin1String("."); + result.append(item); + } + return result; +} + +static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result) +{ + HKEY handle = openKey(parentHandle, KEY_READ, rSubKey); + if (handle == 0) + return; + + QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys); + QStringList childGroups = childKeysOrGroups(handle, QSettingsPrivate::ChildGroups); + RegCloseKey(handle); + + for (int i = 0; i < childKeys.size(); ++i) { + QString s = rSubKey; + if (!s.isEmpty()) + s += QLatin1Char('\\'); + s += childKeys.at(i); + result->insert(s, QString()); + } + + for (int i = 0; i < childGroups.size(); ++i) { + QString s = rSubKey; + if (!s.isEmpty()) + s += QLatin1Char('\\'); + s += childGroups.at(i); + allKeys(parentHandle, s, result); + } +} + +static void deleteChildGroups(HKEY parentHandle) +{ + QStringList childGroups = childKeysOrGroups(parentHandle, QSettingsPrivate::ChildGroups); + + for (int i = 0; i < childGroups.size(); ++i) { + QString group = childGroups.at(i); + + // delete subgroups in group + HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group); + if (childGroupHandle == 0) + continue; + deleteChildGroups(childGroupHandle); + RegCloseKey(childGroupHandle); + + // delete group itself + LONG res; + QT_WA( { + res = RegDeleteKeyW(parentHandle, reinterpret_cast<const wchar_t *>(group.utf16())); + }, { + res = RegDeleteKeyA(parentHandle, group.toLocal8Bit()); + } ); + if (res != ERROR_SUCCESS) { + qWarning("QSettings: RegDeleteKey failed on subkey \"%s\": %s", + group.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + return; + } + } +} + +/******************************************************************************* +** class RegistryKey +*/ + +class RegistryKey +{ +public: + RegistryKey(HKEY parent_handle = 0, const QString &key = QString(), bool read_only = true); + QString key() const; + HKEY handle() const; + HKEY parentHandle() const; + bool readOnly() const; + void close(); +private: + HKEY m_parent_handle; + mutable HKEY m_handle; + QString m_key; + mutable bool m_read_only; +}; + +RegistryKey::RegistryKey(HKEY parent_handle, const QString &key, bool read_only) +{ + m_parent_handle = parent_handle; + m_handle = 0; + m_read_only = read_only; + m_key = key; +} + +QString RegistryKey::key() const +{ + return m_key; +} + +HKEY RegistryKey::handle() const +{ + if (m_handle != 0) + return m_handle; + + if (m_read_only) + m_handle = openKey(m_parent_handle, KEY_READ, m_key); + else + m_handle = createOrOpenKey(m_parent_handle, m_key, &m_read_only); + + return m_handle; +} + +HKEY RegistryKey::parentHandle() const +{ + return m_parent_handle; +} + +bool RegistryKey::readOnly() const +{ + return m_read_only; +} + +void RegistryKey::close() +{ + if (m_handle != 0) + RegCloseKey(m_handle); + m_handle = 0; +} + +typedef QVector<RegistryKey> RegistryKeyList; + +/******************************************************************************* +** class QWinSettingsPrivate +*/ + +class QWinSettingsPrivate : public QSettingsPrivate +{ +public: + QWinSettingsPrivate(QSettings::Scope scope, const QString &organization, + const QString &application); + QWinSettingsPrivate(QString rKey); + ~QWinSettingsPrivate(); + + void remove(const QString &uKey); + void set(const QString &uKey, const QVariant &value); + bool get(const QString &uKey, QVariant *value) const; + QStringList children(const QString &uKey, ChildSpec spec) const; + void clear(); + void sync(); + void flush(); + bool isWritable() const; + HKEY writeHandle() const; + bool readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const; + QString fileName() const; + +private: + RegistryKeyList regList; // list of registry locations to search for keys + bool deleteWriteHandleOnExit; +}; + +QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &organization, + const QString &application) + : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application) +{ + deleteWriteHandleOnExit = false; + + if (!organization.isEmpty()) { + QString prefix = QLatin1String("Software\\") + organization; + QString orgPrefix = prefix + QLatin1String("\\OrganizationDefaults"); + QString appPrefix = prefix + QLatin1Char('\\') + application; + + if (scope == QSettings::UserScope) { + if (!application.isEmpty()) + regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty())); + + regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty())); + } + + if (!application.isEmpty()) + regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty())); + + regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty())); + } + + if (regList.isEmpty()) + setStatus(QSettings::AccessError); +} + +QWinSettingsPrivate::QWinSettingsPrivate(QString rPath) + : QSettingsPrivate(QSettings::NativeFormat) +{ + deleteWriteHandleOnExit = false; + + if (rPath.startsWith(QLatin1String("\\"))) + rPath = rPath.mid(1); + + if (rPath.startsWith(QLatin1String("HKEY_CURRENT_USER\\"))) + regList.append(RegistryKey(HKEY_CURRENT_USER, rPath.mid(18), false)); + else if (rPath == QLatin1String("HKEY_CURRENT_USER")) + regList.append(RegistryKey(HKEY_CURRENT_USER, QString(), false)); + else if (rPath.startsWith(QLatin1String("HKEY_LOCAL_MACHINE\\"))) + regList.append(RegistryKey(HKEY_LOCAL_MACHINE, rPath.mid(19), false)); + else if (rPath == QLatin1String("HKEY_LOCAL_MACHINE")) + regList.append(RegistryKey(HKEY_LOCAL_MACHINE, QString(), false)); + else if (rPath.startsWith(QLatin1String("HKEY_CLASSES_ROOT\\"))) + regList.append(RegistryKey(HKEY_CLASSES_ROOT, rPath.mid(18), false)); + else if (rPath == QLatin1String("HKEY_CLASSES_ROOT")) + regList.append(RegistryKey(HKEY_CLASSES_ROOT, QString(), false)); + else if (rPath.startsWith(QLatin1String("HKEY_USERS\\"))) + regList.append(RegistryKey(HKEY_USERS, rPath.mid(11), false)); + else if (rPath == QLatin1String(QLatin1String("HKEY_USERS"))) + regList.append(RegistryKey(HKEY_USERS, QString(), false)); + else + regList.append(RegistryKey(HKEY_LOCAL_MACHINE, rPath, false)); +} + +bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const +{ + QString rSubkeyName = keyName(rSubKey); + QString rSubkeyPath = keyPath(rSubKey); + + // open a handle on the subkey + HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath); + if (handle == 0) + return false; + + // get the size and type of the value + DWORD dataType; + DWORD dataSize; + LONG res; + QT_WA( { + res = RegQueryValueExW(handle, reinterpret_cast<const wchar_t *>(rSubkeyName.utf16()), 0, &dataType, 0, &dataSize); + }, { + res = RegQueryValueExA(handle, rSubkeyName.toLocal8Bit(), 0, &dataType, 0, &dataSize); + } ); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return false; + } + + // get the value + QByteArray data(dataSize, 0); + QT_WA( { + res = RegQueryValueExW(handle, reinterpret_cast<const wchar_t *>(rSubkeyName.utf16()), 0, 0, + reinterpret_cast<unsigned char*>(data.data()), &dataSize); + }, { + res = RegQueryValueExA(handle, rSubkeyName.toLocal8Bit(), 0, 0, + reinterpret_cast<unsigned char*>(data.data()), &dataSize); + } ); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return false; + } + + switch (dataType) { + case REG_EXPAND_SZ: + case REG_SZ: { + QString s; + QT_WA( { + s = QString::fromUtf16(((const ushort*)data.constData())); + }, { + s = QString::fromLocal8Bit(data.constData()); + } ); + + if (value != 0) + *value = stringToVariant(s); + break; + } + + case REG_MULTI_SZ: { + QStringList l; + int i = 0; + for (;;) { + QString s; + QT_WA( { + s = QString::fromUtf16((const ushort*)data.constData() + i); + }, { + s = QString::fromLocal8Bit(data.constData() + i); + } ); + i += s.length() + 1; + + if (s.isEmpty()) + break; + l.append(s); + } + if (value != 0) + *value = stringListToVariantList(l); + break; + } + + case REG_NONE: + case REG_BINARY: { + QString s; + QT_WA( { + s = QString::fromUtf16((const ushort*)data.constData(), data.size()/2); + }, { + s = QString::fromLocal8Bit(data.constData(), data.size()); + } ); + if (value != 0) + *value = stringToVariant(s); + break; + } + + case REG_DWORD_BIG_ENDIAN: + case REG_DWORD: { + Q_ASSERT(data.size() == sizeof(int)); + int i; + memcpy((char*)&i, data.constData(), sizeof(int)); + if (value != 0) + *value = i; + break; + } + + default: + qWarning("QSettings: Unknown data %d type in Windows registry", static_cast<int>(dataType)); + if (value != 0) + *value = QVariant(); + break; + } + + RegCloseKey(handle); + return true; +} + +HKEY QWinSettingsPrivate::writeHandle() const +{ + if (regList.isEmpty()) + return 0; + const RegistryKey &key = regList.at(0); + if (key.handle() == 0 || key.readOnly()) + return 0; + return key.handle(); +} + +QWinSettingsPrivate::~QWinSettingsPrivate() +{ + if (deleteWriteHandleOnExit && writeHandle() != 0) { +#if defined(Q_OS_WINCE) + remove(regList.at(0).key()); +#else + DWORD res; + QString emptyKey; + QT_WA( { + res = RegDeleteKeyW(writeHandle(), reinterpret_cast<const wchar_t *>(emptyKey.utf16())); + }, { + res = RegDeleteKeyA(writeHandle(), emptyKey.toLocal8Bit()); + } ); + if (res != ERROR_SUCCESS) { + qWarning("QSettings: Failed to delete key \"%s\": %s", + regList.at(0).key().toLatin1().data(), errorCodeToString(res).toLatin1().data()); + } +#endif + } + + for (int i = 0; i < regList.size(); ++i) + regList[i].close(); +} + +void QWinSettingsPrivate::remove(const QString &uKey) +{ + if (writeHandle() == 0) { + setStatus(QSettings::AccessError); + return; + } + + QString rKey = escapedKey(uKey); + + // try to delete value bar in key foo + LONG res; + HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey)); + if (handle != 0) { + QT_WA( { + res = RegDeleteValueW(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16())); + }, { + res = RegDeleteValueA(handle, keyName(rKey).toLocal8Bit()); + } ); + RegCloseKey(handle); + } + + // try to delete key foo/bar and all subkeys + handle = openKey(writeHandle(), registryPermissions, rKey); + if (handle != 0) { + deleteChildGroups(handle); + + if (rKey.isEmpty()) { + QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys); + + for (int i = 0; i < childKeys.size(); ++i) { + QString group = childKeys.at(i); + + LONG res; + QT_WA( { + res = RegDeleteValueW(handle, reinterpret_cast<const wchar_t *>(group.utf16())); + }, { + res = RegDeleteValueA(handle, group.toLocal8Bit()); + } ); + if (res != ERROR_SUCCESS) { + qWarning("QSettings: RegDeleteValue failed on subkey \"%s\": %s", + group.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + } + } + } else { +#if defined(Q_OS_WINCE) + // For WinCE always Close the handle first. + RegCloseKey(handle); +#endif + QT_WA( { + res = RegDeleteKeyW(writeHandle(), reinterpret_cast<const wchar_t *>(rKey.utf16())); + }, { + res = RegDeleteKeyA(writeHandle(), rKey.toLocal8Bit()); + } ); + + if (res != ERROR_SUCCESS) { + qWarning("QSettings: RegDeleteKey failed on key \"%s\": %s", + rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + } + } + RegCloseKey(handle); + } +} + +static bool stringContainsNullChar(const QString &s) +{ + for (int i = 0; i < s.length(); ++i) { + if (s.at(i).unicode() == 0) + return true; + } + return false; +} + +void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value) +{ + if (writeHandle() == 0) { + setStatus(QSettings::AccessError); + return; + } + + QString rKey = escapedKey(uKey); + + HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey)); + if (handle == 0) { + setStatus(QSettings::AccessError); + return; + } + + DWORD type; + QByteArray regValueBuff; + + // Determine the type + switch (value.type()) { + case QVariant::List: + case QVariant::StringList: { + // If none of the elements contains '\0', we can use REG_MULTI_SZ, the + // native registry string list type. Otherwise we use REG_BINARY. + type = REG_MULTI_SZ; + QStringList l = variantListToStringList(value.toList()); + QStringList::const_iterator it = l.constBegin(); + for (; it != l.constEnd(); ++it) { + if ((*it).length() == 0 || stringContainsNullChar(*it)) { + type = REG_BINARY; + break; + } + } + + if (type == REG_BINARY) { + QString s = variantToString(value); + QT_WA( { + regValueBuff = QByteArray((const char*)s.utf16(), s.length()*2); + }, { + regValueBuff = QByteArray((const char*)s.toLocal8Bit(), s.length()); + } ); + } else { + QStringList::const_iterator it = l.constBegin(); + for (; it != l.constEnd(); ++it) { + const QString &s = *it; + QT_WA( { + regValueBuff += QByteArray((const char*)s.utf16(), (s.length() + 1)*2); + }, { + regValueBuff += QByteArray((const char*)s.toLocal8Bit(), s.length() + 1); + } ); + } + QT_WA( { + regValueBuff.append((char)0); + regValueBuff.append((char)0); + }, { + regValueBuff.append((char)0); + } ); + } + break; + } + + case QVariant::Int: { + type = REG_DWORD; + int i = value.toInt(); + regValueBuff = QByteArray((const char*)&i, sizeof(int)); + break; + } + + case QVariant::ByteArray: + // On Win95/98/Me QString::toLocal8Bit() fails to handle chars > 0x7F. So we don't go through variantToString() at all. + if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) { + QByteArray ba = value.toByteArray(); + regValueBuff = "@ByteArray("; + regValueBuff += ba; + regValueBuff += ')'; + if (ba.contains('\0')) { + type = REG_BINARY; + } else { + type = REG_SZ; + regValueBuff += '\0'; + } + + break; + } + // fallthrough intended + + default: { + // If the string does not contain '\0', we can use REG_SZ, the native registry + // string type. Otherwise we use REG_BINARY. + QString s = variantToString(value); + type = stringContainsNullChar(s) ? REG_BINARY : REG_SZ; + if (type == REG_BINARY) { + QT_WA( { + regValueBuff = QByteArray((const char*)s.utf16(), s.length()*2); + }, { + regValueBuff = QByteArray((const char*)s.toLocal8Bit(), s.length()); + } ); + } else { + QT_WA( { + regValueBuff = QByteArray((const char*)s.utf16(), (s.length() + 1)*2); + }, { + regValueBuff = QByteArray((const char*)s.toLocal8Bit(), s.length() + 1); + } ); + } + break; + } + } + + // set the value + LONG res; + QT_WA( { + res = RegSetValueExW(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16()), 0, type, + reinterpret_cast<const unsigned char*>(regValueBuff.constData()), + regValueBuff.size()); + }, { + res = RegSetValueExA(handle, keyName(rKey).toLocal8Bit(), 0, type, + reinterpret_cast<const unsigned char*>(regValueBuff.constData()), + regValueBuff.size()); + } ); + + if (res == ERROR_SUCCESS) { + deleteWriteHandleOnExit = false; + } else { + qWarning("QSettings: failed to set subkey \"%s\": %s", + rKey.toLatin1().data(), errorCodeToString(res).toLatin1().data()); + setStatus(QSettings::AccessError); + } + + RegCloseKey(handle); +} + +bool QWinSettingsPrivate::get(const QString &uKey, QVariant *value) const +{ + QString rKey = escapedKey(uKey); + + for (int i = 0; i < regList.size(); ++i) { + HKEY handle = regList.at(i).handle(); + if (handle != 0 && readKey(handle, rKey, value)) + return true; + + if (!fallbacks) + return false; + } + + return false; +} + +QStringList QWinSettingsPrivate::children(const QString &uKey, ChildSpec spec) const +{ + NameSet result; + QString rKey = escapedKey(uKey); + + for (int i = 0; i < regList.size(); ++i) { + HKEY parent_handle = regList.at(i).handle(); + if (parent_handle == 0) + continue; + HKEY handle = openKey(parent_handle, KEY_READ, rKey); + if (handle == 0) + continue; + + if (spec == AllKeys) { + NameSet keys; + allKeys(handle, QLatin1String(""), &keys); + mergeKeySets(&result, keys); + } else { // ChildGroups or ChildKeys + QStringList names = childKeysOrGroups(handle, spec); + mergeKeySets(&result, names); + } + + RegCloseKey(handle); + + if (!fallbacks) + return result.keys(); + } + + return result.keys(); +} + +void QWinSettingsPrivate::clear() +{ + remove(QString()); + deleteWriteHandleOnExit = true; +} + +void QWinSettingsPrivate::sync() +{ + RegFlushKey(writeHandle()); +} + +void QWinSettingsPrivate::flush() +{ + // Windows does this for us. +} + +QString QWinSettingsPrivate::fileName() const +{ + if (regList.isEmpty()) + return QString(); + + const RegistryKey &key = regList.at(0); + QString result; + if (key.parentHandle() == HKEY_CURRENT_USER) + result = QLatin1String("\\HKEY_CURRENT_USER\\"); + else + result = QLatin1String("\\HKEY_LOCAL_MACHINE\\"); + + return result + regList.at(0).key(); +} + +bool QWinSettingsPrivate::isWritable() const +{ + return writeHandle() != 0; +} + +QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope, + const QString &organization, const QString &application) +{ + if (format == QSettings::NativeFormat) { + return new QWinSettingsPrivate(scope, organization, application); + } else { + return new QConfFileSettingsPrivate(format, scope, organization, application); + } +} + +QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format) +{ + if (format == QSettings::NativeFormat) { + return new QWinSettingsPrivate(fileName); + } else { + return new QConfFileSettingsPrivate(fileName, format); + } +} + +QT_END_NAMESPACE +#endif // QT_NO_SETTINGS |