From 08e1fbbbfa8760e94922d69ab7397af64370081a Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Mon, 18 Feb 2019 14:22:39 +0100 Subject: [PATCH] qmake: add $$read_registry() function Fixes: QTCREATORBUG-21973 Written-by: Oswald Buddenhagen Reviewed-by: Simon Hausmann (cherry-picked from qtbase/f89ac0101ad4a6cb5339a3bfe132aad897eafc9d) Change-Id: I6f67eec7c715686074dd3bdc121170c26daf56da Reviewed-by: Joerg Bornemann --- src/plugins/qtsupport/qtsupport.qbs | 6 + src/shared/proparser/proparser.pri | 2 + src/shared/proparser/qmakebuiltins.cpp | 42 +++++- src/shared/proparser/registry.cpp | 158 +++++++++++++++++++++ src/shared/proparser/registry_p.h | 55 +++++++ tests/auto/profilewriter/profilewriter.qbs | 7 +- 6 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 src/shared/proparser/registry.cpp create mode 100644 src/shared/proparser/registry_p.h diff --git a/src/plugins/qtsupport/qtsupport.qbs b/src/plugins/qtsupport/qtsupport.qbs index d4a140b2aee..8be90315eb3 100644 --- a/src/plugins/qtsupport/qtsupport.qbs +++ b/src/plugins/qtsupport/qtsupport.qbs @@ -19,6 +19,10 @@ Project { "QMAKE_LIBRARY", "QMAKE_BUILTIN_PRFS", ]) + Properties { + condition: qbs.targetOS.contains("windows") + cpp.dynamicLibraries: "advapi32" + } Export { Depends { name: "ProParser" } @@ -48,6 +52,8 @@ Project { "qmakeparser.h", "qmakevfs.cpp", "qmakevfs.h", + "registry.cpp", + "registry_p.h", ] } diff --git a/src/shared/proparser/proparser.pri b/src/shared/proparser/proparser.pri index 53ab86cf575..5f02416ef7d 100644 --- a/src/shared/proparser/proparser.pri +++ b/src/shared/proparser/proparser.pri @@ -15,6 +15,7 @@ HEADERS += \ proitems.h \ prowriter.h \ qmakevfs.h \ + registry_p.h \ ioutils.h SOURCES += \ @@ -26,6 +27,7 @@ SOURCES += \ proitems.cpp \ prowriter.cpp \ qmakevfs.cpp \ + registry.cpp \ ioutils.cpp RESOURCES += proparser.qrc diff --git a/src/shared/proparser/qmakebuiltins.cpp b/src/shared/proparser/qmakebuiltins.cpp index 096524e6dcd..ea7e308cc50 100644 --- a/src/shared/proparser/qmakebuiltins.cpp +++ b/src/shared/proparser/qmakebuiltins.cpp @@ -31,6 +31,10 @@ #include "qmakevfs.h" #include "ioutils.h" +#ifdef Q_OS_WIN +# include "registry_p.h" +#endif + #include #include #include @@ -88,7 +92,7 @@ enum ExpandFunc { E_UPPER, E_LOWER, E_TITLE, E_FILES, E_PROMPT, E_RE_ESCAPE, E_VAL_ESCAPE, E_REPLACE, E_SORT_DEPENDS, E_RESOLVE_DEPENDS, E_ENUMERATE_VARS, E_SHADOWED, E_ABSOLUTE_PATH, E_RELATIVE_PATH, E_CLEAN_PATH, - E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE, E_GETENV + E_SYSTEM_PATH, E_SHELL_PATH, E_SYSTEM_QUOTE, E_SHELL_QUOTE, E_GETENV, E_READ_REGISTRY, }; enum TestFunc { @@ -153,6 +157,7 @@ void QMakeEvaluator::initFunctionStatics() { "system_quote", E_SYSTEM_QUOTE }, { "shell_quote", E_SHELL_QUOTE }, { "getenv", E_GETENV }, + { "read_registry", E_READ_REGISTRY }, }; statics.expands.reserve((int)(sizeof(expandInits)/sizeof(expandInits[0]))); for (unsigned i = 0; i < sizeof(expandInits)/sizeof(expandInits[0]); ++i) @@ -1265,6 +1270,41 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateBuiltinExpand( ret << val; } break; +#ifdef Q_OS_WIN + case E_READ_REGISTRY: { + HKEY tree; + const auto par = args.at(0); + if (!par.compare(QLatin1String("HKCU"), Qt::CaseInsensitive) + || !par.compare(QLatin1String("HKEY_CURRENT_USER"), Qt::CaseInsensitive)) { + tree = HKEY_CURRENT_USER; + } else if (!par.compare(QLatin1String("HKLM"), Qt::CaseInsensitive) + || !par.compare(QLatin1String("HKEY_LOCAL_MACHINE"), Qt::CaseInsensitive)) { + tree = HKEY_LOCAL_MACHINE; + } else { + evalError(fL1S("read_registry(): invalid or unsupported registry tree %1.") + .arg(par.toQString())); + goto rrfail; + } + int flags = 0; + if (args.count() > 2) { + const auto opt = args.at(2); + if (opt == "32" + || !opt.compare(QLatin1String("wow64_32key"), Qt::CaseInsensitive)) { + flags = KEY_WOW64_32KEY; + } else if (opt == "64" + || !opt.compare(QLatin1String("wow64_64key"), Qt::CaseInsensitive)) { + flags = KEY_WOW64_64KEY; + } else { + evalError(fL1S("read_registry(): invalid option %1.") + .arg(opt.toQString())); + goto rrfail; + } + } + ret << ProString(qt_readRegistryKey(tree, args.at(1).toQString(m_tmp1), flags)); + } + rrfail: + break; +#endif default: evalError(fL1S("Function '%1' is not implemented.").arg(func.toQString(m_tmp1))); break; diff --git a/src/shared/proparser/registry.cpp b/src/shared/proparser/registry.cpp new file mode 100644 index 00000000000..960b6f8e580 --- /dev/null +++ b/src/shared/proparser/registry.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the qmake application 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 +#include "registry_p.h" + +namespace QMakeInternal { + +#ifdef Q_OS_WIN32 +/* + Returns the path part of a registry key. + e.g. + For a key + "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir" + it returns + "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\" +*/ +static QString keyPath(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + if (idx == -1) + return QString(); + return rKey.left(idx + 1); +} + +/* + Returns the name part of a registry key. + e.g. + For a key + "Software\\Microsoft\\VisualStudio\\8.0\\Setup\\VC\\ProductDir" + it returns + "ProductDir" +*/ +static QString keyName(const QString &rKey) +{ + int idx = rKey.lastIndexOf(QLatin1Char('\\')); + if (idx == -1) + return rKey; + + QString res(rKey.mid(idx + 1)); + if (res == QLatin1String("Default") || res == QLatin1String(".")) + res = QString(); + return res; +} +#endif + +QString qt_readRegistryKey(HKEY parentHandle, const QString &rSubkey, unsigned long options) +{ + QString result; + +#ifdef Q_OS_WIN32 + QString rSubkeyName = keyName(rSubkey); + QString rSubkeyPath = keyPath(rSubkey); + + HKEY handle = nullptr; + LONG res = RegOpenKeyEx(parentHandle, (wchar_t*)rSubkeyPath.utf16(), 0, + KEY_READ | options, &handle); + + if (res != ERROR_SUCCESS) + return QString(); + + // get the size and type of the value + DWORD dataType; + DWORD dataSize; + res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), nullptr, &dataType, nullptr, &dataSize); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return QString(); + } + + // get the value + QByteArray data(dataSize, 0); + res = RegQueryValueEx(handle, (wchar_t*)rSubkeyName.utf16(), nullptr, nullptr, + reinterpret_cast(data.data()), &dataSize); + if (res != ERROR_SUCCESS) { + RegCloseKey(handle); + return QString(); + } + + switch (dataType) { + case REG_EXPAND_SZ: + case REG_SZ: { + result = QString::fromWCharArray(((const wchar_t *)data.constData())); + break; + } + + case REG_MULTI_SZ: { + QStringList l; + int i = 0; + for (;;) { + QString s = QString::fromWCharArray((const wchar_t *)data.constData() + i); + i += s.length() + 1; + + if (s.isEmpty()) + break; + l.append(s); + } + result = l.join(QLatin1String(", ")); + break; + } + + case REG_NONE: + case REG_BINARY: { + result = QString::fromWCharArray((const wchar_t *)data.constData(), data.size() / 2); + break; + } + + case REG_DWORD_BIG_ENDIAN: + case REG_DWORD: { + Q_ASSERT(data.size() == sizeof(int)); + int i; + memcpy((char*)&i, data.constData(), sizeof(int)); + result = QString::number(i); + break; + } + + default: + qWarning("QSettings: unknown data %u type in windows registry", quint32(dataType)); + break; + } + + RegCloseKey(handle); +#else + Q_UNUSED(parentHandle); + Q_UNUSED(rSubkey) + Q_UNUSED(options); +#endif + + return result; +} + +} // namespace QMakeInternal + diff --git a/src/shared/proparser/registry_p.h b/src/shared/proparser/registry_p.h new file mode 100644 index 00000000000..8defcbae959 --- /dev/null +++ b/src/shared/proparser/registry_p.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** 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. +** +****************************************************************************/ + +#pragma once + +#include + +#ifdef Q_OS_WIN32 + #include +#else + typedef void* HKEY; +#endif + +#include + +namespace QMakeInternal { + +/** + * Read a value from the Windows registry. + * + * If the key is not found, or the registry cannot be accessed (for example + * if this code is compiled for a platform other than Windows), a null + * string is returned. + * + * 32-bit code reads from the registry's 32 bit view (Wow6432Node), + * 64 bit code reads from the 64 bit view. + * Pass KEY_WOW64_32KEY to access the 32 bit view regardless of the + * application's architecture, KEY_WOW64_64KEY respectively. + */ +QString qt_readRegistryKey(HKEY parentHandle, const QString &rSubkey, + unsigned long options = 0); + +} // namespace QMakeInternal diff --git a/tests/auto/profilewriter/profilewriter.qbs b/tests/auto/profilewriter/profilewriter.qbs index 7091472f308..a6fb8ca8794 100644 --- a/tests/auto/profilewriter/profilewriter.qbs +++ b/tests/auto/profilewriter/profilewriter.qbs @@ -18,7 +18,8 @@ QtcAutotest { "qmakeevaluator.h", "qmakeevaluator_p.h", "qmakeevaluator.cpp", "qmakeglobals.h", "qmakeglobals.cpp", "qmakeparser.h", "qmakeparser.cpp", - "qmakevfs.h", "qmakevfs.cpp" + "qmakevfs.h", "qmakevfs.cpp", + "registry_p.h", "registry.cpp", ] } Group { @@ -27,4 +28,8 @@ QtcAutotest { } cpp.includePaths: base.concat([proParserGroup.prefix]) cpp.defines: base.concat("QT_USE_FAST_OPERATOR_PLUS") + Properties { + condition: qbs.targetOS.contains("windows") + cpp.dynamicLibraries: "advapi32" + } }