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. This enum type specifies the storage format used by QSettings.
\value NativeFormat Store the settings using the most \value NativeFormat Store the settings using the most
appropriate storage format for the platform. appropriate storage format for the platform.
On Windows, this means the system registry; On Windows, this means the system registry;
on OS X and iOS, this means the CFPreferences on OS X and iOS, this means the CFPreferences
API; on Unix, this means textual API; on Unix, this means textual
configuration files in INI format. configuration files in INI format.
\value IniFormat Store the settings in INI files. \value Registry32Format Windows only: Explicitly access the 32-bit system registry
\value InvalidFormat Special value returned by registerFormat(). 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 CustomFormat1
\omitvalue CustomFormat2 \omitvalue CustomFormat2
\omitvalue CustomFormat3 \omitvalue CustomFormat3

View File

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

View File

@ -41,6 +41,18 @@
#include "qdebug.h" #include "qdebug.h"
#include <qt_windows.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 QT_BEGIN_NAMESPACE
/* Keys are stored in QStrings. If the variable name starts with 'u', this is a "user" /* 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; return result;
} }
// Open a key with the specified perms // Open a key with the specified "perms".
static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey) // "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; HKEY resultHandle = 0;
LONG res = RegOpenKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), LONG res = RegOpenKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()),
0, perms, &resultHandle); 0, perms | access, &resultHandle);
if (res == ERROR_SUCCESS) if (res == ERROR_SUCCESS)
return resultHandle; return resultHandle;
@ -148,17 +161,18 @@ static HKEY openKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey)
return 0; return 0;
} }
// Open a key with the specified perms, create it if it does not exist // Open a key with the specified "perms", create it if it does not exist.
static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSubKey) // "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 // try to open it
HKEY resultHandle = openKey(parentHandle, perms, rSubKey); HKEY resultHandle = openKey(parentHandle, perms, rSubKey, access);
if (resultHandle != 0) if (resultHandle != 0)
return resultHandle; return resultHandle;
// try to create it // try to create it
LONG res = RegCreateKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), 0, 0, 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) if (res == ERROR_SUCCESS)
return resultHandle; return resultHandle;
@ -169,11 +183,12 @@ static HKEY createOrOpenKey(HKEY parentHandle, REGSAM perms, const QString &rSub
return 0; return 0;
} }
// Open or create a key in read-write mode if possible, otherwise read-only // Open or create a key in read-write mode if possible, otherwise read-only.
static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *readOnly) // "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 // 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 (resultHandle != 0) {
if (readOnly != 0) if (readOnly != 0)
*readOnly = false; *readOnly = false;
@ -181,7 +196,7 @@ static HKEY createOrOpenKey(HKEY parentHandle, const QString &rSubKey, bool *rea
} }
// try to open or create it read/only // 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 (resultHandle != 0) {
if (readOnly != 0) if (readOnly != 0)
*readOnly = true; *readOnly = true;
@ -247,9 +262,9 @@ static QStringList childKeysOrGroups(HKEY parentHandle, QSettingsPrivate::ChildS
return result; 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) if (handle == 0)
return; return;
@ -270,11 +285,11 @@ static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result)
if (!s.isEmpty()) if (!s.isEmpty())
s += QLatin1Char('\\'); s += QLatin1Char('\\');
s += childGroups.at(i); 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); QStringList childGroups = childKeysOrGroups(parentHandle, QSettingsPrivate::ChildGroups);
@ -282,10 +297,10 @@ static void deleteChildGroups(HKEY parentHandle)
QString group = childGroups.at(i); QString group = childGroups.at(i);
// delete subgroups in group // delete subgroups in group
HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group); HKEY childGroupHandle = openKey(parentHandle, registryPermissions, group, access);
if (childGroupHandle == 0) if (childGroupHandle == 0)
continue; continue;
deleteChildGroups(childGroupHandle); deleteChildGroups(childGroupHandle, access);
RegCloseKey(childGroupHandle); RegCloseKey(childGroupHandle);
// delete group itself // delete group itself
@ -305,7 +320,7 @@ static void deleteChildGroups(HKEY parentHandle)
class RegistryKey class RegistryKey
{ {
public: 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; QString key() const;
HKEY handle() const; HKEY handle() const;
HKEY parentHandle() const; HKEY parentHandle() const;
@ -316,13 +331,15 @@ private:
mutable HKEY m_handle; mutable HKEY m_handle;
QString m_key; QString m_key;
mutable bool m_read_only; 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_parent_handle(parent_handle),
m_handle(0), m_handle(0),
m_key(key), 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; return m_handle;
if (m_read_only) 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 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; return m_handle;
} }
@ -371,8 +388,8 @@ class QWinSettingsPrivate : public QSettingsPrivate
{ {
public: public:
QWinSettingsPrivate(QSettings::Scope scope, const QString &organization, QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
const QString &application); const QString &application, REGSAM access = 0);
QWinSettingsPrivate(QString rKey); QWinSettingsPrivate(QString rKey, REGSAM access = 0);
~QWinSettingsPrivate(); ~QWinSettingsPrivate();
void remove(const QString &uKey); void remove(const QString &uKey);
@ -390,11 +407,13 @@ public:
private: private:
RegistryKeyList regList; // list of registry locations to search for keys RegistryKeyList regList; // list of registry locations to search for keys
bool deleteWriteHandleOnExit; bool deleteWriteHandleOnExit;
REGSAM access;
}; };
QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &organization, QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &organization,
const QString &application) const QString &application, REGSAM access)
: QSettingsPrivate(QSettings::NativeFormat, scope, organization, application) : QSettingsPrivate(QSettings::NativeFormat, scope, organization, application),
access(access)
{ {
deleteWriteHandleOnExit = false; deleteWriteHandleOnExit = false;
@ -405,23 +424,24 @@ QWinSettingsPrivate::QWinSettingsPrivate(QSettings::Scope scope, const QString &
if (scope == QSettings::UserScope) { if (scope == QSettings::UserScope) {
if (!application.isEmpty()) 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()) 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()) if (regList.isEmpty())
setStatus(QSettings::AccessError); setStatus(QSettings::AccessError);
} }
QWinSettingsPrivate::QWinSettingsPrivate(QString rPath) QWinSettingsPrivate::QWinSettingsPrivate(QString rPath, REGSAM access)
: QSettingsPrivate(QSettings::NativeFormat) : QSettingsPrivate(QSettings::NativeFormat),
access(access)
{ {
deleteWriteHandleOnExit = false; deleteWriteHandleOnExit = false;
@ -460,9 +480,9 @@ QWinSettingsPrivate::QWinSettingsPrivate(QString rPath)
} }
if (rPath.length() == keyLength) if (rPath.length() == keyLength)
regList.append(RegistryKey(keyName, QString(), false)); regList.append(RegistryKey(keyName, QString(), false, access));
else if (rPath[keyLength] == QLatin1Char('\\')) 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 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); QString rSubkeyPath = keyPath(rSubKey);
// open a handle on the subkey // open a handle on the subkey
HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath); HKEY handle = openKey(parentHandle, KEY_READ, rSubkeyPath, access);
if (handle == 0) if (handle == 0)
return false; return false;
@ -604,16 +624,16 @@ void QWinSettingsPrivate::remove(const QString &uKey)
// try to delete value bar in key foo // try to delete value bar in key foo
LONG res; LONG res;
HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey)); HKEY handle = openKey(writeHandle(), registryPermissions, keyPath(rKey), access);
if (handle != 0) { if (handle != 0) {
res = RegDeleteValue(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16())); res = RegDeleteValue(handle, reinterpret_cast<const wchar_t *>(keyName(rKey).utf16()));
RegCloseKey(handle); RegCloseKey(handle);
} }
// try to delete key foo/bar and all subkeys // try to delete key foo/bar and all subkeys
handle = openKey(writeHandle(), registryPermissions, rKey); handle = openKey(writeHandle(), registryPermissions, rKey, access);
if (handle != 0) { if (handle != 0) {
deleteChildGroups(handle); deleteChildGroups(handle, access);
if (rKey.isEmpty()) { if (rKey.isEmpty()) {
QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys); QStringList childKeys = childKeysOrGroups(handle, QSettingsPrivate::ChildKeys);
@ -661,7 +681,7 @@ void QWinSettingsPrivate::set(const QString &uKey, const QVariant &value)
QString rKey = escapedKey(uKey); QString rKey = escapedKey(uKey);
HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey)); HKEY handle = createOrOpenKey(writeHandle(), registryPermissions, keyPath(rKey), access);
if (handle == 0) { if (handle == 0) {
setStatus(QSettings::AccessError); setStatus(QSettings::AccessError);
return; return;
@ -775,13 +795,13 @@ QStringList QWinSettingsPrivate::children(const QString &uKey, ChildSpec spec) c
HKEY parent_handle = regList.at(i).handle(); HKEY parent_handle = regList.at(i).handle();
if (parent_handle == 0) if (parent_handle == 0)
continue; continue;
HKEY handle = openKey(parent_handle, KEY_READ, rKey); HKEY handle = openKey(parent_handle, KEY_READ, rKey, access);
if (handle == 0) if (handle == 0)
continue; continue;
if (spec == AllKeys) { if (spec == AllKeys) {
NameSet keys; NameSet keys;
allKeys(handle, QLatin1String(""), &keys); allKeys(handle, QLatin1String(""), &keys, access);
mergeKeySets(&result, keys); mergeKeySets(&result, keys);
} else { // ChildGroups or ChildKeys } else { // ChildGroups or ChildKeys
QStringList names = childKeysOrGroups(handle, spec); QStringList names = childKeysOrGroups(handle, spec);
@ -836,20 +856,26 @@ bool QWinSettingsPrivate::isWritable() const
QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope, QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings::Scope scope,
const QString &organization, const QString &application) const QString &organization, const QString &application)
{ {
if (format == QSettings::NativeFormat) { if (format == QSettings::NativeFormat)
return new QWinSettingsPrivate(scope, organization, application); 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); return new QConfFileSettingsPrivate(format, scope, organization, application);
}
} }
QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format) QSettingsPrivate *QSettingsPrivate::create(const QString &fileName, QSettings::Format format)
{ {
if (format == QSettings::NativeFormat) { if (format == QSettings::NativeFormat)
return new QWinSettingsPrivate(fileName); 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); return new QConfFileSettingsPrivate(fileName, format);
}
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -121,6 +121,7 @@ private slots:
void testEmptyKey(); void testEmptyKey();
void testResourceFiles(); void testResourceFiles();
void testRegistryShortRootNames(); void testRegistryShortRootNames();
void testRegistry32And64Bit();
void trailingWhitespace(); void trailingWhitespace();
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
void fileName(); void fileName();
@ -2075,6 +2076,52 @@ void tst_QSettings::testRegistryShortRootNames()
#endif #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() void tst_QSettings::trailingWhitespace()
{ {
{ {