Win32: Consolidate registry code

Add a RAII class for registry keys and use it throughout
the code base.

Change-Id: I666b2fbb790f83436443101d6bc1e3c0525e78df
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Friedemann Kleint 2019-09-27 13:36:38 +02:00
parent 95ac2072bb
commit 05a829f923
17 changed files with 375 additions and 207 deletions

View File

@ -48,11 +48,10 @@
#include "qoperatingsystemversion_p.h"
#if defined(Q_OS_WIN) || defined(Q_OS_CYGWIN) || defined(Q_OS_WINRT)
# include "qoperatingsystemversion_win_p.h"
# if QT_CONFIG(settings)
# include "qsettings.h"
# include "qvariant.h"
# endif
# ifndef Q_OS_WINRT
# include "private/qwinregistry_p.h"
# endif
#endif // Q_OS_WIN || Q_OS_CYGWIN
#include <private/qlocale_tools_p.h>
#include <qmutex.h>
@ -2190,28 +2189,25 @@ const QSysInfo::WinVersion QSysInfo::WindowsVersion = QSysInfo::windowsVersion()
QT_WARNING_POP
#endif
static QString readRegistryString(const QString &key, const QString &subKey)
static QString readVersionRegistryString(const wchar_t *subKey)
{
#if QT_CONFIG(settings)
QSettings settings(key, QSettings::NativeFormat);
return settings.value(subKey).toString();
#if !defined(QT_BUILD_QMAKE) && !defined(Q_OS_WINRT)
return QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)")
.stringValue(subKey);
#else
Q_UNUSED(key);
Q_UNUSED(subKey);
return QString();
#endif
}
static inline QString windowsVersionKey() { return QStringLiteral(R"(HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion)"); }
static inline QString windows10ReleaseId()
{
return readRegistryString(windowsVersionKey(), QStringLiteral("ReleaseId"));
return readVersionRegistryString(L"ReleaseId");
}
static inline QString windows7Build()
{
return readRegistryString(windowsVersionKey(), QStringLiteral("CurrentBuild"));
return readVersionRegistryString(L"CurrentBuild");
}
static QString winSp_helper()
@ -3078,6 +3074,7 @@ QByteArray QSysInfo::machineUniqueId()
}
#elif defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
// Let's poke at the registry
// ### Qt 6: Use new helpers from qwinregistry.cpp (once bootstrap builds are obsolete)
HKEY key = NULL;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Cryptography", 0, KEY_READ | KEY_WOW64_64KEY, &key)
== ERROR_SUCCESS) {

View File

@ -136,6 +136,8 @@ static void mergeKeySets(NameSet *dest, const QStringList &src)
** 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)

View File

@ -88,8 +88,10 @@ win32 {
SOURCES += kernel/qeventdispatcher_winrt.cpp
HEADERS += kernel/qeventdispatcher_winrt_p.h
} else {
SOURCES += kernel/qeventdispatcher_win.cpp
HEADERS += kernel/qeventdispatcher_win_p.h
SOURCES += kernel/qeventdispatcher_win.cpp \
kernel/qwinregistry.cpp
HEADERS += kernel/qeventdispatcher_win_p.h \
kernel/qwinregistry_p.h
}
!winrt: LIBS_PRIVATE += -lversion

View File

@ -0,0 +1,120 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwinregistry_p.h"
#include <QtCore/qvarlengtharray.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
QWinRegistryKey::QWinRegistryKey() :
m_key(nullptr)
{
}
// Open a key with the specified permissions (KEY_READ/KEY_WRITE).
// "access" is to explicitly use the 32- or 64-bit branch.
QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions, REGSAM access)
{
if (RegOpenKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(subKey.utf16()),
0, permissions | access, &m_key) != ERROR_SUCCESS) {
m_key = nullptr;
}
}
QWinRegistryKey::~QWinRegistryKey()
{
close();
}
void QWinRegistryKey::close()
{
if (isValid()) {
RegCloseKey(m_key);
m_key = nullptr;
}
}
QString QWinRegistryKey::stringValue(QStringView subKey) const
{
QString result;
if (!isValid())
return result;
DWORD type;
DWORD size;
auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, &size) != ERROR_SUCCESS
|| (type != REG_SZ && type != REG_EXPAND_SZ) || size <= 2) {
return result;
}
// Reserve more for rare cases where trailing '\0' are missing in registry,
// otherwise chop off the '\0' received.
QString buffer(int(size / sizeof(wchar_t)), Qt::Uninitialized);
if (RegQueryValueEx(m_key, subKeyC, nullptr, &type,
reinterpret_cast<LPBYTE>(buffer.data()), &size) == ERROR_SUCCESS) {
if (buffer.endsWith(QChar::Null))
buffer.chop(1);
} else {
buffer.clear();
}
return buffer;
}
QPair<DWORD, bool> QWinRegistryKey::dwordValue(QStringView subKey) const
{
if (!isValid())
return qMakePair(0, false);
DWORD type;
auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS
|| type != REG_DWORD) {
return qMakePair(0, false);
}
DWORD value = 0;
DWORD size = sizeof(value);
const bool ok =
RegQueryValueEx(m_key, subKeyC, nullptr, nullptr,
reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS;
return qMakePair(value, ok);
}
QT_END_NAMESPACE

View File

@ -0,0 +1,89 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QWINREGISTRY_H
#define QWINREGISTRY_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qpair.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringview.h>
#include <QtCore/qt_windows.h>
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QWinRegistryKey
{
public:
Q_DISABLE_COPY(QWinRegistryKey)
QWinRegistryKey();
explicit QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions = KEY_READ, REGSAM access = 0);
~QWinRegistryKey();
QWinRegistryKey(QWinRegistryKey &&other) noexcept { swap(other); }
QWinRegistryKey &operator=(QWinRegistryKey &&other) noexcept { swap(other); return *this; }
void swap(QWinRegistryKey &other) noexcept { qSwap(m_key, other.m_key); }
bool isValid() const { return m_key != nullptr; }
operator HKEY() const { return m_key; }
void close();
QString stringValue(QStringView subKey) const;
QPair<DWORD, bool> dwordValue(QStringView subKey) const;
private:
HKEY m_key;
};
QT_END_NAMESPACE
#endif // QWINREGISTRY_H

View File

@ -46,13 +46,14 @@
#include <algorithm>
QT_BEGIN_NAMESPACE
#ifndef Q_OS_WINRT
#include <private/qwinregistry_p.h>
// The registry-based timezone backend is not available on WinRT, which falls back to equivalent APIs.
#define QT_USE_REGISTRY_TIMEZONE 1
#endif
QT_BEGIN_NAMESPACE
/*
Private
@ -71,8 +72,8 @@ QT_BEGIN_NAMESPACE
// Vista introduced support for historic data, see MSDN docs on DYNAMIC_TIME_ZONE_INFORMATION
// http://msdn.microsoft.com/en-gb/library/windows/desktop/ms724253%28v=vs.85%29.aspx
#ifdef QT_USE_REGISTRY_TIMEZONE
static const char tzRegPath[] = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones";
static const char currTzRegPath[] = "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation";
static const wchar_t tzRegPath[] = LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones)";
static const wchar_t currTzRegPath[] = LR"(SYSTEM\CurrentControlSet\Control\TimeZoneInformation)";
#endif
enum {
@ -138,27 +139,6 @@ bool equalTzi(const TIME_ZONE_INFORMATION &tzi1, const TIME_ZONE_INFORMATION &tz
}
#ifdef QT_USE_REGISTRY_TIMEZONE
bool openRegistryKey(const QString &keyPath, HKEY *key)
{
return RegOpenKeyEx(HKEY_LOCAL_MACHINE, reinterpret_cast<const wchar_t*>(keyPath.utf16()),
0, KEY_READ, key) == ERROR_SUCCESS;
}
QString readRegistryString(const HKEY &key, const wchar_t *value)
{
wchar_t buffer[MAX_PATH] = {0};
DWORD size = sizeof(wchar_t) * MAX_PATH;
RegQueryValueEx(key, value, nullptr, nullptr, reinterpret_cast<LPBYTE>(buffer), &size);
return QString::fromWCharArray(buffer);
}
int readRegistryValue(const HKEY &key, const wchar_t *value)
{
DWORD buffer;
DWORD size = sizeof(buffer);
RegQueryValueEx(key, value, nullptr, nullptr, reinterpret_cast<LPBYTE>(&buffer), &size);
return buffer;
}
QWinTimeZonePrivate::QWinTransitionRule readRegistryRule(const HKEY &key,
const wchar_t *value, bool *ok)
@ -185,12 +165,11 @@ TIME_ZONE_INFORMATION getRegistryTzi(const QByteArray &windowsId, bool *ok)
TIME_ZONE_INFORMATION tzi;
REG_TZI_FORMAT regTzi;
DWORD regTziSize = sizeof(regTzi);
HKEY key = NULL;
const QString tziKeyPath = QString::fromUtf8(tzRegPath) + QLatin1Char('\\')
const QString tziKeyPath = QString::fromWCharArray(tzRegPath) + QLatin1Char('\\')
+ QString::fromUtf8(windowsId);
if (openRegistryKey(tziKeyPath, &key)) {
QWinRegistryKey key(HKEY_LOCAL_MACHINE, tziKeyPath);
if (key.isValid()) {
DWORD size = sizeof(tzi.DaylightName);
RegQueryValueEx(key, L"Dlt", nullptr, nullptr, reinterpret_cast<LPBYTE>(tzi.DaylightName), &size);
@ -206,8 +185,6 @@ TIME_ZONE_INFORMATION getRegistryTzi(const QByteArray &windowsId, bool *ok)
tzi.DaylightDate = regTzi.DaylightDate;
*ok = true;
}
RegCloseKey(key);
}
return tzi;
@ -299,8 +276,8 @@ QList<QByteArray> availableWindowsIds()
#ifdef QT_USE_REGISTRY_TIMEZONE
// TODO Consider caching results in a global static, very unlikely to change.
QList<QByteArray> list;
HKEY key = NULL;
if (openRegistryKey(QString::fromUtf8(tzRegPath), &key)) {
QWinRegistryKey key(HKEY_LOCAL_MACHINE, tzRegPath);
if (key.isValid()) {
DWORD idCount = 0;
if (RegQueryInfoKey(key, 0, 0, 0, &idCount, 0, 0, 0, 0, 0, 0, 0) == ERROR_SUCCESS
&& idCount > 0) {
@ -311,7 +288,6 @@ QList<QByteArray> availableWindowsIds()
list.append(QString::fromWCharArray(buffer).toUtf8());
}
}
RegCloseKey(key);
}
return list;
#else // QT_USE_REGISTRY_TIMEZONE
@ -325,15 +301,10 @@ QByteArray windowsSystemZoneId()
{
#ifdef QT_USE_REGISTRY_TIMEZONE
// On Vista and later is held in the value TimeZoneKeyName in key currTzRegPath
QString id;
HKEY key = NULL;
QString tziKeyPath = QString::fromUtf8(currTzRegPath);
if (openRegistryKey(tziKeyPath, &key)) {
id = readRegistryString(key, L"TimeZoneKeyName");
RegCloseKey(key);
const QString id = QWinRegistryKey(HKEY_LOCAL_MACHINE, currTzRegPath)
.stringValue(L"TimeZoneKeyName");
if (!id.isEmpty())
return std::move(id).toUtf8();
}
return id.toUtf8();
// On XP we have to iterate over the zones until we find a match on
// names/offsets with the current data
@ -575,22 +546,22 @@ void QWinTimeZonePrivate::init(const QByteArray &ianaId)
if (!m_windowsId.isEmpty()) {
#ifdef QT_USE_REGISTRY_TIMEZONE
// Open the base TZI for the time zone
HKEY baseKey = NULL;
const QString baseKeyPath = QString::fromUtf8(tzRegPath) + QLatin1Char('\\')
const QString baseKeyPath = QString::fromWCharArray(tzRegPath) + QLatin1Char('\\')
+ QString::fromUtf8(m_windowsId);
if (openRegistryKey(baseKeyPath, &baseKey)) {
QWinRegistryKey baseKey(HKEY_LOCAL_MACHINE, baseKeyPath);
if (baseKey.isValid()) {
// Load the localized names
m_displayName = readRegistryString(baseKey, L"Display");
m_standardName = readRegistryString(baseKey, L"Std");
m_daylightName = readRegistryString(baseKey, L"Dlt");
m_displayName = baseKey.stringValue(L"Display");
m_standardName = baseKey.stringValue(L"Std");
m_daylightName = baseKey.stringValue(L"Dlt");
// On Vista and later the optional dynamic key holds historic data
const QString dynamicKeyPath = baseKeyPath + QLatin1String("\\Dynamic DST");
HKEY dynamicKey = NULL;
if (openRegistryKey(dynamicKeyPath, &dynamicKey)) {
QWinRegistryKey dynamicKey(HKEY_LOCAL_MACHINE, dynamicKeyPath);
if (dynamicKey.isValid()) {
// Find out the start and end years stored, then iterate over them
int startYear = readRegistryValue(dynamicKey, L"FirstEntry");
int endYear = readRegistryValue(dynamicKey, L"LastEntry");
for (int year = startYear; year <= endYear; ++year) {
const auto startYear = dynamicKey.dwordValue(L"FirstEntry");
const auto endYear = dynamicKey.dwordValue(L"LastEntry");
for (int year = int(startYear.first); year <= int(endYear.first); ++year) {
bool ruleOk;
QWinTransitionRule rule = readRegistryRule(dynamicKey,
reinterpret_cast<LPCWSTR>(QString::number(year).utf16()),
@ -611,7 +582,6 @@ void QWinTimeZonePrivate::init(const QByteArray &ianaId)
m_tranRules.append(rule);
}
}
RegCloseKey(dynamicKey);
} else {
// No dynamic data so use the base data
bool ruleOk;
@ -620,7 +590,6 @@ void QWinTimeZonePrivate::init(const QByteArray &ianaId)
if (ruleOk)
m_tranRules.append(rule);
}
RegCloseKey(baseKey);
}
#else // QT_USE_REGISTRY_TIMEZONE
if (gTimeZones->isEmpty())

View File

@ -53,6 +53,7 @@
#include <QtCore/QtEndian>
#include <QtCore/QThreadStorage>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h>
#include <wchar.h>
@ -1210,33 +1211,8 @@ static int QT_WIN_CALLBACK populateFontFamilies(const LOGFONT *logFont, const TE
void QWindowsFontDatabase::addDefaultEUDCFont()
{
QString path;
{
HKEY key;
if (RegOpenKeyEx(HKEY_CURRENT_USER,
L"EUDC\\1252",
0,
KEY_READ,
&key) != ERROR_SUCCESS) {
return;
}
WCHAR value[MAX_PATH];
DWORD bufferSize = sizeof(value);
ZeroMemory(value, bufferSize);
if (RegQueryValueEx(key,
L"SystemDefaultEUDCFont",
nullptr,
nullptr,
reinterpret_cast<LPBYTE>(value),
&bufferSize) == ERROR_SUCCESS) {
path = QString::fromWCharArray(value);
}
RegCloseKey(key);
}
const QString path = QWinRegistryKey(HKEY_CURRENT_USER, LR"(EUDC\1252)")
.stringValue(L"SystemDefaultEUDCFont");
if (!path.isEmpty()) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
@ -2105,28 +2081,6 @@ int QWindowsFontDatabase::defaultVerticalDPI()
return vDPI;
}
QString QWindowsFontDatabase::readRegistryString(HKEY parentHandle, const wchar_t *keyPath, const wchar_t *keyName)
{
QString result;
HKEY handle = 0;
if (RegOpenKeyEx(parentHandle, keyPath, 0, KEY_READ, &handle) == ERROR_SUCCESS) {
// get the size and type of the value
DWORD dataType;
DWORD dataSize;
if (RegQueryValueEx(handle, keyName, 0, &dataType, 0, &dataSize) == ERROR_SUCCESS) {
if (dataType == REG_SZ || dataType == REG_EXPAND_SZ) {
dataSize += 2; // '\0' missing?
QVarLengthArray<unsigned char> data(dataSize);
data[dataSize - 2] = data[dataSize - 1] = '\0';
if (RegQueryValueEx(handle, keyName, 0, 0, data.data(), &dataSize) == ERROR_SUCCESS)
result = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(data.data()));
}
}
RegCloseKey(handle);
}
return result;
}
bool QWindowsFontDatabase::isPrivateFontFamily(const QString &family) const
{
return m_eudcFonts.contains(family) || QPlatformFontDatabase::isPrivateFontFamily(family);

View File

@ -133,8 +133,6 @@ public:
static void setFontOptions(unsigned options);
static unsigned fontOptions();
static QString readRegistryString(HKEY parentHandle, const wchar_t *keyPath, const wchar_t *keyName);
private:
void removeApplicationFonts();
void addDefaultEUDCFont();

View File

@ -47,6 +47,7 @@
#include <QtCore/QFile>
#include <private/qstringiterator_p.h>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <QtGui/private/qhighdpiscaling_p.h>
@ -945,10 +946,10 @@ void QWindowsFontEngineDirectWrite::initFontInfo(const QFontDef &request,
QString QWindowsFontEngineDirectWrite::fontNameSubstitute(const QString &familyName)
{
const wchar_t key[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
const QString substitute =
QWindowsFontDatabase::readRegistryString(HKEY_LOCAL_MACHINE, key,
reinterpret_cast<const wchar_t *>(familyName.utf16()));
QWinRegistryKey(HKEY_LOCAL_MACHINE,
LR"(Software\Microsoft\Windows NT\CurrentVersion\FontSubstitutes)")
.stringValue(familyName);
return substitute.isEmpty() ? familyName : substitute;
}

View File

@ -79,6 +79,7 @@
#include <QtCore/qscopedpointer.h>
#include <QtCore/quuid.h>
#include <QtCore/private/qsystemlibrary_p.h>
#include <QtCore/private/qwinregistry_p.h>
#include <QtEventDispatcherSupport/private/qwindowsguieventdispatcher_p.h>
@ -1518,28 +1519,13 @@ QTouchDevice *QWindowsContext::touchDevice() const
d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice();
}
static DWORD readDwordRegistrySetting(const wchar_t *regKey, const wchar_t *subKey, DWORD defaultValue)
{
DWORD result = defaultValue;
HKEY handle;
if (RegOpenKeyEx(HKEY_CURRENT_USER, regKey, 0, KEY_READ, &handle) == ERROR_SUCCESS) {
DWORD type;
if (RegQueryValueEx(handle, subKey, nullptr, &type, nullptr, nullptr) == ERROR_SUCCESS
&& type == REG_DWORD) {
DWORD value;
DWORD size = sizeof(result);
if (RegQueryValueEx(handle, subKey, nullptr, nullptr, reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS)
result = value;
}
RegCloseKey(handle);
}
return result;
}
DWORD QWindowsContext::readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue)
{
return readDwordRegistrySetting(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",
subKey, defaultValue);
const auto value =
QWinRegistryKey(HKEY_CURRENT_USER,
LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced)")
.dwordValue(subKey);
return value.second ? value.first : defaultValue;
}
static inline bool isEmptyRect(const RECT &rect)

View File

@ -45,6 +45,8 @@
#include <QtCore/qdebug.h>
#include <QtCore/qdir.h>
#include <QtCore/private/qwinregistry_p.h>
#include <shlobj.h>
#include <intshcut.h>
@ -78,35 +80,24 @@ static inline QString mailCommand()
const wchar_t mailUserKey[] = L"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\mailto\\UserChoice";
wchar_t command[MAX_PATH] = {0};
// Check if user has set preference, otherwise use default.
HKEY handle;
QString keyName;
if (!RegOpenKeyEx(HKEY_CURRENT_USER, mailUserKey, 0, KEY_READ, &handle)) {
DWORD bufferSize = BufferSize;
if (!RegQueryValueEx(handle, L"Progid", nullptr, nullptr, reinterpret_cast<unsigned char*>(command), &bufferSize))
keyName = QString::fromWCharArray(command);
RegCloseKey(handle);
}
QString keyName = QWinRegistryKey(HKEY_CURRENT_USER, mailUserKey)
.stringValue( L"Progid");
const QLatin1String mailto = keyName.isEmpty() ? QLatin1String("mailto") : QLatin1String();
keyName += mailto + QLatin1String("\\Shell\\Open\\Command");
if (debug)
qDebug() << __FUNCTION__ << "keyName=" << keyName;
command[0] = 0;
if (!RegOpenKeyExW(HKEY_CLASSES_ROOT, reinterpret_cast<const wchar_t*>(keyName.utf16()), 0, KEY_READ, &handle)) {
DWORD bufferSize = BufferSize;
RegQueryValueEx(handle, L"", nullptr, nullptr, reinterpret_cast<unsigned char*>(command), &bufferSize);
RegCloseKey(handle);
}
const QString command = QWinRegistryKey(HKEY_CLASSES_ROOT, keyName).stringValue(L"");
// QTBUG-57816: As of Windows 10, if there is no mail client installed, an entry like
// "rundll32.exe .. url.dll,MailToProtocolHandler %l" is returned. Launching it
// silently fails or brings up a broken dialog after a long time, so exclude it and
// fall back to ShellExecute() which brings up the URL assocation dialog.
if (!command[0] || wcsstr(command, L",MailToProtocolHandler") != nullptr)
if (command.isEmpty() || command.contains(QLatin1String(",MailToProtocolHandler")))
return QString();
wchar_t expandedCommand[MAX_PATH] = {0};
return ExpandEnvironmentStrings(command, expandedCommand, MAX_PATH) ?
QString::fromWCharArray(expandedCommand) : QString::fromWCharArray(command);
return ExpandEnvironmentStrings(reinterpret_cast<const wchar_t *>(command.utf16()),
expandedCommand, MAX_PATH)
? QString::fromWCharArray(expandedCommand) : command;
}
static inline bool launchMail(const QUrl &url)

View File

@ -126,6 +126,7 @@ win32:SOURCES += ../../corelib/global/qoperatingsystemversion_win.cpp \
../../corelib/kernel/qsharedmemory_win.cpp \
../../corelib/kernel/qsystemsemaphore_win.cpp \
../../corelib/plugin/qsystemlibrary.cpp \
../../corelib/kernel/qwinregistry.cpp \
mac {
SOURCES += \

View File

@ -12,3 +12,6 @@ SUBDIRS=\
qtendian \
qglobalstatic \
qhooks
win32:!winrt: SUBDIRS += \
qwinregistry

View File

@ -0,0 +1,8 @@
CONFIG += testcase
QT += testlib core-private
QT -= gui
TARGET = tst_qwinregistry
CONFIG += console
SOURCES += tst_qwinregistry.cpp

View File

@ -0,0 +1,68 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/private/qwinregistry_p.h>
class tst_QWinRegistry : public QObject
{
Q_OBJECT
public Q_SLOTS:
void initTestCase();
private Q_SLOTS:
void values();
};
void tst_QWinRegistry::initTestCase()
{
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows10)
QSKIP("This test requires registry values present in Windows 10");
}
void tst_QWinRegistry::values()
{
QWinRegistryKey key(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)");
QVERIFY(key.isValid());
QVERIFY(!key.stringValue(L"ProductName").isEmpty());
QVERIFY(key.stringValue(L"NonExistingKey").isEmpty());
auto majorVersion = key.dwordValue(L"CurrentMajorVersionNumber");
QVERIFY(majorVersion.second);
QVERIFY(majorVersion.first > 0);
auto nonExistingValue = key.dwordValue(L"NonExistingKey");
QVERIFY(!nonExistingValue.second);
QCOMPARE(nonExistingValue.first, 0u);
}
QTEST_APPLESS_MAIN(tst_QWinRegistry);
#include "tst_qwinregistry.moc"

View File

@ -49,6 +49,7 @@
#ifdef Q_OS_WIN
#include <qt_windows.h>
#if !defined(Q_OS_WINRT)
#include <private/qwinregistry_p.h>
#include <lm.h>
#endif
#endif
@ -1243,17 +1244,12 @@ void tst_QFileInfo::fileTimes()
//In Vista the last-access timestamp is not updated when the file is accessed/touched (by default).
//To enable this the HKLM\SYSTEM\CurrentControlSet\Control\FileSystem\NtfsDisableLastAccessUpdate
//is set to 0, in the test machine.
HKEY key;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\FileSystem",
0, KEY_READ, &key)) {
DWORD disabledAccessTimes = 0;
DWORD size = sizeof(DWORD);
LONG error = RegQueryValueEx(key, L"NtfsDisableLastAccessUpdate"
, NULL, NULL, (LPBYTE)&disabledAccessTimes, &size);
if (ERROR_SUCCESS == error && disabledAccessTimes)
const auto disabledAccessTimes =
QWinRegistryKey(HKEY_LOCAL_MACHINE,
LR"(SYSTEM\CurrentControlSet\Control\FileSystem)")
.dwordValue(L"NtfsDisableLastAccessUpdate");
if (disabledAccessTimes.second && disabledAccessTimes.first != 0)
noAccessTime = true;
RegCloseKey(key);
}
#endif
if (noAccessTime)

View File

@ -54,6 +54,9 @@
#if defined(Q_OS_WIN)
#include <QtCore/qt_windows.h>
#ifndef Q_OS_WINRT
# include <private/qwinregistry_p.h>
#endif
#else
#include <unistd.h>
#endif
@ -3623,16 +3626,13 @@ void tst_QSettings::recursionBug()
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
static DWORD readKeyType(HKEY handle, const QString &rSubKey)
static DWORD readKeyType(HKEY handle, QStringView rSubKey)
{
DWORD dataType;
DWORD dataSize;
LONG res = RegQueryValueEx(handle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()), 0, &dataType, 0, &dataSize);
if (res == ERROR_SUCCESS)
return dataType;
return 0;
LONG res = RegQueryValueEx(handle, reinterpret_cast<const wchar_t *>(rSubKey.utf16()),
nullptr, &dataType, nullptr, &dataSize);
return res == ERROR_SUCCESS ? dataType : 0;
}
// This is a regression test for QTBUG-13249, where QSettings was storing
@ -3652,29 +3652,12 @@ void tst_QSettings::consistentRegistryStorage()
QCOMPARE(settings1.value("quint64_value").toULongLong(), (quint64)1024);
settings1.sync();
HKEY handle;
LONG res;
QString keyName = "Software\\software.org\\KillerAPP";
res = RegOpenKeyEx(HKEY_CURRENT_USER, reinterpret_cast<const wchar_t *>(keyName.utf16()), 0, KEY_READ, &handle);
if (res == ERROR_SUCCESS)
{
DWORD dataType;
dataType = readKeyType(handle, QString("qint32_value"));
if (dataType != 0) {
QCOMPARE((int)REG_DWORD, (int)dataType);
}
dataType = readKeyType(handle, QString("quint32_value"));
if (dataType != 0) {
QCOMPARE((int)REG_DWORD, (int)dataType);
}
dataType = readKeyType(handle, QString("qint64_value"));
if (dataType != 0) {
QCOMPARE((int)REG_QWORD, (int)dataType);
}
dataType = readKeyType(handle, QString("quint64_value"));
if (dataType != 0) {
QCOMPARE((int)REG_QWORD, (int)dataType);
}
QWinRegistryKey handle(HKEY_CURRENT_USER, LR"(Software\software.org\KillerAPP)");
if (handle.isValid()) {
QCOMPARE(readKeyType(handle, L"qint32_value"), DWORD(REG_DWORD));
QCOMPARE(readKeyType(handle, L"quint32_value"), DWORD(REG_DWORD));
QCOMPARE(readKeyType(handle, L"qint64_value"), DWORD(REG_QWORD));
QCOMPARE(readKeyType(handle, L"quint64_value"), DWORD(REG_QWORD));
RegCloseKey(handle);
}
}