From 589bfddc7c44a92e7e3f98a1e73871793c153ab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Mon, 10 Mar 2025 14:25:18 +0100 Subject: [PATCH] Teach QWinRegistryKey to observe changes to the key In some cases there are no explicit notification APIs for changes to system settings, and the developer is expected to observe changes to the registry keys directly. For example this applies to color profile associations, as documented in: https://learn.microsoft.com/en-us/windows/win32/wcs/wcs-registry-keys Change-Id: I3507058d90b1d32b8b5256f40861126277242cd6 Reviewed-by: Thiago Macieira --- src/corelib/kernel/qwinregistry.cpp | 48 ++++++++++++++++++- src/corelib/kernel/qwinregistry_p.h | 17 +++++-- .../qwinregistrykey/tst_qwinregistrykey.cpp | 23 +++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) diff --git a/src/corelib/kernel/qwinregistry.cpp b/src/corelib/kernel/qwinregistry.cpp index 760e2d4e8df..2bd1518e5be 100644 --- a/src/corelib/kernel/qwinregistry.cpp +++ b/src/corelib/kernel/qwinregistry.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include @@ -20,14 +22,17 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -QWinRegistryKey::QWinRegistryKey() +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) + REGSAM permissions, REGSAM access, + QObject *parent) + : QObject(parent) { if (RegOpenKeyExW(parentHandle, reinterpret_cast(subKey.utf16()), 0, permissions | access, &m_key) != ERROR_SUCCESS) { @@ -174,6 +179,45 @@ QString QWinRegistryKey::stringValue(QStringView subKey) const return value(subKey).value_or(QString()); } +void QWinRegistryKey::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 = new QWinEventNotifier(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(); + + return QObject::connectNotify(signal); +} + #ifndef QT_NO_DEBUG_STREAM QDebug operator<<(QDebug debug, const QWinRegistryKey &key) { diff --git a/src/corelib/kernel/qwinregistry_p.h b/src/corelib/kernel/qwinregistry_p.h index 4422b6a93b1..12f30d81374 100644 --- a/src/corelib/kernel/qwinregistry_p.h +++ b/src/corelib/kernel/qwinregistry_p.h @@ -20,17 +20,21 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE -class Q_CORE_EXPORT QWinRegistryKey +class Q_CORE_EXPORT QWinRegistryKey : public QObject { Q_DISABLE_COPY(QWinRegistryKey) + Q_OBJECT public: - QWinRegistryKey(); + QWinRegistryKey(QObject *parent = nullptr); explicit QWinRegistryKey(HKEY parentHandle, QStringView subKey, - REGSAM permissions = KEY_READ, REGSAM access = 0); + REGSAM permissions = KEY_READ, REGSAM access = 0, + QObject *parent = nullptr); ~QWinRegistryKey(); QWinRegistryKey(QWinRegistryKey &&other) noexcept @@ -64,8 +68,15 @@ public: friend Q_CORE_EXPORT QDebug operator<<(QDebug dbg, const QWinRegistryKey &); #endif +Q_SIGNALS: + void valueChanged(); + +protected: + void connectNotify(const QMetaMethod &signal) override; + private: HKEY m_key = nullptr; + QUniqueWin32NullHandle m_keyChangedEvent; }; QT_END_NAMESPACE diff --git a/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp b/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp index 2d11d5b31f4..8125851c531 100644 --- a/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp +++ b/tests/auto/corelib/kernel/qwinregistrykey/tst_qwinregistrykey.cpp @@ -99,6 +99,7 @@ private Q_SLOTS: void cleanupTestCase(); void qwinregistrykey(); void name(); + void valueChanged(); private: bool m_available = false; @@ -266,6 +267,28 @@ void tst_qwinregistrykey::name() } } +void tst_qwinregistrykey::valueChanged() +{ + if (!m_available) + QSKIP("The test data is not ready."); + + QWinRegistryKey testKey(HKEY_CURRENT_USER, TEST_KEY, KEY_READ | KEY_WRITE); + QVERIFY(testKey.isValid()); + + QVERIFY(write(testKey, u"valueThatCanChange", -1)); + + bool valueChanged = false; + QObject::connect(&testKey, &QWinRegistryKey::valueChanged, [&] { + valueChanged = true; + }); + + for (int i = 0; i < 10; ++i) { + valueChanged = false; + QVERIFY(write(testKey, u"valueThatCanChange", i)); + QTRY_VERIFY(valueChanged); + } +} + QTEST_MAIN(tst_qwinregistrykey) #include "tst_qwinregistrykey.moc"