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"