diff options
Diffstat (limited to '')
-rw-r--r-- | xpcom/ds/nsWindowsRegKey.cpp | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/xpcom/ds/nsWindowsRegKey.cpp b/xpcom/ds/nsWindowsRegKey.cpp new file mode 100644 index 0000000000..73283de12f --- /dev/null +++ b/xpcom/ds/nsWindowsRegKey.cpp @@ -0,0 +1,528 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <windows.h> +#include <shlwapi.h> +#include <stdlib.h> +#include "nsWindowsRegKey.h" +#include "nsString.h" +#include "nsCOMPtr.h" +#include "mozilla/Attributes.h" + +//----------------------------------------------------------------------------- + +// According to MSDN, the following limits apply (in characters excluding room +// for terminating null character): +#define MAX_KEY_NAME_LEN 255 +#define MAX_VALUE_NAME_LEN 16383 + +class nsWindowsRegKey final : public nsIWindowsRegKey { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIWINDOWSREGKEY + + nsWindowsRegKey() + : mKey(nullptr), mWatchEvent(nullptr), mWatchRecursive(FALSE) {} + + private: + ~nsWindowsRegKey() { Close(); } + + HKEY mKey; + HANDLE mWatchEvent; + BOOL mWatchRecursive; +}; + +NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey) + +NS_IMETHODIMP +nsWindowsRegKey::GetKey(HKEY* aKey) { + *aKey = mKey; + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::SetKey(HKEY aKey) { + // We do not close the older aKey! + StopWatching(); + + mKey = aKey; + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::Close() { + StopWatching(); + + if (mKey) { + RegCloseKey(mKey); + mKey = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::Open(uint32_t aRootKey, const nsAString& aPath, + uint32_t aMode) { + Close(); + + LONG rv = + RegOpenKeyExW((HKEY)(intptr_t)aRootKey, PromiseFlatString(aPath).get(), 0, + (REGSAM)aMode, &mKey); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::Create(uint32_t aRootKey, const nsAString& aPath, + uint32_t aMode) { + Close(); + + DWORD disposition; + LONG rv = RegCreateKeyExW( + (HKEY)(intptr_t)aRootKey, PromiseFlatString(aPath).get(), 0, nullptr, + REG_OPTION_NON_VOLATILE, (REGSAM)aMode, nullptr, &mKey, &disposition); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::OpenChild(const nsAString& aPath, uint32_t aMode, + nsIWindowsRegKey** aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey(); + + nsresult rv = child->Open((uintptr_t)mKey, aPath, aMode); + if (NS_FAILED(rv)) { + return rv; + } + + child.swap(*aResult); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::CreateChild(const nsAString& aPath, uint32_t aMode, + nsIWindowsRegKey** aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey(); + + nsresult rv = child->Create((uintptr_t)mKey, aPath, aMode); + if (NS_FAILED(rv)) { + return rv; + } + + child.swap(*aResult); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::GetChildCount(uint32_t* aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + DWORD numSubKeys; + LONG rv = + RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys, nullptr, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + if (rv != ERROR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + *aResult = numSubKeys; + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::GetChildName(uint32_t aIndex, nsAString& aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + FILETIME lastWritten; + + wchar_t nameBuf[MAX_KEY_NAME_LEN + 1]; + DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]); + + LONG rv = RegEnumKeyExW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr, + nullptr, &lastWritten); + if (rv != ERROR_SUCCESS) { + return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here? + } + + aResult.Assign(nameBuf, nameLen); + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::HasChild(const nsAString& aName, bool* aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + // Check for the existence of a child key by opening the key with minimal + // rights. Perhaps there is a more efficient way to do this? + + HKEY key; + LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(aName).get(), 0, + STANDARD_RIGHTS_READ, &key); + + if ((*aResult = (rv == ERROR_SUCCESS && key))) { + RegCloseKey(key); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::GetValueCount(uint32_t* aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + DWORD numValues; + LONG rv = + RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr, &numValues, nullptr, nullptr, nullptr, nullptr); + if (rv != ERROR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + *aResult = numValues; + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::GetValueName(uint32_t aIndex, nsAString& aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + wchar_t nameBuf[MAX_VALUE_NAME_LEN]; + DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]); + + LONG rv = RegEnumValueW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr, + nullptr, nullptr); + if (rv != ERROR_SUCCESS) { + return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here? + } + + aResult.Assign(nameBuf, nameLen); + + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::HasValue(const nsAString& aName, bool* aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr, + nullptr, nullptr); + + *aResult = (rv == ERROR_SUCCESS); + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::RemoveChild(const nsAString& aName) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(aName).get()); + + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::RemoveValue(const nsAString& aName) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + LONG rv = RegDeleteValueW(mKey, PromiseFlatString(aName).get()); + + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::GetValueType(const nsAString& aName, uint32_t* aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, + (LPDWORD)aResult, nullptr, nullptr); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::ReadStringValue(const nsAString& aName, nsAString& aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + DWORD type, size; + + const nsString& flatName = PromiseFlatString(aName); + + LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size); + if (rv != ERROR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + // This must be a string type in order to fetch the value as a string. + // We're being a bit forgiving here by allowing types other than REG_SZ. + if (type != REG_SZ && type != REG_EXPAND_SZ && type != REG_MULTI_SZ) { + return NS_ERROR_FAILURE; + } + + // The buffer size must be a multiple of 2. + if (size % 2 != 0) { + return NS_ERROR_UNEXPECTED; + } + + if (size == 0) { + aResult.Truncate(); + return NS_OK; + } + + // |size| may or may not include the terminating null character. + DWORD resultLen = size / 2; + + if (!aResult.SetLength(resultLen, mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + auto begin = aResult.BeginWriting(); + + rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE)begin, &size); + + if (!aResult.CharAt(resultLen - 1)) { + // The string passed to us had a null terminator in the final position. + aResult.Truncate(resultLen - 1); + } + + // Expand the environment variables if needed + if (type == REG_EXPAND_SZ) { + const nsString& flatSource = PromiseFlatString(aResult); + resultLen = ExpandEnvironmentStringsW(flatSource.get(), nullptr, 0); + if (resultLen > 1) { + nsAutoString expandedResult; + // |resultLen| includes the terminating null character + --resultLen; + if (!expandedResult.SetLength(resultLen, mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + resultLen = ExpandEnvironmentStringsW( + flatSource.get(), expandedResult.get(), resultLen + 1); + if (resultLen <= 0) { + rv = ERROR_UNKNOWN_FEATURE; + aResult.Truncate(); + } else { + rv = ERROR_SUCCESS; + aResult = expandedResult; + } + } else if (resultLen == 1) { + // It apparently expands to nothing (just a null terminator). + aResult.Truncate(); + } + } + + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::ReadIntValue(const nsAString& aName, uint32_t* aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + DWORD size = sizeof(*aResult); + LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr, + (LPBYTE)aResult, &size); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::ReadInt64Value(const nsAString& aName, uint64_t* aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + DWORD size = sizeof(*aResult); + LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr, + (LPBYTE)aResult, &size); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::ReadBinaryValue(const nsAString& aName, nsACString& aResult) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + DWORD size; + LONG rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr, + nullptr, &size); + + if (rv != ERROR_SUCCESS) { + return NS_ERROR_FAILURE; + } + + if (!size) { + aResult.Truncate(); + return NS_OK; + } + + if (!aResult.SetLength(size, mozilla::fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + auto begin = aResult.BeginWriting(); + + rv = RegQueryValueExW(mKey, PromiseFlatString(aName).get(), 0, nullptr, + (LPBYTE)begin, &size); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::WriteStringValue(const nsAString& aName, + const nsAString& aValue) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + // Need to indicate complete size of buffer including null terminator. + const nsString& flatValue = PromiseFlatString(aValue); + + LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_SZ, + (const BYTE*)flatValue.get(), + (flatValue.Length() + 1) * sizeof(char16_t)); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::WriteIntValue(const nsAString& aName, uint32_t aValue) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_DWORD, + (const BYTE*)&aValue, sizeof(aValue)); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::WriteInt64Value(const nsAString& aName, uint64_t aValue) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_QWORD, + (const BYTE*)&aValue, sizeof(aValue)); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::WriteBinaryValue(const nsAString& aName, + const nsACString& aValue) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + const nsCString& flatValue = PromiseFlatCString(aValue); + LONG rv = RegSetValueExW(mKey, PromiseFlatString(aName).get(), 0, REG_BINARY, + (const BYTE*)flatValue.get(), flatValue.Length()); + return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsWindowsRegKey::StartWatching(bool aRecurse) { + if (NS_WARN_IF(!mKey)) { + return NS_ERROR_NOT_INITIALIZED; + } + + if (mWatchEvent) { + return NS_OK; + } + + mWatchEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr); + if (!mWatchEvent) { + return NS_ERROR_OUT_OF_MEMORY; + } + + DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | + REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY; + + LONG rv = RegNotifyChangeKeyValue(mKey, aRecurse, filter, mWatchEvent, TRUE); + if (rv != ERROR_SUCCESS) { + StopWatching(); + // On older versions of Windows, this call is not implemented, so simply + // return NS_OK in those cases and pretend that the watching is happening. + return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE; + } + + mWatchRecursive = aRecurse; + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::StopWatching() { + if (mWatchEvent) { + CloseHandle(mWatchEvent); + mWatchEvent = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::HasChanged(bool* aResult) { + if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) { + // An event only gets signaled once, then it's done, so we have to set up + // another event to watch. + StopWatching(); + StartWatching(mWatchRecursive); + *aResult = true; + } else { + *aResult = false; + } + return NS_OK; +} + +NS_IMETHODIMP +nsWindowsRegKey::IsWatching(bool* aResult) { + *aResult = (mWatchEvent != nullptr); + return NS_OK; +} + +//----------------------------------------------------------------------------- + +void NS_NewWindowsRegKey(nsIWindowsRegKey** aResult) { + RefPtr<nsWindowsRegKey> key = new nsWindowsRegKey(); + key.forget(aResult); +} + +//----------------------------------------------------------------------------- + +nsresult nsWindowsRegKeyConstructor(nsISupports* aDelegate, const nsIID& aIID, + void** aResult) { + if (aDelegate) { + return NS_ERROR_NO_AGGREGATION; + } + + nsCOMPtr<nsIWindowsRegKey> key; + NS_NewWindowsRegKey(getter_AddRefs(key)); + return key->QueryInterface(aIID, aResult); +} |