1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_widget_WinRegistry_h__
7 #define mozilla_widget_WinRegistry_h__
14 #include "mozilla/Maybe.h"
15 #include "mozilla/Span.h"
17 class nsISerialEventTarget
;
19 namespace mozilla::widget::WinRegistry
{
21 // According to MSDN, the following limits apply (in characters excluding room
22 // for terminating null character):
23 static constexpr size_t kMaxKeyNameLen
= 255;
24 static constexpr size_t kMaxValueNameLen
= 16383;
26 /// https://learn.microsoft.com/en-us/windows/win32/shell/regsam
27 enum class KeyMode
: uint32_t {
28 AllAccess
= KEY_ALL_ACCESS
,
29 QueryValue
= KEY_QUERY_VALUE
,
30 CreateLink
= KEY_CREATE_LINK
,
31 CreateSubKey
= KEY_CREATE_SUB_KEY
,
32 EnumerateSubkeys
= KEY_ENUMERATE_SUB_KEYS
,
33 Execute
= KEY_EXECUTE
,
36 SetValue
= KEY_SET_VALUE
,
40 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(KeyMode
);
42 enum class StringFlags
: uint32_t {
43 // Whether to allow REG_SZ strings.
45 // Whether to read EXPAND_SZ strings.
47 // Whether to treat MULTI_SZ values as strings. This is a historical
48 // idiosyncrasy of the nsIWindowsRegKey, but most likely not what you want.
49 LegacyMultiSz
= 1 << 2,
50 // Whether to expand environment variables in EXPAND_SZ values.
51 // Only makes sense along with the ExpandSz variable.
52 ExpandEnvironment
= 1 << 3,
53 // By default, only allow regular Sz, and don't perform environment expansion.
57 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(StringFlags
);
59 // Convenience alias for legacy WinUtils callers, to preserve behavior.
60 // Chances are users of these flags could just look at Sz strings, tho.
61 static constexpr auto kLegacyWinUtilsStringFlags
=
62 StringFlags::Sz
| StringFlags::ExpandSz
;
64 // https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-value-types
65 enum class ValueType
: uint32_t {
68 ExpandSz
= REG_EXPAND_SZ
,
70 MultiSz
= REG_MULTI_SZ
,
83 Key(const Key
&) = delete;
84 Key(Key
&& aOther
) { std::swap(mKey
, aOther
.mKey
); }
86 Key
& operator=(const Key
&) = delete;
87 Key
& operator=(Key
&& aOther
) {
88 std::swap(mKey
, aOther
.mKey
);
92 Key(HKEY aParent
, const nsString
& aPath
, KeyMode aMode
, CreateFlag
);
93 Key(HKEY aParent
, const nsString
& aPath
, KeyMode aMode
);
95 Key(const Key
& aParent
, const nsString
& aPath
, KeyMode aMode
, CreateFlag
)
96 : Key(aParent
.mKey
, aPath
, aMode
, Create
) {}
97 Key(const Key
& aParent
, const nsString
& aPath
, KeyMode aMode
)
98 : Key(aParent
.mKey
, aPath
, aMode
) {}
106 explicit operator bool() const { return !!mKey
; }
108 uint32_t GetChildCount() const;
109 [[nodiscard
]] bool GetChildName(uint32_t aIndex
, nsAString
& aResult
) const;
110 [[nodiscard
]] bool RemoveChildKey(const nsString
& aName
) const;
112 uint32_t GetValueCount() const;
113 [[nodiscard
]] bool GetValueName(uint32_t aIndex
, nsAString
& aResult
) const;
114 ValueType
GetValueType(const nsString
& aName
) const;
115 bool RemoveValue(const nsString
& aName
) const;
117 Maybe
<uint32_t> GetValueAsDword(const nsString
& aName
) const;
118 bool WriteValueAsDword(const nsString
& aName
, uint32_t aValue
);
120 Maybe
<uint64_t> GetValueAsQword(const nsString
& aName
) const;
121 [[nodiscard
]] bool WriteValueAsQword(const nsString
& aName
, uint64_t aValue
);
123 [[nodiscard
]] bool GetValueAsBinary(const nsString
& aName
,
124 nsTArray
<uint8_t>&) const;
125 Maybe
<nsTArray
<uint8_t>> GetValueAsBinary(const nsString
& aName
) const;
126 [[nodiscard
]] bool WriteValueAsBinary(const nsString
& aName
,
127 Span
<const uint8_t> aValue
);
129 [[nodiscard
]] bool GetValueAsString(const nsString
& aName
, nsString
& aResult
,
130 StringFlags
= StringFlags::Default
) const;
131 Maybe
<nsString
> GetValueAsString(const nsString
& aName
,
132 StringFlags
= StringFlags::Default
) const;
133 // Reads a string value into a buffer. Returns Some(length) if the string is
134 // read fully, in which case the passed memory region contains a
135 // null-terminated string.
136 // Doesn't perform environment expansion (and asserts if you pass the
137 // ExpandEnvironment flag).
138 [[nodiscard
]] Maybe
<uint32_t> GetValueAsString(
139 const nsString
& aName
, Span
<char16_t
>,
140 StringFlags
= StringFlags::Default
) const;
141 [[nodiscard
]] Maybe
<uint32_t> GetValueAsString(
142 const nsString
& aName
, Span
<wchar_t> aBuffer
,
143 StringFlags aFlags
= StringFlags::Default
) const {
144 return GetValueAsString(
145 aName
, Span
<char16_t
>((char16_t
*)aBuffer
.data(), aBuffer
.Length()),
149 [[nodiscard
]] bool WriteValueAsString(const nsString
& aName
,
150 const nsString
& aValue
) {
152 return SUCCEEDED(RegSetValueExW(mKey
, aName
.get(), 0, REG_SZ
,
153 (const BYTE
*)aValue
.get(),
154 (aValue
.Length() + 1) * sizeof(char16_t
)));
157 HKEY
RawKey() const { return mKey
; }
163 inline bool HasKey(HKEY aRootKey
, const nsString
& aKeyName
) {
164 return !!Key(aRootKey
, aKeyName
, KeyMode::Read
);
167 // Returns a single string value from the registry into a buffer.
168 [[nodiscard
]] inline bool GetString(HKEY aRootKey
, const nsString
& aKeyName
,
169 const nsString
& aValueName
,
170 Span
<char16_t
> aBuffer
,
171 StringFlags aFlags
= StringFlags::Default
) {
172 Key
k(aRootKey
, aKeyName
, KeyMode::QueryValue
);
173 return k
&& k
.GetValueAsString(aValueName
, aBuffer
, aFlags
);
175 [[nodiscard
]] inline bool GetString(HKEY aRootKey
, const nsString
& aKeyName
,
176 const nsString
& aValueName
,
177 Span
<wchar_t> aBuffer
,
178 StringFlags aFlags
= StringFlags::Default
) {
179 return GetString(aRootKey
, aKeyName
, aValueName
,
180 Span
<char16_t
>((char16_t
*)aBuffer
.data(), aBuffer
.Length()),
183 [[nodiscard
]] inline bool GetString(HKEY aRootKey
, const nsString
& aKeyName
,
184 const nsString
& aValueName
,
186 StringFlags aFlags
= StringFlags::Default
) {
187 Key
k(aRootKey
, aKeyName
, KeyMode::QueryValue
);
188 return k
&& k
.GetValueAsString(aValueName
, aBuffer
, aFlags
);
190 inline Maybe
<nsString
> GetString(HKEY aRootKey
, const nsString
& aKeyName
,
191 const nsString
& aValueName
,
192 StringFlags aFlags
= StringFlags::Default
) {
193 Key
k(aRootKey
, aKeyName
, KeyMode::QueryValue
);
197 return k
.GetValueAsString(aValueName
, aFlags
);
200 class KeyWatcher final
{
202 using Callback
= std::function
<void()>;
204 KeyWatcher(const KeyWatcher
&) = delete;
206 const Key
& GetKey() const { return mKey
; }
208 // Start watching a key. The watching is recursive (the whole key subtree is
209 // watched), and the callback is executed every time the key or any of its
210 // descendants change until the watcher is destroyed.
212 // @param aKey the key to watch. Must have been opened with the
213 // KeyMode::Notify flag.
214 // @param aTargetSerialEventTarget the target event target to dispatch the
216 // @param aCallback the closure to run every time that registry key changes.
217 KeyWatcher(Key
&& aKey
, nsISerialEventTarget
* aTargetSerialEventTarget
,
218 Callback
&& aCallback
);
223 static void CALLBACK
WatchCallback(void* aContext
, BOOLEAN
);
227 nsCOMPtr
<nsISerialEventTarget
> mEventTarget
;
229 HANDLE mEvent
= nullptr;
230 HANDLE mWaitObject
= nullptr;
233 } // namespace mozilla::widget::WinRegistry