зеркало из https://github.com/mozilla/gecko-dev.git
326 строки
10 KiB
C++
326 строки
10 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim:set ts=2 sts=2 sw=2 et cin: */
|
|
/* 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 "WinRegistry.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
namespace mozilla::widget::WinRegistry {
|
|
|
|
Key::Key(HKEY aParent, const nsString& aPath, KeyMode aMode, CreateFlag) {
|
|
MOZ_ASSERT(aParent);
|
|
DWORD disposition;
|
|
::RegCreateKeyExW(aParent, aPath.get(), 0, nullptr, REG_OPTION_NON_VOLATILE,
|
|
(REGSAM)aMode, nullptr, &mKey, &disposition);
|
|
}
|
|
|
|
Key::Key(HKEY aParent, const nsString& aPath, KeyMode aMode) {
|
|
MOZ_ASSERT(aParent);
|
|
::RegOpenKeyExW(aParent, aPath.get(), 0, (REGSAM)aMode, &mKey);
|
|
}
|
|
|
|
uint32_t Key::GetChildCount() const {
|
|
MOZ_ASSERT(mKey);
|
|
DWORD result = 0;
|
|
::RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &result, nullptr, nullptr,
|
|
nullptr, nullptr, nullptr, nullptr, nullptr);
|
|
return result;
|
|
}
|
|
|
|
bool Key::GetChildName(uint32_t aIndex, nsAString& aResult) const {
|
|
MOZ_ASSERT(mKey);
|
|
FILETIME lastWritten;
|
|
|
|
wchar_t nameBuf[kMaxKeyNameLen + 1];
|
|
DWORD nameLen = std::size(nameBuf);
|
|
|
|
LONG rv = RegEnumKeyExW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
|
|
nullptr, &lastWritten);
|
|
if (rv != ERROR_SUCCESS) {
|
|
return false;
|
|
}
|
|
aResult.Assign(nameBuf, nameLen);
|
|
return true;
|
|
}
|
|
|
|
bool Key::RemoveChildKey(const nsString& aName) const {
|
|
MOZ_ASSERT(mKey);
|
|
return SUCCEEDED(RegDeleteKeyW(mKey, aName.get()));
|
|
}
|
|
|
|
uint32_t Key::GetValueCount() const {
|
|
MOZ_ASSERT(mKey);
|
|
DWORD result = 0;
|
|
::RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
|
&result, nullptr, nullptr, nullptr, nullptr);
|
|
return result;
|
|
}
|
|
|
|
bool Key::GetValueName(uint32_t aIndex, nsAString& aResult) const {
|
|
MOZ_ASSERT(mKey);
|
|
wchar_t nameBuf[kMaxValueNameLen + 1];
|
|
DWORD nameLen = std::size(nameBuf);
|
|
|
|
LONG rv = RegEnumValueW(mKey, aIndex, nameBuf, &nameLen, nullptr, nullptr,
|
|
nullptr, nullptr);
|
|
if (rv != ERROR_SUCCESS) {
|
|
return false;
|
|
}
|
|
aResult.Assign(nameBuf, nameLen);
|
|
return true;
|
|
}
|
|
|
|
ValueType Key::GetValueType(const nsString& aName) const {
|
|
MOZ_ASSERT(mKey);
|
|
DWORD result;
|
|
LONG rv =
|
|
RegQueryValueExW(mKey, aName.get(), nullptr, &result, nullptr, nullptr);
|
|
return SUCCEEDED(rv) ? ValueType(result) : ValueType::None;
|
|
}
|
|
|
|
bool Key::RemoveValue(const nsString& aName) const {
|
|
MOZ_ASSERT(mKey);
|
|
return SUCCEEDED(RegDeleteValueW(mKey, aName.get()));
|
|
}
|
|
|
|
Maybe<uint32_t> Key::GetValueAsDword(const nsString& aName) const {
|
|
MOZ_ASSERT(mKey);
|
|
DWORD type;
|
|
DWORD value = 0;
|
|
DWORD size = sizeof(DWORD);
|
|
HRESULT rv = RegQueryValueExW(mKey, aName.get(), nullptr, &type,
|
|
(LPBYTE)&value, &size);
|
|
if (FAILED(rv) || type != REG_DWORD) {
|
|
return Nothing();
|
|
}
|
|
return Some(value);
|
|
}
|
|
|
|
bool Key::WriteValueAsDword(const nsString& aName, uint32_t aValue) {
|
|
MOZ_ASSERT(mKey);
|
|
return SUCCEEDED(RegSetValueExW(mKey, aName.get(), 0, REG_DWORD,
|
|
(const BYTE*)&aValue, sizeof(aValue)));
|
|
}
|
|
|
|
Maybe<uint64_t> Key::GetValueAsQword(const nsString& aName) const {
|
|
MOZ_ASSERT(mKey);
|
|
DWORD type;
|
|
uint64_t value = 0;
|
|
DWORD size = sizeof(uint64_t);
|
|
HRESULT rv = RegQueryValueExW(mKey, aName.get(), nullptr, &type,
|
|
(LPBYTE)&value, &size);
|
|
if (FAILED(rv) || type != REG_QWORD) {
|
|
return Nothing();
|
|
}
|
|
return Some(value);
|
|
}
|
|
|
|
bool Key::WriteValueAsQword(const nsString& aName, uint64_t aValue) {
|
|
MOZ_ASSERT(mKey);
|
|
return SUCCEEDED(RegSetValueExW(mKey, aName.get(), 0, REG_QWORD,
|
|
(const BYTE*)&aValue, sizeof(aValue)));
|
|
}
|
|
|
|
bool Key::GetValueAsBinary(const nsString& aName,
|
|
nsTArray<uint8_t>& aResult) const {
|
|
MOZ_ASSERT(mKey);
|
|
DWORD type;
|
|
DWORD size;
|
|
LONG rv = RegQueryValueExW(mKey, aName.get(), nullptr, &type, nullptr, &size);
|
|
if (FAILED(rv) || type != REG_BINARY) {
|
|
return false;
|
|
}
|
|
if (!aResult.SetLength(size, fallible)) {
|
|
return false;
|
|
}
|
|
rv = RegQueryValueExW(mKey, aName.get(), nullptr, nullptr, aResult.Elements(),
|
|
&size);
|
|
return SUCCEEDED(rv);
|
|
}
|
|
|
|
Maybe<nsTArray<uint8_t>> Key::GetValueAsBinary(const nsString& aName) const {
|
|
nsTArray<uint8_t> value;
|
|
Maybe<nsTArray<uint8_t>> result;
|
|
if (GetValueAsBinary(aName, value)) {
|
|
result.emplace(std::move(value));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool Key::WriteValueAsBinary(const nsString& aName,
|
|
Span<const uint8_t> aValue) {
|
|
MOZ_ASSERT(mKey);
|
|
return SUCCEEDED(RegSetValueExW(mKey, aName.get(), 0, REG_BINARY,
|
|
(const BYTE*)aValue.data(), aValue.size()));
|
|
}
|
|
|
|
static bool IsStringType(DWORD aType, StringFlags aFlags) {
|
|
switch (aType) {
|
|
case REG_SZ:
|
|
return bool(aFlags & StringFlags::Sz);
|
|
case REG_EXPAND_SZ:
|
|
return bool(aFlags & StringFlags::ExpandSz);
|
|
case REG_MULTI_SZ:
|
|
return bool(aFlags & StringFlags::LegacyMultiSz);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Key::GetValueAsString(const nsString& aName, nsString& aResult,
|
|
StringFlags aFlags) const {
|
|
MOZ_ASSERT(mKey);
|
|
DWORD type;
|
|
DWORD size;
|
|
LONG rv = RegQueryValueExW(mKey, aName.get(), nullptr, &type, nullptr, &size);
|
|
if (FAILED(rv) || !IsStringType(type, aFlags)) {
|
|
return false;
|
|
}
|
|
if (!size) {
|
|
aResult.Truncate();
|
|
return true;
|
|
}
|
|
// The buffer size must be a multiple of 2.
|
|
if (NS_WARN_IF(size % 2 != 0)) {
|
|
return false;
|
|
}
|
|
size_t resultLen = size / 2;
|
|
{
|
|
auto handleOrError =
|
|
aResult.BulkWrite(resultLen, 0, /* aAllowShrinking = */ false);
|
|
if (NS_WARN_IF(handleOrError.isErr())) {
|
|
return false;
|
|
}
|
|
auto handle = handleOrError.unwrap();
|
|
auto len = GetValueAsString(aName, {handle.Elements(), handle.Length() + 1},
|
|
aFlags & ~StringFlags::ExpandEnvironment);
|
|
if (NS_WARN_IF(!len)) {
|
|
return false;
|
|
}
|
|
handle.Finish(*len, /* aAllowShrinking = */ false);
|
|
if (*len && !aResult.CharAt(*len - 1)) {
|
|
// The string passed to us had a null terminator in the final
|
|
// position.
|
|
aResult.Truncate(*len - 1);
|
|
}
|
|
}
|
|
if (type == REG_EXPAND_SZ && (aFlags & StringFlags::ExpandEnvironment)) {
|
|
resultLen = ExpandEnvironmentStringsW(aResult.get(), nullptr, 0);
|
|
if (resultLen > 1) {
|
|
nsString expandedResult;
|
|
// |resultLen| includes the terminating null character
|
|
resultLen--;
|
|
if (!expandedResult.SetLength(resultLen, fallible)) {
|
|
return false;
|
|
}
|
|
resultLen = ExpandEnvironmentStringsW(aResult.get(), expandedResult.get(),
|
|
resultLen + 1);
|
|
if (resultLen <= 0) {
|
|
return false;
|
|
}
|
|
aResult = std::move(expandedResult);
|
|
} else if (resultLen == 1) {
|
|
// It apparently expands to nothing (just a null terminator).
|
|
resultLen = 0;
|
|
aResult.Truncate();
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Maybe<nsString> Key::GetValueAsString(const nsString& aName,
|
|
StringFlags aFlags) const {
|
|
nsString value;
|
|
Maybe<nsString> result;
|
|
if (GetValueAsString(aName, value, aFlags)) {
|
|
result.emplace(std::move(value));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Maybe<uint32_t> Key::GetValueAsString(const nsString& aName,
|
|
Span<char16_t> aBuffer,
|
|
StringFlags aFlags) const {
|
|
MOZ_ASSERT(mKey);
|
|
MOZ_ASSERT(aBuffer.Length(), "Empty buffer?");
|
|
MOZ_ASSERT(!(aFlags & StringFlags::ExpandEnvironment),
|
|
"Environment expansion not performed on a single buffer");
|
|
|
|
DWORD size = aBuffer.LengthBytes();
|
|
DWORD type;
|
|
HRESULT rv = RegQueryValueExW(mKey, aName.get(), nullptr, &type,
|
|
(LPBYTE)aBuffer.data(), &size);
|
|
if (FAILED(rv)) {
|
|
return Nothing();
|
|
}
|
|
if (!IsStringType(type, aFlags)) {
|
|
return Nothing();
|
|
}
|
|
uint32_t len = size ? size / sizeof(char16_t) - 1 : 0;
|
|
aBuffer[len] = 0;
|
|
return Some(len);
|
|
}
|
|
|
|
KeyWatcher::KeyWatcher(Key&& aKey,
|
|
nsISerialEventTarget* aTargetSerialEventTarget,
|
|
Callback&& aCallback)
|
|
: mKey(std::move(aKey)),
|
|
mEventTarget(aTargetSerialEventTarget),
|
|
mCallback(std::move(aCallback)) {
|
|
MOZ_ASSERT(mKey);
|
|
MOZ_ASSERT(mEventTarget);
|
|
MOZ_ASSERT(mCallback);
|
|
mEvent = CreateEvent(nullptr, /* bManualReset = */ FALSE,
|
|
/* bInitialState = */ FALSE, nullptr);
|
|
if (NS_WARN_IF(!mEvent)) {
|
|
return;
|
|
}
|
|
|
|
if (NS_WARN_IF(!Register())) {
|
|
return;
|
|
}
|
|
|
|
// The callback only dispatches to the relevant event target, so we can use
|
|
// WT_EXECUTEINWAITTHREAD.
|
|
RegisterWaitForSingleObject(&mWaitObject, mEvent, WatchCallback, this,
|
|
INFINITE, WT_EXECUTEINWAITTHREAD);
|
|
}
|
|
|
|
void KeyWatcher::WatchCallback(void* aContext, BOOLEAN) {
|
|
auto* watcher = static_cast<KeyWatcher*>(aContext);
|
|
watcher->Register();
|
|
watcher->mEventTarget->Dispatch(
|
|
NS_NewRunnableFunction("KeyWatcher callback", watcher->mCallback));
|
|
}
|
|
|
|
// As per the documentation in:
|
|
// https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regnotifychangekeyvalue
|
|
//
|
|
// This function detects a single change. After the caller receives a
|
|
// notification event, it should call the function again to receive the next
|
|
// notification.
|
|
bool KeyWatcher::Register() {
|
|
MOZ_ASSERT(mEvent);
|
|
DWORD flags = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
|
|
REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY |
|
|
REG_NOTIFY_THREAD_AGNOSTIC;
|
|
HRESULT rv =
|
|
RegNotifyChangeKeyValue(mKey.RawKey(), /* bWatchSubtree = */ TRUE, flags,
|
|
mEvent, /* fAsynchronous = */ TRUE);
|
|
return !NS_WARN_IF(FAILED(rv));
|
|
}
|
|
|
|
KeyWatcher::~KeyWatcher() {
|
|
if (mWaitObject) {
|
|
UnregisterWait(mWaitObject);
|
|
CloseHandle(mWaitObject);
|
|
}
|
|
if (mEvent) {
|
|
CloseHandle(mEvent);
|
|
}
|
|
}
|
|
|
|
} // namespace mozilla::widget::WinRegistry
|