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:
Sebastian Schuberth 2015-07-03 10:55:08 +02:00
parent bfecc2dbc6
commit 64261c0871
4 changed files with 143 additions and 55 deletions

View File

@ -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

View File

@ -79,6 +79,11 @@ public:
NativeFormat,
IniFormat,
#ifdef Q_OS_WIN
Registry32Format,
Registry64Format,
#endif
InvalidFormat = 16,
CustomFormat1,
CustomFormat2,

View File

@ -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

View File

@ -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()
{
{