123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826 | // Copyright (C) 2016 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qsettings.h"#include"qsettings_p.h"#include"qlist.h"#include"qmap.h"#include"qdebug.h"#include"qscopeguard.h"#include <QtCore/private/wcharhelpers_win_p.h>#include <qt_windows.h>// See "Accessing an Alternate Registry View" at:// http://msdn.microsoft.com/en-us/library/aa384129%28VS.85%29.aspx#ifndef KEY_WOW64_64KEY// Access a 32-bit key from either a 32-bit or 64-bit application.# define KEY_WOW64_64KEY 0x0100#endif#ifndef KEY_WOW64_32KEY// Access a 64-bit key from either a 32-bit or 64-bit application.# define KEY_WOW64_32KEY 0x0200#endif QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;/* 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(u'\\');if(idx == -1)returnQString();return rKey.left(idx +1);}static QString keyName(const QString &rKey){int idx = rKey.lastIndexOf(u'\\'); QString res;if(idx == -1) res = rKey;else res = rKey.mid(idx +1);if(res =="Default"_L1 || res =="."_L1) res =""_L1;return res;}static QString escapedKey(QString uKey){ QChar *data = uKey.data();int l = uKey.length();for(int i =0; i < l; ++i) {auto&ucs = data[i].unicode();if(ucs =='\\') ucs ='/';else if(ucs =='/') ucs ='\\';}return uKey;}static QString unescapedKey(QString rKey){returnescapedKey(rKey);}typedef QMap<QString, QString> NameSet;static voidmergeKeySets(NameSet *dest,const NameSet &src){NameSet::const_iterator it = src.constBegin();for(; it != src.constEnd(); ++it) dest->insert(unescapedKey(it.key()),QString());}static voidmergeKeySets(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*/// ### Qt 6: Use new helpers from qwinregistry.cpp (once bootstrap builds are obsolete)// Open a key with the specified "perms".// "access" is to explicitly use the 32- or 64-bit branch.static HKEY openKey(HKEY parentHandle, REGSAM perms,const QString &rSubKey, REGSAM access =0){ HKEY resultHandle =0; LONG res =RegOpenKeyEx(parentHandle,reinterpret_cast<const wchar_t*>(rSubKey.utf16()),0, perms | access, &resultHandle);if(res == ERROR_SUCCESS)return resultHandle;return0;}// Open a key with the specified "perms", create it if it does not exist.// "access" is to explicitly use the 32- or 64-bit branch.static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms,const QString &rSubKey, REGSAM access =0){// try to open it HKEY resultHandle =openKey(parentHandle, perms, rSubKey, access);if(resultHandle !=0)return resultHandle;// try to create it LONG res =RegCreateKeyEx(parentHandle,reinterpret_cast<const wchar_t*>(rSubKey.utf16()),0,0, REG_OPTION_NON_VOLATILE, perms | access,0, &resultHandle,0);if(res == ERROR_SUCCESS)return resultHandle;//qErrnoWarning(int(res), "QSettings: Failed to create subkey \"%ls\"",// qUtf16Printable(rSubKey));return0;}// Open or create a key in read-write mode if possible, otherwise read-only.// "access" is to explicitly use the 32- or 64-bit branch.static HKEY createOrOpenKey(HKEY parentHandle,const QString &rSubKey,bool*readOnly, REGSAM access =0){// try to open or create it read/write HKEY resultHandle =createOrOpenKey(parentHandle, registryPermissions, rSubKey, access);if(resultHandle !=0) {if(readOnly !=0)*readOnly =false;return resultHandle;}// try to open or create it read/only resultHandle =createOrOpenKey(parentHandle, KEY_READ, rSubKey, access);if(resultHandle !=0) {if(readOnly !=0)*readOnly =true;return resultHandle;}return0;}static QStringList childKeysOrGroups(HKEY parentHandle,QSettingsPrivate::ChildSpec spec){ QStringList result; DWORD numKeys; DWORD maxKeySize; DWORD numSubgroups; DWORD maxSubgroupSize;// Find the number of keys and subgroups, as well as the max of their lengths. LONG res =RegQueryInfoKey(parentHandle,0,0,0, &numSubgroups, &maxSubgroupSize,0,&numKeys, &maxKeySize,0,0,0);if(res != ERROR_SUCCESS) {qErrnoWarning(int(res),"QSettings: RegQueryInfoKey() failed");return result;}++maxSubgroupSize;++maxKeySize;int n;int m;if(spec ==QSettingsPrivate::ChildKeys) { n = numKeys; m = maxKeySize;}else{ n = numSubgroups; m = maxSubgroupSize;}/* The size does not include the terminating null character. */++m;// Get the list QByteArray buff(m *sizeof(wchar_t),0);for(int i =0; i < n; ++i) { QString item; DWORD l =DWORD(buff.size()) /DWORD(sizeof(wchar_t));if(spec ==QSettingsPrivate::ChildKeys) { res =RegEnumValue(parentHandle, i,reinterpret_cast<wchar_t*>(buff.data()), &l,0,0,0,0);}else{ res =RegEnumKeyEx(parentHandle, i,reinterpret_cast<wchar_t*>(buff.data()), &l,0,0,0,0);}if(res == ERROR_SUCCESS) item =QString::fromWCharArray((const wchar_t*)buff.constData(), l);if(res != ERROR_SUCCESS) {qErrnoWarning(int(res),"QSettings: RegEnumValue failed");continue;}if(item.isEmpty()) item ="."_L1; result.append(item);}return result;}static voidallKeys(HKEY parentHandle,const QString &rSubKey, NameSet *result, REGSAM access =0){ HKEY handle =openKey(parentHandle, KEY_READ, rSubKey, access);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 += u'\\'; s += childKeys.at(i); result->insert(s,QString());}for(int i =0; i < childGroups.size(); ++i) { QString s = rSubKey;if(!s.isEmpty()) s += u'\\'; s += childGroups.at(i);allKeys(parentHandle, s, result, access);}}static voiddeleteChildGroups(HKEY parentHandle, REGSAM access =0){ 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, access);if(childGroupHandle ==0)continue;deleteChildGroups(childGroupHandle, access);RegCloseKey(childGroupHandle);// delete group itself LONG res =RegDeleteKey(parentHandle,qt_castToWchar(group));if(res != ERROR_SUCCESS) {qErrnoWarning(int(res),"QSettings: RegDeleteKey failed on subkey\"%ls\"",qUtf16Printable(group));return;}}}/********************************************************************************* class RegistryKey*/class RegistryKey {public:RegistryKey(HKEY parent_handle =0,const QString &key =QString(),bool read_only =true, REGSAM access =0); QString key()const; HKEY handle()const; HKEY parentHandle()const;boolreadOnly()const;voidclose();private: HKEY m_parent_handle;mutable HKEY m_handle; QString m_key;mutablebool m_read_only; REGSAM m_access;};RegistryKey::RegistryKey(HKEY parent_handle,const QString &key,bool read_only, REGSAM access):m_parent_handle(parent_handle),m_handle(0),m_key(key),m_read_only(read_only),m_access(access){} 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, m_access);else m_handle =createOrOpenKey(m_parent_handle, m_key, &m_read_only, m_access);return m_handle;} HKEY RegistryKey::parentHandle()const{return m_parent_handle;}boolRegistryKey::readOnly()const{return m_read_only;}voidRegistryKey::close(){if(m_handle !=0)RegCloseKey(m_handle); m_handle =0;}typedef QList<RegistryKey> RegistryKeyList;/********************************************************************************* class QWinSettingsPrivate*/class QWinSettingsPrivate :public QSettingsPrivate {Q_DISABLE_COPY(QWinSettingsPrivate)public:QWinSettingsPrivate(QSettings::Scope scope,const QString &organization,const QString &application, REGSAM access =0);QWinSettingsPrivate(QString rKey, REGSAM access =0);~QWinSettingsPrivate() override;voidremove(const QString &uKey) override;voidset(const QString &uKey,const QVariant &value) override;std::optional<QVariant>get(const QString &uKey)const override; QStringList children(const QString &uKey, ChildSpec spec)const override;voidclear() override;voidsync() override;voidflush() override;boolisWritable()const override; HKEY writeHandle()const;std::optional<QVariant>readKey(HKEY parentHandle,const QString &rSubKey)const; QString fileName()const override;private: RegistryKeyList regList;// list of registry locations to search for keysbool deleteWriteHandleOnExit; REGSAM access;};QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope,const QString &organization,const QString &application, REGSAM access):QSettingsPrivate(QSettings::NativeFormat, scope, organization, application),access(access){ deleteWriteHandleOnExit =false;if(!organization.isEmpty()) { QString prefix ="Software\\"_L1 + organization; QString orgPrefix = prefix +"\\OrganizationDefaults"_L1; QString appPrefix = prefix + u'\\'+ application;if(scope ==QSettings::UserScope) {if(!application.isEmpty()) regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty(), access)); regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty(), access));}if(!application.isEmpty()) regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty(), access)); regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty(), access));}if(regList.isEmpty())setStatus(QSettings::AccessError);}QWinSettingsPrivate::QWinSettingsPrivate(QString rPath, REGSAM access):QSettingsPrivate(QSettings::NativeFormat),access(access){ deleteWriteHandleOnExit =false;if(rPath.startsWith(u'\\')) rPath.remove(0,1);int keyLength; HKEY keyName;if(rPath.startsWith("HKEY_CURRENT_USER"_L1)) { keyLength =17; keyName = HKEY_CURRENT_USER;}else if(rPath.startsWith("HKCU"_L1)) { keyLength =4; keyName = HKEY_CURRENT_USER;}else if(rPath.startsWith("HKEY_LOCAL_MACHINE"_L1)) { keyLength =18; keyName = HKEY_LOCAL_MACHINE;}else if(rPath.startsWith("HKLM"_L1)) { keyLength =4; keyName = HKEY_LOCAL_MACHINE;}else if(rPath.startsWith("HKEY_CLASSES_ROOT"_L1)) { keyLength =17; keyName = HKEY_CLASSES_ROOT;}else if(rPath.startsWith("HKCR"_L1)) { keyLength =4; keyName = HKEY_CLASSES_ROOT;}else if(rPath.startsWith("HKEY_USERS"_L1)) { keyLength =10; keyName = HKEY_USERS;}else if(rPath.startsWith("HKU"_L1)) { keyLength =3; keyName = HKEY_USERS;}else{return;}if(rPath.length() == keyLength) regList.append(RegistryKey(keyName,QString(),false, access));else if(rPath[keyLength] == u'\\') regList.append(RegistryKey(keyName, rPath.mid(keyLength+1),false, access));}std::optional<QVariant>QWinSettingsPrivate::readKey(HKEY parentHandle,const QString &rSubKey)const{ QString rSubkeyName =keyName(rSubKey); QString rSubkeyPath =keyPath(rSubKey);// open a handle on the subkey HKEY handle =openKey(parentHandle, KEY_READ, rSubkeyPath, access);if(handle ==0)returnstd::nullopt;constauto closeKey =qScopeGuard([handle] {RegCloseKey(handle); });// get the size and type of the value DWORD dataType; DWORD dataSize; LONG res =RegQueryValueEx(handle,reinterpret_cast<const wchar_t*>(rSubkeyName.utf16()),0, &dataType,0, &dataSize);if(res != ERROR_SUCCESS)returnstd::nullopt;// workaround for rare cases where trailing '\0' are missing in registryif(dataType == REG_SZ || dataType == REG_EXPAND_SZ) dataSize +=2;else if(dataType == REG_MULTI_SZ) dataSize +=4;// get the value QByteArray data(dataSize,0); res =RegQueryValueEx(handle,reinterpret_cast<const wchar_t*>(rSubkeyName.utf16()),0,0,reinterpret_cast<unsigned char*>(data.data()), &dataSize);if(res != ERROR_SUCCESS)returnstd::nullopt;switch(dataType) {case REG_EXPAND_SZ:case REG_SZ: { QString s;if(dataSize) { s =QString::fromWCharArray(reinterpret_cast<const wchar_t*>(data.constData()));}returnstringToVariant(s);}case REG_MULTI_SZ: { QStringList l;if(dataSize) {int i =0;for(;;) { QString s =QString::fromWCharArray(reinterpret_cast<const wchar_t*>(data.constData()) + i); i += s.length() +1;if(s.isEmpty())break; l.append(s);}}returnstringListToVariantList(l);}case REG_NONE:case REG_BINARY: { QString s;if(dataSize) { s =QString::fromWCharArray(reinterpret_cast<const wchar_t*>(data.constData()), data.size() /2);}returnstringToVariant(s);}case REG_DWORD_BIG_ENDIAN:case REG_DWORD: {Q_ASSERT(data.size() ==sizeof(int));int i;memcpy(reinterpret_cast<char*>(&i), data.constData(),sizeof(int));return i;}case REG_QWORD: {Q_ASSERT(data.size() ==sizeof(qint64)); qint64 i;memcpy(reinterpret_cast<char*>(&i), data.constData(),sizeof(qint64));return i;}default:qWarning("QSettings: Unknown data %d type in Windows registry",static_cast<int>(dataType));break;}returnstd::nullopt;} HKEY QWinSettingsPrivate::writeHandle()const{if(regList.isEmpty())return0;const RegistryKey &key = regList.at(0);if(key.handle() ==0|| key.readOnly())return0;return key.handle();}QWinSettingsPrivate::~QWinSettingsPrivate(){if(deleteWriteHandleOnExit &&writeHandle() !=0) { DWORD res =RegDeleteKey(writeHandle(), L"");if(res != ERROR_SUCCESS) {qErrnoWarning(int(res),"QSettings: Failed to delete key\"%ls\"",qUtf16Printable(regList.constFirst().key()));}}for(int i =0; i < regList.size(); ++i) regList[i].close();}voidQWinSettingsPrivate::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), access);if(handle !=0) { res =RegDeleteValue(handle,reinterpret_cast<const wchar_t*>(keyName(rKey).utf16()));RegCloseKey(handle);}// try to delete key foo/bar and all subkeys handle =openKey(writeHandle(), registryPermissions, rKey, access);if(handle !=0) {deleteChildGroups(handle, access);if(rKey.isEmpty()) {const QStringList childKeys =childKeysOrGroups(handle,QSettingsPrivate::ChildKeys);for(const QString &group : childKeys) { LONG res =RegDeleteValue(handle,qt_castToWchar(group));if(res != ERROR_SUCCESS) {qErrnoWarning(int(res),"QSettings: RegDeleteValue failed on subkey\"%ls\"",qUtf16Printable(group));}}}else{ res =RegDeleteKey(writeHandle(),reinterpret_cast<const wchar_t*>(rKey.utf16()));if(res != ERROR_SUCCESS) {qErrnoWarning(int(res),"QSettings: RegDeleteKey failed on key\"%ls\"",qUtf16Printable(rKey));}}RegCloseKey(handle);}}voidQWinSettingsPrivate::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), access);if(handle ==0) {setStatus(QSettings::AccessError);return;} DWORD type; QByteArray regValueBuff;// Determine the typeswitch(value.typeId()) {caseQMetaType::QVariantList:caseQMetaType::QStringList: {// 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|| it->contains(QChar::Null)) { type = REG_BINARY;break;}}if(type == REG_BINARY) {const QString s =variantToString(value); regValueBuff =QByteArray(reinterpret_cast<const char*>(s.data()), s.length() *2);}else{QStringList::const_iterator it = l.constBegin();for(; it != l.constEnd(); ++it) {const QString &s = *it; regValueBuff +=QByteArray(reinterpret_cast<const char*>(s.utf16()), (s.length() +1) *2);} regValueBuff.append((char)0); regValueBuff.append((char)0);}break;}caseQMetaType::Int:caseQMetaType::UInt: { type = REG_DWORD; qint32 i = value.toInt(); regValueBuff =QByteArray(reinterpret_cast<const char*>(&i),sizeof(qint32));break;}caseQMetaType::LongLong:caseQMetaType::ULongLong: { type = REG_QWORD; qint64 i = value.toLongLong(); regValueBuff =QByteArray(reinterpret_cast<const char*>(&i),sizeof(qint64));break;}caseQMetaType::QByteArray:Q_FALLTHROUGH();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 = s.contains(QChar::Null) ? REG_BINARY : REG_SZ;int length = s.length();if(type == REG_SZ)++length; regValueBuff =QByteArray(reinterpret_cast<const char*>(s.constData()),int(sizeof(wchar_t)) * length);break;}}// set the value LONG res =RegSetValueEx(handle,reinterpret_cast<const wchar_t*>(keyName(rKey).utf16()),0, type,reinterpret_cast<const unsigned char*>(regValueBuff.constData()), regValueBuff.size());if(res == ERROR_SUCCESS) { deleteWriteHandleOnExit =false;}else{qErrnoWarning(int(res),"QSettings: failed to set subkey\"%ls\"",qUtf16Printable(rKey));setStatus(QSettings::AccessError);}RegCloseKey(handle);}std::optional<QVariant>QWinSettingsPrivate::get(const QString &uKey)const{ QString rKey =escapedKey(uKey);for(const RegistryKey &r : regList) { HKEY handle = r.handle();if(handle !=0) {if(auto result =readKey(handle, rKey))return result;}if(!fallbacks)returnstd::nullopt;}returnstd::nullopt;} QStringList QWinSettingsPrivate::children(const QString &uKey, ChildSpec spec)const{ NameSet result; QString rKey =escapedKey(uKey);for(const RegistryKey &r : regList) { HKEY parent_handle = r.handle();if(parent_handle ==0) {if(fallbacks)continue;break;} HKEY handle =openKey(parent_handle, KEY_READ, rKey, access);if(handle ==0) {if(fallbacks)continue;break;}if(spec == AllKeys) { NameSet keys;allKeys(handle,""_L1, &keys, access);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();}voidQWinSettingsPrivate::clear(){remove(QString()); deleteWriteHandleOnExit =true;}voidQWinSettingsPrivate::sync(){RegFlushKey(writeHandle());}voidQWinSettingsPrivate::flush(){// Windows does this for us.} QString QWinSettingsPrivate::fileName()const{if(regList.isEmpty())returnQString();const RegistryKey &key = regList.at(0); QString result;if(key.parentHandle() == HKEY_CURRENT_USER) result ="\\HKEY_CURRENT_USER\\"_L1;else result ="\\HKEY_LOCAL_MACHINE\\"_L1;return result + regList.at(0).key();}boolQWinSettingsPrivate::isWritable()const{returnwriteHandle() !=0;} QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format,QSettings::Scope scope,const QString &organization,const QString &application){switch(format) {caseQSettings::NativeFormat:return newQWinSettingsPrivate(scope, organization, application);caseQSettings::Registry32Format:return newQWinSettingsPrivate(scope, organization, application, KEY_WOW64_32KEY);caseQSettings::Registry64Format:return newQWinSettingsPrivate(scope, organization, application, KEY_WOW64_64KEY);default:break;}return newQConfFileSettingsPrivate(format, scope, organization, application);} QSettingsPrivate *QSettingsPrivate::create(const QString &fileName,QSettings::Format format){switch(format) {caseQSettings::NativeFormat:return newQWinSettingsPrivate(fileName);caseQSettings::Registry32Format:return newQWinSettingsPrivate(fileName, KEY_WOW64_32KEY);caseQSettings::Registry64Format:return newQWinSettingsPrivate(fileName, KEY_WOW64_64KEY);default:break;}return newQConfFileSettingsPrivate(fileName, format);} QT_END_NAMESPACE
|