123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 | // Copyright (C) 2019 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#define UMDF_USING_NTSTATUS// Avoid ntstatus redefinitions#include"qwinregistry_p.h"#include <QtCore/qvarlengtharray.h>#include <QtCore/qdebug.h>#include <QtCore/qendian.h>#include <QtCore/qlist.h>#include <QtCore/qwineventnotifier.h>#include <QtCore/private/qsystemerror_p.h>#include <ntstatus.h>// User mode version of ZwQueryKey, as per:// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwquerykeyextern"C" NTSTATUS NTSYSCALLAPI NTAPI NtQueryKey(HANDLE KeyHandle,int KeyInformationClass, PVOID KeyInformation, ULONG Length, PULONG ResultLength); QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;QWinRegistryKey::QWinRegistryKey(QObject *parent):QObject(parent){}// 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, QObject *parent):QObject(parent){if(RegOpenKeyExW(parentHandle,reinterpret_cast<const wchar_t*>(subKey.utf16()),0, permissions | access, &m_key) != ERROR_SUCCESS) { m_key =nullptr;}}QWinRegistryKey::~QWinRegistryKey(){close();}voidQWinRegistryKey::close(){if(isValid()) {RegCloseKey(m_key); m_key =nullptr;}} QString QWinRegistryKey::name()const{if(!isValid())return{};// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-_key_name_informationconstexprint kKeyNameInformation =3;struct KeyNameInformation { ULONG length =0; WCHAR name[1] = {}; };// Resolve name of key iteratively by first computing the needed size,// and then querying the name, accounting for the possibility that the// name length changes behind our back a few times. DWORD keyInformationSize =0;for(int i =0; i <5; ++i) { QByteArray buffer(keyInformationSize,0u);auto*keyNameInformation =reinterpret_cast<KeyNameInformation *>(buffer.data());switch(NtQueryKey(m_key, kKeyNameInformation, keyNameInformation, keyInformationSize, &keyInformationSize)) {case STATUS_BUFFER_TOO_SMALL:case STATUS_BUFFER_OVERFLOW:continue;case STATUS_SUCCESS: {returnQString::fromWCharArray(keyNameInformation->name, keyNameInformation->length /sizeof(wchar_t));}default:return{};}}return{};} QVariant QWinRegistryKey::value(QStringView subKey)const{// NOTE: Empty value name is allowed in Windows registry, it means the default// or unnamed value of a key, you can read/write/delete such value normally.if(!isValid())return{};// Use nullptr when we need to access the default value.constauto subKeyC = subKey.isEmpty() ?nullptr:reinterpret_cast<const wchar_t*>(subKey.utf16());// Get the size and type of the value. DWORD dataType = REG_NONE; DWORD dataSize =0; LONG ret =RegQueryValueExW(m_key, subKeyC,nullptr, &dataType,nullptr, &dataSize);if(ret != ERROR_SUCCESS)return{};// Workaround for rare cases where the trailing '\0' is missing.if(dataType == REG_SZ || dataType == REG_EXPAND_SZ) dataSize +=2;else if(dataType == REG_MULTI_SZ) dataSize +=4;// Get the value. QVarLengthArray<unsigned char>data(dataSize);std::fill(data.data(), data.data() + dataSize,0u); ret =RegQueryValueExW(m_key, subKeyC,nullptr,nullptr, data.data(), &dataSize);if(ret != ERROR_SUCCESS)return{};switch(dataType) {case REG_SZ:case REG_EXPAND_SZ: {if(dataSize >0) {returnQString::fromWCharArray(reinterpret_cast<const wchar_t*>(data.constData()));}returnQString();}case REG_MULTI_SZ: {if(dataSize >0) { QStringList list = {};int i =0;while(true) {const QString str =QString::fromWCharArray(reinterpret_cast<const wchar_t*>(data.constData()) + i); i += str.length() +1;if(str.isEmpty())break; list.append(str);}return list;}returnQStringList();}case REG_NONE:// No specific type, treat as binary data.case REG_BINARY: {if(dataSize >0) {returnQString::fromWCharArray(reinterpret_cast<const wchar_t*>(data.constData()), data.size() /2);}returnQString();}case REG_DWORD:// Same as REG_DWORD_LITTLE_ENDIANreturn qFromLittleEndian<quint32>(data.constData());case REG_DWORD_BIG_ENDIAN:return qFromBigEndian<quint32>(data.constData());case REG_QWORD:// Same as REG_QWORD_LITTLE_ENDIANreturn qFromLittleEndian<quint64>(data.constData());default:break;}return{};}// Returns the value of the specified subKey as a string, obtained using// qvariant_cast from the underlying QVariant. If that value is not a string,// or the subKey has no value, a string whose isNull() is true is returned.// Otherwise, the resulting string (which may be empty) is returned. QString QWinRegistryKey::stringValue(QStringView subKey)const{return value<QString>(subKey).value_or(QString());}voidQWinRegistryKey::connectNotify(const QMetaMethod &signal){if(signal !=QMetaMethod::fromSignal(&QWinRegistryKey::valueChanged))return;if(!isValid())return;if(m_keyChangedEvent)return; m_keyChangedEvent.reset(CreateEvent(nullptr,false,false,nullptr));auto*notifier =newQWinEventNotifier(m_keyChangedEvent.get(),this);auto registerForNotification = [this] {constexpr auto changeFilter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY;if(auto status =RegNotifyChangeKeyValue(m_key,true, changeFilter, m_keyChangedEvent.get(),true); status != ERROR_SUCCESS) {qWarning() <<"Failed to register notification for registry key"<<this<<"due to"<<QSystemError::windowsString(status);}};QObject::connect(notifier, &QWinEventNotifier::activated,this,[this, registerForNotification] { emit valueChanged();registerForNotification();});registerForNotification();returnQObject::connectNotify(signal);}#ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug,const QWinRegistryKey &key){ QDebugStateSaver saver(debug); debug.nospace(); debug <<"QWinRegistryKey(";if(key.isValid()) debug << key.name(); debug <<')';return debug;}#endif QT_END_NAMESPACE
|