/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the Qt Mobility Components. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qvaluespace_p.h" #include "qvaluespacepublisher.h" #include #include #include #include #include #include #ifdef Q_OS_WINCE #include #endif #include // Define win32 version to pull in RegisterWaitForSingleObject and UnregisterWait. #define _WIN32_WINNT 0x0500 #include #ifdef Q_OS_WINCE #include #define RegistryCallback REGISTRYNOTIFYCALLBACK #else #define RegistryCallback WAITORTIMERCALLBACK #endif QTM_BEGIN_NAMESPACE #ifdef Q_OS_WINCE class QWindowsCENotify : public QThread { Q_OBJECT public: QWindowsCENotify(QObject *parent = 0); ~QWindowsCENotify(); void addHandle(HANDLE handle); void removeHandle(HANDLE handle); protected: void run(); signals: void eventSignaled(void *handle); private: HANDLE wakeUp; QSet handles; }; QWindowsCENotify::QWindowsCENotify(QObject *parent) : QThread(parent) { wakeUp = CreateEvent(0, false, false, 0); } QWindowsCENotify::~QWindowsCENotify() { CloseHandle(wakeUp); wakeUp = INVALID_HANDLE_VALUE; wait(); } void QWindowsCENotify::addHandle(HANDLE handle) { handles.insert(handle); SetEvent(wakeUp); } void QWindowsCENotify::removeHandle(HANDLE handle) { handles.remove(handle); SetEvent(wakeUp); } void QWindowsCENotify::run() { while (wakeUp != INVALID_HANDLE_VALUE) { ResetEvent(wakeUp); QVector waitHandles = handles.toList().toVector(); waitHandles.append(wakeUp); DWORD index = WaitForMultipleObjects(waitHandles.count(), waitHandles.data(), false, INFINITE); if (index >= WAIT_OBJECT_0 && index < WAIT_OBJECT_0 + waitHandles.count()) { if (index - WAIT_OBJECT_0 != waitHandles.count() - 1) { handles.remove(waitHandles.at(index - WAIT_OBJECT_0)); emit eventSignaled(waitHandles.at(index - WAIT_OBJECT_0)); } } else if (index >= WAIT_ABANDONED_0 && index < WAIT_ABANDONED_0 + waitHandles.count()) { //qDebug() << "woke up because of abandoned index" << index - WAIT_ABANDONED_0; } else { qDebug() << "WaitForMultipleObjects failed with error" << GetLastError(); } } } #endif class RegistryLayer : public QAbstractValueSpaceLayer { Q_OBJECT public: RegistryLayer(const QString &basePath, bool volatileKeys, RegistryCallback callback); ~RegistryLayer(); /* Common functions */ bool startup(Type t); Handle item(Handle parent, const QString &path); void removeHandle(Handle handle); void setProperty(Handle handle, Properties); bool value(Handle handle, QVariant *data); bool value(Handle handle, const QString &subPath, QVariant *data); QSet children(Handle handle); /* QValueSpaceSubscriber functions */ bool supportsInterestNotification() const; bool notifyInterest(Handle handle, bool interested); /* QValueSpacePublisher functions */ bool setValue(QValueSpacePublisher *creator, Handle handle, const QVariant &data); bool setValue(QValueSpacePublisher *creator, Handle handle, const QString &path, const QVariant &data); bool removeValue(QValueSpacePublisher *creator, Handle handle, const QString &subPath); bool removeSubTree(QValueSpacePublisher *creator, Handle parent); void addWatch(QValueSpacePublisher *creator, Handle handle); void removeWatches(QValueSpacePublisher *creator, Handle parent); void sync(); public slots: void emitHandleChanged(void *hkey); #ifdef Q_OS_WINCE void eventSignaled(void *handle); #endif private: struct RegistryHandle { RegistryHandle(const QString &p) : path(p), valueHandle(false), refCount(1) { } QString path; bool valueHandle; unsigned int refCount; }; void openRegistryKey(RegistryHandle *handle); bool createRegistryKey(RegistryHandle *handle); bool removeRegistryValue(RegistryHandle *handle, const QString &path); void closeRegistryKey(RegistryHandle *handle); void pruneEmptyKeys(RegistryHandle *handle); QMutex localLock; QString m_basePath; bool m_volatileKeys; RegistryCallback m_callback; QHash handles; RegistryHandle *registryHandle(Handle handle) { if (handle == InvalidHandle) return 0; RegistryHandle *h = reinterpret_cast(handle); if (handles.values().contains(h)) return h; return 0; } QMap hKeys; QMultiMap notifyProxies; // MinGW complains about QPair<::HANDLE, ::HANDLE> typedef ::HANDLE HandleType; typedef QPair HandlePair; QMap waitHandles; #ifdef Q_OS_WINCE QWindowsCENotify *notifyThread; #endif QMap > creators; }; class VolatileRegistryLayer : public RegistryLayer { Q_OBJECT public: VolatileRegistryLayer(); ~VolatileRegistryLayer(); /* Common functions */ QString name(); QUuid id(); unsigned int order(); QValueSpace::LayerOptions layerOptions() const; static VolatileRegistryLayer *instance(); }; Q_GLOBAL_STATIC(VolatileRegistryLayer, volatileRegistryLayer); QVALUESPACE_AUTO_INSTALL_LAYER(VolatileRegistryLayer); class NonVolatileRegistryLayer : public RegistryLayer { Q_OBJECT public: NonVolatileRegistryLayer(); ~NonVolatileRegistryLayer(); /* Common functions */ QString name(); QUuid id(); unsigned int order(); QValueSpace::LayerOptions layerOptions() const; static NonVolatileRegistryLayer *instance(); }; Q_GLOBAL_STATIC(NonVolatileRegistryLayer, nonVolatileRegistryLayer); QVALUESPACE_AUTO_INSTALL_LAYER(NonVolatileRegistryLayer); #ifdef Q_OS_WINCE void qVolatileRegistryLayerCallback(HREGNOTIFY hNotify, DWORD dwUserData, const PBYTE pData, const UINT cbdata) { } void qNonVolatileRegistryLayerCallback(HREGNOTIFY hNotify, DWORD dwUserData, const PBYTE pData, const UINT cbdata) { } #else void CALLBACK qVolatileRegistryLayerCallback(PVOID lpParameter, BOOLEAN) { QMetaObject::invokeMethod(volatileRegistryLayer(), "emitHandleChanged", Qt::QueuedConnection, Q_ARG(void *, lpParameter)); } void CALLBACK qNonVolatileRegistryLayerCallback(PVOID lpParameter, BOOLEAN) { QMetaObject::invokeMethod(nonVolatileRegistryLayer(), "emitHandleChanged", Qt::QueuedConnection, Q_ARG(void *, lpParameter)); } #endif static QString qConvertPath(const QString &path) { QString fixedPath(path); while (fixedPath.endsWith(QLatin1Char('/'))) fixedPath.chop(1); fixedPath.replace(QLatin1Char('/'), QLatin1Char('\\')); return fixedPath; } VolatileRegistryLayer::VolatileRegistryLayer() : RegistryLayer(QLatin1String("Software/Nokia/QtMobility/volatileContext"), true, &qVolatileRegistryLayerCallback) { } VolatileRegistryLayer::~VolatileRegistryLayer() { } QString VolatileRegistryLayer::name() { return QLatin1String("Volatile Registry Layer"); } QUuid VolatileRegistryLayer::id() { return QVALUESPACE_VOLATILEREGISTRY_LAYER; } unsigned int VolatileRegistryLayer::order() { return 0x1000; } QValueSpace::LayerOptions VolatileRegistryLayer::layerOptions() const { return QValueSpace::TransientLayer | QValueSpace::WritableLayer; } VolatileRegistryLayer *VolatileRegistryLayer::instance() { return volatileRegistryLayer(); } NonVolatileRegistryLayer::NonVolatileRegistryLayer() : RegistryLayer(QLatin1String("Software/Nokia/QtMobility/nonVolatileContext"), false, &qNonVolatileRegistryLayerCallback) { } NonVolatileRegistryLayer::~NonVolatileRegistryLayer() { } QString NonVolatileRegistryLayer::name() { return QLatin1String("Non-Volatile Registry Layer"); } QUuid NonVolatileRegistryLayer::id() { return QVALUESPACE_NONVOLATILEREGISTRY_LAYER; } unsigned int NonVolatileRegistryLayer::order() { return 0; } QValueSpace::LayerOptions NonVolatileRegistryLayer::layerOptions() const { return QValueSpace::PermanentLayer | QValueSpace::WritableLayer; } NonVolatileRegistryLayer *NonVolatileRegistryLayer::instance() { return nonVolatileRegistryLayer(); } RegistryLayer::RegistryLayer(const QString &basePath, bool volatileKeys, RegistryCallback callback) : localLock(QMutex::Recursive), m_basePath(basePath), m_volatileKeys(volatileKeys), m_callback(callback) { // Ensure that the m_basePath key exists and is non-volatile. HKEY key; RegCreateKeyEx(HKEY_CURRENT_USER, reinterpret_cast(qConvertPath(m_basePath).utf16()), 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &key, 0); RegCloseKey(key); #ifdef Q_OS_WINCE notifyThread = new QWindowsCENotify(this); connect(notifyThread, SIGNAL(eventSignaled(void*)), this, SLOT(eventSignaled(void*)), Qt::QueuedConnection); notifyThread->start(); #endif } RegistryLayer::~RegistryLayer() { QMutableHashIterator i(handles); while (i.hasNext()) { i.next(); RegistryHandle *handle = i.value(); if (handle->valueHandle) removeHandle(Handle(handle)); } i.toFront(); while (i.hasNext()) { i.next(); removeHandle(Handle(i.value())); } } bool RegistryLayer::startup(Type) { return true; } bool RegistryLayer::value(Handle handle, QVariant *data) { QMutexLocker locker(&localLock); RegistryHandle *rh = registryHandle(handle); if (!rh) return false; return value(InvalidHandle, rh->path, data); } bool RegistryLayer::value(Handle handle, const QString &subPath, QVariant *data) { QMutexLocker locker(&localLock); if (handle != InvalidHandle && !registryHandle(handle)) return false; QString path(subPath); while (path.endsWith(QLatin1Char('/'))) path.chop(1); if (handle != InvalidHandle) while (path.startsWith(QLatin1Char('/'))) path = path.mid(1); Handle fullHandle = item(handle, subPath); if (fullHandle != InvalidHandle) { RegistryHandle *rh = registryHandle(fullHandle); if (rh) { openRegistryKey(rh); if (!rh->valueHandle) { handle = fullHandle; path.clear(); } } } int index = path.lastIndexOf(QLatin1Char('/'), -1); bool createdHandle = false; QString value; if (index == -1) { value = path; } else { // want a value that is in a sub path under handle value = path.mid(index + 1); path.truncate(index); if (path.isEmpty()) path = QLatin1String("/"); handle = item(handle, path); createdHandle = true; } RegistryHandle *rh = registryHandle(handle); openRegistryKey(rh); if (!hKeys.contains(rh)) { if (createdHandle) removeHandle(handle); return false; } HKEY key = hKeys.value(rh); DWORD regSize = 0; long result = RegQueryValueEx(key, reinterpret_cast(value.utf16()), 0, 0, 0, ®Size); if (result == ERROR_FILE_NOT_FOUND) { *data = QVariant(); if (createdHandle) removeHandle(handle); return false; } else if (result != ERROR_SUCCESS) { qDebug() << "RegQueryValueEx failed with error" << result; *data = QVariant(); if (createdHandle) removeHandle(handle); return false; } BYTE *regData = new BYTE[regSize]; DWORD regType; result = RegQueryValueEx(key, reinterpret_cast(value.utf16()), 0, ®Type, regData, ®Size); if (result != ERROR_SUCCESS) { qDebug() << "real RegQueryValueEx failed with error" << result; if (createdHandle) removeHandle(handle); return false; } switch (regType) { case REG_DWORD: *data = qVariantFromValue(*reinterpret_cast(regData)); break; case REG_QWORD: *data = qVariantFromValue(*reinterpret_cast(regData)); break; case REG_SZ: *data = qVariantFromValue(QString::fromWCharArray(reinterpret_cast(regData))); break; case REG_MULTI_SZ: { QStringList list; wchar_t *temp = reinterpret_cast(regData); while (*temp != L'\0') { QString string = QString::fromWCharArray(temp); list << string; temp += string.length() + 1; } *data = qVariantFromValue(list); break; } case REG_BINARY: *data = qVariantFromValue(QByteArray(reinterpret_cast(regData), regSize)); break; case REG_NONE: { QDataStream stream(QByteArray::fromRawData(reinterpret_cast(regData), regSize)); stream >> *data; break; } default: qDebug() << "Unknown REG type" << regType; delete[] regData; if (createdHandle) removeHandle(handle); return false; break; } delete[] regData; if (createdHandle) removeHandle(handle); return true; } #define MAX_KEY_LENGTH 255 #define MAX_NAME_LENGTH 16383 QSet RegistryLayer::children(Handle handle) { QMutexLocker locker(&localLock); QSet foundChildren; RegistryHandle *rh = registryHandle(handle); if (!rh) return foundChildren; openRegistryKey(rh); if (rh->valueHandle || !hKeys.contains(rh)) return foundChildren; HKEY key = hKeys.value(rh); int i = 0; long result; do { TCHAR subKey[MAX_KEY_LENGTH]; DWORD subKeySize = MAX_KEY_LENGTH; result = RegEnumKeyEx(key, i, subKey, &subKeySize, 0, 0, 0, 0); if (result == ERROR_KEY_DELETED) { QMetaObject::invokeMethod(this, "emitHandleChanged", Qt::QueuedConnection, Q_ARG(void *, key)); break; } if (result != ERROR_SUCCESS) break; foundChildren << QString::fromWCharArray(subKey, subKeySize); ++i; } while (result == ERROR_SUCCESS); i = 0; do { TCHAR valueName[MAX_NAME_LENGTH]; DWORD valueNameSize = MAX_NAME_LENGTH; result = RegEnumValue(key, i, valueName, &valueNameSize, 0, 0, 0, 0); if (result == ERROR_KEY_DELETED) { QMetaObject::invokeMethod(this, "emitHandleChanged", Qt::QueuedConnection, Q_ARG(void *, key)); break; } if (result != ERROR_SUCCESS) break; QString value = QString::fromWCharArray(valueName, valueNameSize); if (!value.isEmpty()) foundChildren << value; ++i; } while (result == ERROR_SUCCESS); return foundChildren; } QAbstractValueSpaceLayer::Handle RegistryLayer::item(Handle parent, const QString &path) { QMutexLocker locker(&localLock); QString fullPath; // Fail on invalid path. if (path.isEmpty() || path.contains(QLatin1String("//"))) return InvalidHandle; if (parent == InvalidHandle) { fullPath = path; } else { RegistryHandle *rh = registryHandle(parent); if (!rh) return InvalidHandle; if (path == QLatin1String("/")) { fullPath = rh->path; } else if (rh->path.endsWith(QLatin1Char('/')) && path.startsWith(QLatin1Char('/'))) fullPath = rh->path + path.mid(1); else if (!rh->path.endsWith(QLatin1Char('/')) && !path.startsWith(QLatin1Char('/'))) fullPath = rh->path + QLatin1Char('/') + path; else fullPath = rh->path + path; } if (handles.contains(fullPath)) { RegistryHandle *rh = handles.value(fullPath); ++rh->refCount; return Handle(rh); } // Create a new handle for path RegistryHandle *rh = new RegistryHandle(fullPath); handles.insert(fullPath, rh); return Handle(rh); } void RegistryLayer::setProperty(Handle handle, Properties properties) { QMutexLocker locker(&localLock); RegistryHandle *rh = registryHandle(handle); if (!rh) return; if (properties & QAbstractValueSpaceLayer::Publish) { // Enable change notification for handle openRegistryKey(rh); if (!hKeys.contains(rh)) { // The key does not exist. Temporarily use the parent key as a proxy. QString path = rh->path; while (!path.isEmpty()) { rh = registryHandle(item(InvalidHandle, path)); openRegistryKey(rh); if (hKeys.contains(rh)) { notifyProxies.insert(rh, registryHandle(handle)); break; } removeHandle(Handle(rh)); // root path doesn't exists if (path.length() == 1) return; int index = path.lastIndexOf(QLatin1Char('/')); if (index == 0) path.truncate(1); else path.truncate(index); } } HKEY key = hKeys.value(rh); if (waitHandles.contains(key)) return; #ifdef Q_OS_WINCE DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET; ::HANDLE event = CeFindFirstRegChange(key, true, filter); if (event == INVALID_HANDLE_VALUE) { qDebug() << "CeFindFirstRegChange failed with error" << GetLastError(); return; } notifyThread->addHandle(event); waitHandles.insert(key, QPair<::HANDLE, ::HANDLE>(event, INVALID_HANDLE_VALUE)); #else ::HANDLE event = CreateEvent(0, false, false, 0); if (event == 0) { qDebug() << "CreateEvent failed with error" << GetLastError(); return; } DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET; long result = RegNotifyChangeKeyValue(key, true, filter, event, true); if (result != ERROR_SUCCESS) { qDebug() << "RegNotifyChangeKeyValue failed with error" << result; return; } ::HANDLE waitHandle; if (!RegisterWaitForSingleObject(&waitHandle, event, m_callback, key, INFINITE, WT_EXECUTEDEFAULT)) { qDebug() << "RegisterWaitForSingleObject failed with error" << GetLastError(); return; } //waitHandles.insert(key, QPair<::HANDLE, ::HANDLE>(event, waitHandle)); waitHandles.insert(key, HandlePair(event, waitHandle)); #endif } if (!(properties & QAbstractValueSpaceLayer::Publish)) { // Disable change notification for handle if (!hKeys.contains(rh)) return; HKEY key = hKeys.value(rh); if (waitHandles.contains(key)) { //QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key); HandlePair wait = waitHandles.take(key); #ifdef Q_OS_WINCE notifyThread->removeHandle(wait.first); #else UnregisterWait(wait.second); #endif CloseHandle(wait.first); } } } void RegistryLayer::removeHandle(Handle handle) { QMutexLocker locker(&localLock); RegistryHandle *rh = registryHandle(handle); if (!rh) return; if (--rh->refCount) return; QList proxies = notifyProxies.keys(rh); while (!proxies.isEmpty()) { notifyProxies.remove(proxies.first(), rh); removeHandle(Handle(proxies.takeFirst())); } handles.remove(rh->path); closeRegistryKey(rh); delete rh; } void RegistryLayer::closeRegistryKey(RegistryHandle *handle) { QMutexLocker locker(&localLock); if (!hKeys.contains(handle)) return; HKEY key = hKeys.take(handle); // Check if other handles are using this registry key. if (!hKeys.keys(key).isEmpty()) return; if (waitHandles.contains(key)) { //QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key); HandlePair wait = waitHandles.take(key); #ifdef Q_OS_WINCE notifyThread->removeHandle(wait.first); #else UnregisterWait(wait.second); #endif CloseHandle(wait.first); } RegCloseKey(key); } static LONG qRegDeleteTree(HKEY hKey, LPCTSTR lpSubKey) { HKEY key; long result = RegOpenKeyEx(hKey, lpSubKey, 0, KEY_ALL_ACCESS, &key); if (result != ERROR_SUCCESS) { if (result == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; else return result; } do { TCHAR subKey[MAX_KEY_LENGTH]; DWORD subKeySize = MAX_KEY_LENGTH; result = RegEnumKeyEx(key, 0, subKey, &subKeySize, 0, 0, 0, 0); if (result == ERROR_NO_MORE_ITEMS) break; result = qRegDeleteTree(key, subKey); } while (result == ERROR_SUCCESS); RegCloseKey(key); if (result != ERROR_NO_MORE_ITEMS && result != ERROR_SUCCESS) return result; return RegDeleteKey(hKey, lpSubKey); } bool RegistryLayer::removeRegistryValue(RegistryHandle *handle, const QString &subPath) { QMutexLocker locker(&localLock); QString path(subPath); while (path.endsWith(QLatin1Char('/'))) path.chop(1); int index = path.lastIndexOf(QLatin1Char('/'), -1); bool createdHandle = false; RegistryHandle *rh; QString value; if (index == -1) { rh = handle; value = path; } else { // want a value that is in a sub path under handle value = path.mid(index + 1); path.truncate(index); if (path.isEmpty()) path.append(QLatin1Char('/')); rh = registryHandle(item(handle ? Handle(handle) : InvalidHandle, path)); createdHandle = true; } #ifdef Q_OS_WINCE QList deletedKeys; QString fullPath; if (rh && rh->path != QLatin1String("/")) fullPath = rh->path + QLatin1Char('/') + value; else fullPath = QLatin1Char('/') + value; foreach (RegistryHandle *h, hKeys.keys()) { if (h->path.startsWith(fullPath) && !h->valueHandle) { deletedKeys.append(h); closeRegistryKey(h); } } #endif openRegistryKey(rh); if (!hKeys.contains(rh)) { if (createdHandle) removeHandle(Handle(rh)); return false; } HKEY key = hKeys.value(rh); long result = RegDeleteValue(key, reinterpret_cast(value.utf16())); if (result == ERROR_FILE_NOT_FOUND) { result = qRegDeleteTree(key, reinterpret_cast(value.utf16())); if (result == ERROR_SUCCESS) { const QString rootPath = rh->path; QList paths = handles.keys(); while (!paths.isEmpty()) { QString p = paths.takeFirst(); if (p.startsWith(rootPath)) closeRegistryKey(handles.value(p)); } } if (result != ERROR_SUCCESS) qDebug() << "RegDeleteTree failed with error" << result; } else if (result != ERROR_SUCCESS) { qDebug() << "RegDeleteValue failed with error" << result; } #ifdef Q_OS_WINCE while (!deletedKeys.isEmpty()) emit handleChanged(Handle(deletedKeys.takeFirst())); #endif if (createdHandle) removeHandle(Handle(rh)); return result == ERROR_SUCCESS; } bool RegistryLayer::setValue(QValueSpacePublisher *creator, Handle handle, const QVariant &data) { return setValue(creator, handle, QString(), data); } bool RegistryLayer::setValue(QValueSpacePublisher *creator, Handle handle, const QString &subPath, const QVariant &data) { QMutexLocker locker(&localLock); RegistryHandle *rh = registryHandle(handle); if (!rh) return false; QString path(subPath); while (path.endsWith(QLatin1Char('/'))) path.chop(1); int index = path.lastIndexOf(QLatin1Char('/'), -1); bool createdHandle = false; QString value; if (index == -1) { value = path; } else { // want a value that is in a sub path under handle value = path.mid(index + 1); path.truncate(index); if (path.isEmpty()) path.append(QLatin1Char('/')); rh = registryHandle(item(Handle(rh), path)); createdHandle = true; } if (!value.isEmpty()) { QString fullPath = rh->path; if (!fullPath.endsWith(QLatin1Char('/'))) fullPath.append(QLatin1Char('/')); fullPath.append(value); RegistryHandle *fullHandle = registryHandle(item(InvalidHandle, fullPath)); if (fullHandle) { fullHandle->valueHandle = true; removeHandle(Handle(fullHandle)); } } if (createRegistryKey(rh)) { if (!creators[creator].contains(rh->path)) creators[creator].append(rh->path); } if (!hKeys.contains(rh)) { if (createdHandle) removeHandle(Handle(rh)); return false; } HKEY key = hKeys.value(rh); long result; switch (data.type()) { case QVariant::UInt: { DWORD temp = data.toUInt(); result = RegSetValueEx(key, reinterpret_cast(value.utf16()), 0, REG_DWORD, reinterpret_cast(&temp), sizeof(DWORD)); break; } case QVariant::ULongLong: { quint64 temp = data.toULongLong(); result = RegSetValueEx(key, reinterpret_cast(value.utf16()), 0, REG_QWORD, reinterpret_cast(&temp), sizeof(quint64)); break; } case QVariant::String: { // This may be wrong! QString tempString = data.toString(); int length = tempString.length() + 1; wchar_t *temp = new wchar_t[length]; tempString.toWCharArray(temp); temp[length - 1] = L'\0'; result = RegSetValueEx(key, reinterpret_cast(value.utf16()), 0, REG_SZ, reinterpret_cast(temp), length * sizeof(wchar_t)); delete[] temp; break; } case QVariant::StringList: { const QString joined = data.toStringList().join(QString(QLatin1Char('\0'))); int length = joined.length() + 2; wchar_t *temp = new wchar_t[length]; joined.toWCharArray(temp); temp[length - 2] = L'\0'; temp[length - 1] = L'\0'; result = RegSetValueEx(key, reinterpret_cast(value.utf16()), 0, REG_MULTI_SZ, reinterpret_cast(temp), length * sizeof(wchar_t)); delete[] temp; break; } case QVariant::ByteArray: { QByteArray temp = data.toByteArray(); result = RegSetValueEx(key, reinterpret_cast(value.utf16()), 0, REG_BINARY, reinterpret_cast(temp.constData()), temp.length()); break; } default: { QByteArray temp; QDataStream stream(&temp, QIODevice::WriteOnly | QIODevice::Truncate); stream << data; result = RegSetValueEx(key, reinterpret_cast(value.utf16()), 0, REG_NONE, reinterpret_cast(temp.constData()), temp.length()); break; } }; QString fullPath(rh->path); if (fullPath != QLatin1String("/")) fullPath.append(QLatin1Char('/')); fullPath.append(value); if (!creators[creator].contains(fullPath)) creators[creator].append(fullPath); if (createdHandle) removeHandle(Handle(rh)); return result == ERROR_SUCCESS; } void RegistryLayer::sync() { QMutexLocker locker(&localLock); // Wait for change notification callbacks before returning QEventLoop loop; connect(this, SIGNAL(handleChanged(quintptr)), &loop, SLOT(quit())); bool wait = false; QList keys = hKeys.values(); while (!keys.isEmpty()) { HKEY key = keys.takeFirst(); if (!wait && waitHandles.contains(key)) wait = true; RegFlushKey(key); } if (wait) { locker.unlock(); QTimer::singleShot(1000, &loop, SLOT(quit())); loop.exec(); } } void RegistryLayer::emitHandleChanged(void *k) { QMutexLocker locker(&localLock); HKEY key = reinterpret_cast(k); QList changedHandles = hKeys.keys(key); if (changedHandles.isEmpty()) { if (waitHandles.contains(key)) { //QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key); HandlePair wait = waitHandles.take(key); #ifdef Q_OS_WINCE notifyThread->removeHandle(wait.first); #else UnregisterWait(wait.second); #endif CloseHandle(wait.first); return; } } while (!changedHandles.isEmpty()) { RegistryHandle *handle = changedHandles.takeFirst(); emit handleChanged(Handle(handle)); // Emit signal for handles that this handle is proxying for. foreach (RegistryHandle *proxied, notifyProxies.values(handle)) { openRegistryKey(proxied); if (hKeys.contains(proxied)) { notifyProxies.remove(handle, proxied); removeHandle(Handle(handle)); setProperty(Handle(proxied), Publish); emit handleChanged(Handle(proxied)); } } } if (waitHandles.contains(key)) { //QPair<::HANDLE, ::HANDLE> wait = waitHandles.take(key); HandlePair wait = waitHandles.take(key); ::HANDLE event = wait.first; #ifdef Q_OS_WINCE notifyThread->removeHandle(event); #else UnregisterWait(wait.second); #endif #ifdef Q_OS_WINCE if (!CeFindNextRegChange(event)) { long result = GetLastError(); if (result == ERROR_KEY_DELETED) { CloseHandle(event); QList changedHandles = hKeys.keys(key); for (int i = 0; i < changedHandles.count(); ++i) hKeys.remove(changedHandles.at(i)); RegCloseKey(key); for (int i = 0; i < changedHandles.count(); ++i) setProperty(Handle(changedHandles.at(i)), Publish); } else { qDebug() << "CeFindNextRegChange failed with error" << result; } return; } notifyThread->addHandle(event); waitHandles.insert(key, QPair<::HANDLE, ::HANDLE>(event, INVALID_HANDLE_VALUE)); #else long result = RegNotifyChangeKeyValue(key, true, REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | REG_NOTIFY_CHANGE_LAST_SET, event, true); if (result != ERROR_SUCCESS) { if (result == ERROR_KEY_DELETED || result == ERROR_INVALID_PARAMETER) { CloseHandle(event); QList changedHandles = hKeys.keys(key); for (int i = 0; i < changedHandles.count(); ++i) hKeys.remove(changedHandles.at(i)); RegCloseKey(key); for (int i = 0; i < changedHandles.count(); ++i) setProperty(Handle(changedHandles.at(i)), Publish); } else { qDebug() << "RegNotifyChangeKeyValue failed with error" << result; } return; } ::HANDLE waitHandle; if (!RegisterWaitForSingleObject(&waitHandle, event, m_callback, key, INFINITE, WT_EXECUTEDEFAULT)) { qDebug() << "RegisterWaitForSingleObject failed with error" << GetLastError(); return; } //waitHandles.insert(key, QPair<::HANDLE, ::HANDLE>(event, waitHandle)); waitHandles.insert(key, HandlePair(event, waitHandle)); #endif } } #ifdef Q_OS_WINCE void RegistryLayer::eventSignaled(void *handle) { QMutexLocker locker(&localLock); HKEY key = waitHandles.key(QPair<::HANDLE, ::HANDLE>(handle, INVALID_HANDLE_VALUE), 0); if (key != 0) emitHandleChanged(key); } #endif void RegistryLayer::openRegistryKey(RegistryHandle *handle) { QMutexLocker locker(&localLock); if (!handle) return; // Check if HKEY for this handle already exists. if (hKeys.contains(handle)) return; const QString fullPath = qConvertPath(m_basePath + handle->path); // Attempt to open registry key path HKEY key; long result = RegOpenKeyEx(HKEY_CURRENT_USER, reinterpret_cast(fullPath.utf16()), 0, KEY_ALL_ACCESS, &key); if (result == ERROR_SUCCESS) { hKeys.insert(handle, key); } else if (result == ERROR_FILE_NOT_FOUND) { // Key for handle does not exist in Registry, check if it is a value handle. int index = handle->path.lastIndexOf(QLatin1Char('/'), -1); const QString parentPath = handle->path.left(index); const QString valueName = handle->path.mid(index + 1); RegistryHandle *parentHandle = registryHandle(item(InvalidHandle, parentPath)); if (!parentHandle) return; openRegistryKey(parentHandle); if (!hKeys.contains(parentHandle)) { removeHandle(Handle(parentHandle)); return; } // Check if value exists. if (!children(Handle(parentHandle)).contains(valueName)) { removeHandle(Handle(parentHandle)); return; } handle->valueHandle = true; hKeys.insert(handle, hKeys.value(parentHandle)); removeHandle(Handle(parentHandle)); } } bool RegistryLayer::createRegistryKey(RegistryHandle *handle) { QMutexLocker locker(&localLock); // Check if HKEY for this handle already exists. if (hKeys.contains(handle)) return false; const QString fullPath = qConvertPath(m_basePath + handle->path); // Attempt to open registry key path HKEY key; DWORD disposition; long result = RegCreateKeyEx(HKEY_CURRENT_USER, reinterpret_cast(fullPath.utf16()), 0, 0, (m_volatileKeys ? REG_OPTION_VOLATILE : REG_OPTION_NON_VOLATILE), KEY_ALL_ACCESS, 0, &key, &disposition); if (result == ERROR_SUCCESS) hKeys.insert(handle, key); return disposition == REG_CREATED_NEW_KEY; } bool RegistryLayer::removeSubTree(QValueSpacePublisher *creator, Handle handle) { QMutexLocker locker(&localLock); RegistryHandle *rh = registryHandle(handle); if (!rh) return false; QList paths = creators.value(creator); while (!paths.isEmpty()) { QString item = paths.takeFirst(); if (!item.startsWith(rh->path)) continue; removeRegistryValue(0, item); creators[creator].removeOne(item); int index = item.lastIndexOf(QLatin1Char('/')); if (index == -1) continue; item.truncate(index); if (!paths.contains(item)) paths.append(item); } pruneEmptyKeys(rh); return true; } void RegistryLayer::pruneEmptyKeys(RegistryHandle *handle) { QMutexLocker locker(&localLock); if (!children(Handle(handle)).isEmpty()) return; QString path = handle->path; while (path != QLatin1String("/")) { int index = path.lastIndexOf(QLatin1Char('/'), -1); QString value = path.mid(index + 1); path.truncate(index); if (path.isEmpty()) path.append(QLatin1Char('/')); RegistryHandle *rh = registryHandle(item(InvalidHandle, path)); openRegistryKey(rh); if (!hKeys.contains(rh)) { removeHandle(Handle(rh)); return; } HKEY key = hKeys.value(rh); long result = RegDeleteKey(key, reinterpret_cast(value.utf16())); if (result == ERROR_SUCCESS) { QList paths = handles.keys(); while (!paths.isEmpty()) { const QString p = paths.takeFirst(); if (p.startsWith(path)) closeRegistryKey(handles.value(p)); } } else if (result != ERROR_FILE_NOT_FOUND) { return; } bool hasChildren = !children(Handle(rh)).isEmpty(); removeHandle(Handle(rh)); if (hasChildren) break; } } bool RegistryLayer::removeValue(QValueSpacePublisher *creator, Handle handle, const QString &subPath) { QMutexLocker locker(&localLock); QString fullPath; if (handle == InvalidHandle) { fullPath = subPath; } else { RegistryHandle *rh = registryHandle(handle); if (!rh) return false; if (subPath == QLatin1String("/")) fullPath = rh->path; else if (rh->path.endsWith(QLatin1Char('/')) && subPath.startsWith(QLatin1Char('/'))) fullPath = rh->path + subPath.mid(1); else if (!rh->path.endsWith(QLatin1Char('/')) && !subPath.startsWith(QLatin1Char('/'))) fullPath = rh->path + QLatin1Char('/') + subPath; else fullPath = rh->path + subPath; } // permanent layer always removes items even if our records show that creator does not own it. if (!creators[creator].contains(fullPath) && (layerOptions() & QValueSpace::TransientLayer)) return false; removeRegistryValue(0, fullPath); creators[creator].removeOne(fullPath); return true; } void RegistryLayer::addWatch(QValueSpacePublisher *, Handle) { } void RegistryLayer::removeWatches(QValueSpacePublisher *, Handle) { } bool RegistryLayer::supportsInterestNotification() const { return false; } bool RegistryLayer::notifyInterest(Handle, bool) { return false; } #include QTM_END_NAMESPACE