Support accessing 32-bit Registry from 64-bit programs and vice versa
For details see "Accessing an Alternate Registry View": http://msdn.microsoft.com/en-us/library/aa384129%28VS.85%29.aspx Task-number: QTBUG-3845 Change-Id: Iecf24b15dc01830686ddd708871bc3392d95282f Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
bfecc2dbc6
commit
64261c0871
@ -2393,14 +2393,24 @@ void QConfFileSettingsPrivate::ensureSectionParsed(QConfFile *confFile,
|
||||
|
||||
This enum type specifies the storage format used by QSettings.
|
||||
|
||||
\value NativeFormat Store the settings using the most
|
||||
appropriate storage format for the platform.
|
||||
On Windows, this means the system registry;
|
||||
on OS X and iOS, this means the CFPreferences
|
||||
API; on Unix, this means textual
|
||||
configuration files in INI format.
|
||||
\value IniFormat Store the settings in INI files.
|
||||
\value InvalidFormat Special value returned by registerFormat().
|
||||
\value NativeFormat Store the settings using the most
|
||||
appropriate storage format for the platform.
|
||||
On Windows, this means the system registry;
|
||||
on OS X and iOS, this means the CFPreferences
|
||||
API; on Unix, this means textual
|
||||
configuration files in INI format.
|
||||
\value Registry32Format Windows only: Explicitly access the 32-bit system registry
|
||||
from a 64-bit application running on 64-bit Windows.
|
||||
On 32-bit Windows or from a 32-bit application on 64-bit Windows,
|
||||
this works the same as specifying NativeFormat.
|
||||
This enum value was added in Qt 5.6.
|
||||
\value Registry64Format Windows only: Explicitly access the 64-bit system registry
|
||||
from a 32-bit application running on 64-bit Windows.
|
||||
On 32-bit Windows or from a 64-bit application on 64-bit Windows,
|
||||
this works the same as specifying NativeFormat.
|
||||
This enum value was added in Qt 5.6.
|
||||
\value IniFormat Store the settings in INI files.
|
||||
\value InvalidFormat Special value returned by registerFormat().
|
||||
\omitvalue CustomFormat1
|
||||
\omitvalue CustomFormat2
|
||||
\omitvalue CustomFormat3
|
||||
|
@ -79,6 +79,11 @@ public:
|
||||
NativeFormat,
|
||||
IniFormat,
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
Registry32Format,
|
||||
Registry64Format,
|
||||
#endif
|
||||
|
||||
InvalidFormat = 16,
|
||||
CustomFormat1,
|
||||
CustomFormat2,
|
||||
|
@ -41,6 +41,18 @@
|
||||
#include "qdebug.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
|
||||
|
||||
/* Keys are stored in QStrings. If the variable name starts with 'u', this is a "user"
|
||||
@ -135,12 +147,13 @@ static QString errorCodeToString(DWORD errorCode)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Open a key with the specified perms
|
||||
static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey)
|
||||
// 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, &resultHandle);
|
||||
0, perms | access, &resultHandle);
|
||||
|
||||
if (res == ERROR_SUCCESS)
|
||||
return resultHandle;
|
||||
@ -148,17 +161,18 @@ static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey)
|
||||
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)
|
||||
// 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);
|
||||
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, 0, &resultHandle, 0);
|
||||
REG_OPTION_NON_VOLATILE, perms | access, 0, &resultHandle, 0);
|
||||
|
||||
if (res == ERROR_SUCCESS)
|
||||
return resultHandle;
|
||||
@ -169,11 +183,12 @@ static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSub
|
||||
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)
|
||||
// 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);
|
||||
HKEY resultHandle = createOrOpenKey(parentHandle, registryPermissions, rSubKey, access);
|
||||
if (resultHandle != 0) {
|
||||
if (readOnly != 0)
|
||||
*readOnly = false;
|
||||
@ -181,7 +196,7 @@ static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *rea
|
||||
}
|
||||
|
||||
// try to open or create it read/only
|
||||
resultHandle = createOrOpenKey(parentHandle, KEY_READ, rSubKey);
|
||||
resultHandle = createOrOpenKey(parentHandle, KEY_READ, rSubKey, access);
|
||||
if (resultHandle != 0) {
|
||||
if (readOnly != 0)
|
||||
*readOnly = true;
|
||||
@ -247,9 +262,9 @@ static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildS
|
||||
return result;
|
||||
}
|
||||
|
||||
static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result)
|
||||
static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result, REGSAM access = 0)
|
||||
{
|
||||
HKEY handle = openKey(parentHandle, KEY_READ, rSubKey);
|
||||
HKEY handle = openKey(parentHandle, KEY_READ, rSubKey, access);
|
||||
if (handle == 0)
|
||||
return;
|
||||
|
||||
@ -270,11 +285,11 @@ static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result)
|
||||
if (!s.isEmpty())
|
||||
s += QLatin1Char('\\');
|
||||
s += childGroups.at(i);
|
||||
allKeys(parentHandle, s, result);
|
||||
allKeys(parentHandle, s, result, access);
|
||||
}
|
||||
}
|
||||
|
||||
static void deleteChildGroups(HKEY parentHandle)
|
||||
static void deleteChildGroups(HKEY parentHandle, REGSAM access = 0)
|
||||
{
|
||||
QStringList childGroups = childKeysOrGroups(parentHandle, QSettingsPrivate::ChildGroups);
|
||||
|
||||
@ -282,10 +297,10 @@ static void deleteChildGroups(HKEY parentHandle)
|
||||
QString group = childGroups.at(i);
|
||||
|
||||
// delete subgroups in group
|
||||
HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group);
|
||||
HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group, access);
|
||||
if (childGroupHandle == 0)
|
||||
continue;
|
||||
deleteChildGroups(childGroupHandle);
|
||||
deleteChildGroups(childGroupHandle, access);
|
||||
RegCloseKey(childGroupHandle);
|
||||
|
||||
// delete group itself
|
||||
@ -305,7 +320,7 @@ static void deleteChildGroups(HKEY parentHandle)
|
||||
class RegistryKey
|
||||
{
|
||||
public:
|
||||
RegistryKey(HKEY parent_handle = 0, const QString &key = QString(), bool read_only = true);
|
||||
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;
|
||||
@ -316,13 +331,15 @@ private:
|
||||
mutable HKEY m_handle;
|
||||
QString m_key;
|
||||
mutable bool m_read_only;
|
||||
REGSAM m_access;
|
||||
};
|
||||
|
||||
RegistryKey::RegistryKey(HKEY parent_handle, const QString &key, bool read_only)
|
||||
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_read_only(read_only),
|
||||
m_access(access)
|
||||
{
|
||||
}
|
||||
|
||||
@ -337,9 +354,9 @@ HKEY RegistryKey::handle() const
|
||||
return m_handle;
|
||||
|
||||
if (m_read_only)
|
||||
m_handle = openKey(m_parent_handle, KEY_READ, m_key);
|
||||
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_handle = createOrOpenKey(m_parent_handle, m_key, &m_read_only, m_access);
|
||||
|
||||
return m_handle;
|
||||
}
|
||||
@ -371,8 +388,8 @@ class QWinSettingsPrivate : public QSettingsPrivate
|
||||
{
|
||||
public:
|
||||
QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
|
||||
const QString &application);
|
||||
QWinSettingsPrivate(QString rKey);
|
||||
const QString &application, REGSAM access = 0);
|
||||
QWinSettingsPrivate(QString rKey, REGSAM access = 0);
|
||||
~QWinSettingsPrivate();
|
||||
|
||||
void remove(const QString &uKey);
|
||||
@ -390,11 +407,13 @@ public:
|
||||
private:
|
||||
RegistryKeyList regList; // list of registry locations to search for keys
|
||||
bool deleteWriteHandleOnExit;
|
||||
REGSAM access;
|
||||
};
|
||||
|
||||
QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
|
||||
const QString &application)
|
||||
: QSettingsPrivate(QSettings::NativeFormat, scope, organization, application)
|
||||
const QString &application, REGSAM access)
|
||||
: QSettingsPrivate(QSettings::NativeFormat, scope, organization, application),
|
||||
access(access)
|
||||
{
|
||||
deleteWriteHandleOnExit = false;
|
||||
|
||||
@ -405,23 +424,24 @@ QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &
|
||||
|
||||
if (scope == QSettings::UserScope) {
|
||||
if (!application.isEmpty())
|
||||
regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty()));
|
||||
regList.append(RegistryKey(HKEY_CURRENT_USER, appPrefix, !regList.isEmpty(), access));
|
||||
|
||||
regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty()));
|
||||
regList.append(RegistryKey(HKEY_CURRENT_USER, orgPrefix, !regList.isEmpty(), access));
|
||||
}
|
||||
|
||||
if (!application.isEmpty())
|
||||
regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty()));
|
||||
regList.append(RegistryKey(HKEY_LOCAL_MACHINE, appPrefix, !regList.isEmpty(), access));
|
||||
|
||||
regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty()));
|
||||
regList.append(RegistryKey(HKEY_LOCAL_MACHINE, orgPrefix, !regList.isEmpty(), access));
|
||||
}
|
||||
|
||||
if (regList.isEmpty())
|
||||
setStatus(QSettings::AccessError);
|
||||
}
|
||||
|
||||
QWinSettingsPrivate::QWinSettingsPrivate(QString rPath)
|
||||
: QSettingsPrivate(QSettings::NativeFormat)
|
||||
QWinSettingsPrivate::QWinSettingsPrivate(QString rPath, REGSAM access)
|
||||
: QSettingsPrivate(QSettings::NativeFormat),
|
||||
access(access)
|
||||
{
|
||||
deleteWriteHandleOnExit = false;
|
||||
|
||||
@ -460,9 +480,9 @@ QWinSettingsPrivate::QWinSettingsPrivate(QString rPath)
|
||||
}
|
||||
|
||||
if (rPath.length() == keyLength)
|
||||
regList.append(RegistryKey(keyName, QString(), false));
|
||||
regList.append(RegistryKey(keyName, QString(), false, access));
|
||||
else if (rPath[keyLength] == QLatin1Char('\\'))
|
||||
regList.append(RegistryKey(keyName, rPath.mid(keyLength+1), false));
|
||||
regList.append(RegistryKey(keyName, rPath.mid(keyLength+1), false, access));
|
||||
}
|
||||
|
||||
bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVariant *value) const
|
||||
@ -471,7 +491,7 @@ bool QWinSettingsPrivate::readKey(HKEY parentHandle, const QString &rSubKey, QVa
|
||||
QString rSubkeyPath = keyPath(rSubKey);
|
||||
|
||||
// open a handle on the subkey
|
||||
HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath);
|
||||
HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath, access);
|
||||
if (handle == 0)
|
||||
return false;
|
||||
|
||||
@ -604,16 +624,16 @@ void QWinSettingsPrivate::remove(const QString &uKey)
|
||||
|
||||
// try to delete value bar in key foo
|
||||
LONG res;
|
||||
HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey));
|
||||
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);
|
||||
handle = openKey(writeHandle(), registryPermissions, rKey, access);
|
||||
if (handle != 0) {
|
||||
deleteChildGroups(handle);
|
||||
deleteChildGroups(handle, access);
|
||||
|
||||
if (rKey.isEmpty()) {
|
||||
QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys);
|
||||
@ -661,7 +681,7 @@ void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value)
|
||||
|
||||
QString rKey = escapedKey(uKey);
|
||||
|
||||
HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey));
|
||||
HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey), access);
|
||||
if (handle == 0) {
|
||||
setStatus(QSettings::AccessError);
|
||||
return;
|
||||
@ -775,13 +795,13 @@ QStringList QWinSettingsPrivate::children(const QString &uKey, ChildSpec spec) c
|
||||
HKEY parent_handle = regList.at(i).handle();
|
||||
if (parent_handle == 0)
|
||||
continue;
|
||||
HKEY handle = openKey(parent_handle, KEY_READ, rKey);
|
||||
HKEY handle = openKey(parent_handle, KEY_READ, rKey, access);
|
||||
if (handle == 0)
|
||||
continue;
|
||||
|
||||
if (spec == AllKeys) {
|
||||
NameSet keys;
|
||||
allKeys(handle, QLatin1String(""), &keys);
|
||||
allKeys(handle, QLatin1String(""), &keys, access);
|
||||
mergeKeySets(&result, keys);
|
||||
} else { // ChildGroups or ChildKeys
|
||||
QStringList names = childKeysOrGroups(handle, spec);
|
||||
@ -836,20 +856,26 @@ bool QWinSettingsPrivate::isWritable() const
|
||||
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
|
||||
const QString &organization, const QString &application)
|
||||
{
|
||||
if (format == QSettings::NativeFormat) {
|
||||
if (format == QSettings::NativeFormat)
|
||||
return new QWinSettingsPrivate(scope, organization, application);
|
||||
} else {
|
||||
else if (format == QSettings::Registry32Format)
|
||||
return new QWinSettingsPrivate(scope, organization, application, KEY_WOW64_32KEY);
|
||||
else if (format == QSettings::Registry64Format)
|
||||
return new QWinSettingsPrivate(scope, organization, application, KEY_WOW64_64KEY);
|
||||
else
|
||||
return new QConfFileSettingsPrivate(format, scope, organization, application);
|
||||
}
|
||||
}
|
||||
|
||||
QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
|
||||
{
|
||||
if (format == QSettings::NativeFormat) {
|
||||
if (format == QSettings::NativeFormat)
|
||||
return new QWinSettingsPrivate(fileName);
|
||||
} else {
|
||||
else if (format == QSettings::Registry32Format)
|
||||
return new QWinSettingsPrivate(fileName, KEY_WOW64_32KEY);
|
||||
else if (format == QSettings::Registry64Format)
|
||||
return new QWinSettingsPrivate(fileName, KEY_WOW64_64KEY);
|
||||
else
|
||||
return new QConfFileSettingsPrivate(fileName, format);
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -121,6 +121,7 @@ private slots:
|
||||
void testEmptyKey();
|
||||
void testResourceFiles();
|
||||
void testRegistryShortRootNames();
|
||||
void testRegistry32And64Bit();
|
||||
void trailingWhitespace();
|
||||
#ifdef Q_OS_MAC
|
||||
void fileName();
|
||||
@ -2075,6 +2076,52 @@ void tst_QSettings::testRegistryShortRootNames()
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QSettings::testRegistry32And64Bit()
|
||||
{
|
||||
#if !defined (Q_OS_WIN) || defined(Q_OS_WINRT)
|
||||
QSKIP("This test is specific to the Windows registry.", SkipAll);
|
||||
#else
|
||||
|
||||
const QString key("HKEY_LOCAL_MACHINE\\Software");
|
||||
const QString keyWow("HKEY_LOCAL_MACHINE\\Software\\Wow6432Node");
|
||||
|
||||
#ifndef Q_OS_WIN64
|
||||
// This branch is taken at compile time if targeting 32-bit; it does not
|
||||
// necessarily mean that the OS running the test is 32-bit (it could be
|
||||
// e.g. 64-bit).
|
||||
QCOMPARE(QSettings(key, QSettings::NativeFormat).childGroups(),
|
||||
QSettings(key, QSettings::Registry32Format).childGroups());
|
||||
|
||||
// Detect whether we are running under 64-bit Windows.
|
||||
typedef BOOL (WINAPI *IsWow64ProcessPtr)(HANDLE hProcess, PBOOL Wow64Process);
|
||||
IsWow64ProcessPtr IsWow64Process = (IsWow64ProcessPtr)QLibrary::resolve(
|
||||
"kernel32.dll", "IsWow64Process");
|
||||
|
||||
if (IsWow64Process) {
|
||||
BOOL IsWow64 = FALSE;
|
||||
if (IsWow64Process(GetCurrentProcess(), &IsWow64) && IsWow64) {
|
||||
// The 64-bit registry's "Wow6432Node" key should match the 32-bit registry.
|
||||
// If we are not on 32-bit Windows, these should never be the same,
|
||||
// because the 64-bit registry has a "Wow6432Node" key.
|
||||
QCOMPARE(QSettings(keyWow, QSettings::Registry64Format).childGroups(),
|
||||
QSettings(key, QSettings::Registry32Format).childGroups());
|
||||
}
|
||||
}
|
||||
#else
|
||||
// This branch is taken at compile time if targeting 64-bit; it does not
|
||||
// necessarily mean that the OS running the test is 64-bit (it could be
|
||||
// e.g. 128-bit).
|
||||
QCOMPARE(QSettings(key, QSettings::NativeFormat).childGroups(),
|
||||
QSettings(key, QSettings::Registry64Format).childGroups());
|
||||
|
||||
// The 64-bit registry's "Wow6432Node" key should match the 32-bit registry.
|
||||
QCOMPARE(QSettings(keyWow, QSettings::Registry64Format).childGroups(),
|
||||
QSettings(key, QSettings::Registry32Format).childGroups());
|
||||
#endif
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QSettings::trailingWhitespace()
|
||||
{
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user