1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "nsWindowsRegKey.h"
13 #include "mozilla/Attributes.h"
14 #include "nsAutoPtr.h"
16 //-----------------------------------------------------------------------------
18 // According to MSDN, the following limits apply (in characters excluding room
19 // for terminating null character):
20 #define MAX_KEY_NAME_LEN 255
21 #define MAX_VALUE_NAME_LEN 16383
23 class nsWindowsRegKey MOZ_FINAL
: public nsIWindowsRegKey
27 NS_DECL_NSIWINDOWSREGKEY
31 , mWatchEvent(nullptr)
32 , mWatchRecursive(FALSE
)
47 NS_IMPL_ISUPPORTS1(nsWindowsRegKey
, nsIWindowsRegKey
)
50 nsWindowsRegKey::GetKey(HKEY
*key
)
57 nsWindowsRegKey::SetKey(HKEY key
)
59 // We do not close the older key!
67 nsWindowsRegKey::Close()
79 nsWindowsRegKey::Open(uint32_t rootKey
, const nsAString
&path
, uint32_t mode
)
83 LONG rv
= RegOpenKeyExW((HKEY
) rootKey
, PromiseFlatString(path
).get(), 0,
84 (REGSAM
) mode
, &mKey
);
86 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
90 nsWindowsRegKey::Create(uint32_t rootKey
, const nsAString
&path
, uint32_t mode
)
95 LONG rv
= RegCreateKeyExW((HKEY
) rootKey
, PromiseFlatString(path
).get(), 0,
96 nullptr, REG_OPTION_NON_VOLATILE
, (REGSAM
) mode
, nullptr,
99 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
103 nsWindowsRegKey::OpenChild(const nsAString
&path
, uint32_t mode
,
104 nsIWindowsRegKey
**result
)
106 if (NS_WARN_IF(!mKey
))
107 return NS_ERROR_NOT_INITIALIZED
;
109 nsCOMPtr
<nsIWindowsRegKey
> child
= new nsWindowsRegKey();
111 nsresult rv
= child
->Open((uintptr_t) mKey
, path
, mode
);
120 nsWindowsRegKey::CreateChild(const nsAString
&path
, uint32_t mode
,
121 nsIWindowsRegKey
**result
)
123 if (NS_WARN_IF(!mKey
))
124 return NS_ERROR_NOT_INITIALIZED
;
126 nsCOMPtr
<nsIWindowsRegKey
> child
= new nsWindowsRegKey();
128 nsresult rv
= child
->Create((uintptr_t) mKey
, path
, mode
);
137 nsWindowsRegKey::GetChildCount(uint32_t *result
)
139 if (NS_WARN_IF(!mKey
))
140 return NS_ERROR_NOT_INITIALIZED
;
143 LONG rv
= RegQueryInfoKeyW(mKey
, nullptr, nullptr, nullptr, &numSubKeys
,
144 nullptr, nullptr, nullptr, nullptr, nullptr,
146 if (rv
!= ERROR_SUCCESS
)
147 return NS_ERROR_FAILURE
;
149 *result
= numSubKeys
;
154 nsWindowsRegKey::GetChildName(uint32_t index
, nsAString
&result
)
156 if (NS_WARN_IF(!mKey
))
157 return NS_ERROR_NOT_INITIALIZED
;
159 FILETIME lastWritten
;
161 wchar_t nameBuf
[MAX_KEY_NAME_LEN
+ 1];
162 DWORD nameLen
= sizeof(nameBuf
) / sizeof(nameBuf
[0]);
164 LONG rv
= RegEnumKeyExW(mKey
, index
, nameBuf
, &nameLen
, nullptr, nullptr,
165 nullptr, &lastWritten
);
166 if (rv
!= ERROR_SUCCESS
)
167 return NS_ERROR_NOT_AVAILABLE
; // XXX what's the best error code here?
169 result
.Assign(nameBuf
, nameLen
);
175 nsWindowsRegKey::HasChild(const nsAString
&name
, bool *result
)
177 if (NS_WARN_IF(!mKey
))
178 return NS_ERROR_NOT_INITIALIZED
;
180 // Check for the existence of a child key by opening the key with minimal
181 // rights. Perhaps there is a more efficient way to do this?
184 LONG rv
= RegOpenKeyExW(mKey
, PromiseFlatString(name
).get(), 0,
185 STANDARD_RIGHTS_READ
, &key
);
187 if ((*result
= (rv
== ERROR_SUCCESS
&& key
)))
194 nsWindowsRegKey::GetValueCount(uint32_t *result
)
196 if (NS_WARN_IF(!mKey
))
197 return NS_ERROR_NOT_INITIALIZED
;
200 LONG rv
= RegQueryInfoKeyW(mKey
, nullptr, nullptr, nullptr, nullptr,
201 nullptr, nullptr, &numValues
, nullptr, nullptr,
203 if (rv
!= ERROR_SUCCESS
)
204 return NS_ERROR_FAILURE
;
211 nsWindowsRegKey::GetValueName(uint32_t index
, nsAString
&result
)
213 if (NS_WARN_IF(!mKey
))
214 return NS_ERROR_NOT_INITIALIZED
;
216 wchar_t nameBuf
[MAX_VALUE_NAME_LEN
];
217 DWORD nameLen
= sizeof(nameBuf
) / sizeof(nameBuf
[0]);
219 LONG rv
= RegEnumValueW(mKey
, index
, nameBuf
, &nameLen
, nullptr, nullptr,
221 if (rv
!= ERROR_SUCCESS
)
222 return NS_ERROR_NOT_AVAILABLE
; // XXX what's the best error code here?
224 result
.Assign(nameBuf
, nameLen
);
230 nsWindowsRegKey::HasValue(const nsAString
&name
, bool *result
)
232 if (NS_WARN_IF(!mKey
))
233 return NS_ERROR_NOT_INITIALIZED
;
235 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, nullptr,
238 *result
= (rv
== ERROR_SUCCESS
);
243 nsWindowsRegKey::RemoveChild(const nsAString
&name
)
245 if (NS_WARN_IF(!mKey
))
246 return NS_ERROR_NOT_INITIALIZED
;
248 LONG rv
= RegDeleteKeyW(mKey
, PromiseFlatString(name
).get());
250 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
254 nsWindowsRegKey::RemoveValue(const nsAString
&name
)
256 if (NS_WARN_IF(!mKey
))
257 return NS_ERROR_NOT_INITIALIZED
;
259 LONG rv
= RegDeleteValueW(mKey
, PromiseFlatString(name
).get());
261 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
265 nsWindowsRegKey::GetValueType(const nsAString
&name
, uint32_t *result
)
267 if (NS_WARN_IF(!mKey
))
268 return NS_ERROR_NOT_INITIALIZED
;
270 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0,
271 (LPDWORD
) result
, nullptr, nullptr);
273 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
277 nsWindowsRegKey::ReadStringValue(const nsAString
&name
, nsAString
&result
)
279 if (NS_WARN_IF(!mKey
))
280 return NS_ERROR_NOT_INITIALIZED
;
284 const nsString
&flatName
= PromiseFlatString(name
);
286 LONG rv
= RegQueryValueExW(mKey
, flatName
.get(), 0, &type
, nullptr, &size
);
287 if (rv
!= ERROR_SUCCESS
)
288 return NS_ERROR_FAILURE
;
290 // This must be a string type in order to fetch the value as a string.
291 // We're being a bit forgiving here by allowing types other than REG_SZ.
292 if (type
!= REG_SZ
&& type
== REG_EXPAND_SZ
&& type
== REG_MULTI_SZ
)
293 return NS_ERROR_FAILURE
;
295 // The buffer size must be a multiple of 2.
297 return NS_ERROR_UNEXPECTED
;
304 // |size| may or may not include the terminating null character.
305 DWORD resultLen
= size
/ 2;
307 result
.SetLength(resultLen
);
308 nsAString::iterator begin
;
309 result
.BeginWriting(begin
);
310 if (begin
.size_forward() != resultLen
)
311 return NS_ERROR_OUT_OF_MEMORY
;
313 rv
= RegQueryValueExW(mKey
, flatName
.get(), 0, &type
, (LPBYTE
) begin
.get(),
316 if (!result
.CharAt(resultLen
-1)) {
317 // The string passed to us had a null terminator in the final position.
318 result
.Truncate(resultLen
-1);
321 // Expand the environment variables if needed
322 if (type
== REG_EXPAND_SZ
) {
323 const nsString
&flatSource
= PromiseFlatString(result
);
324 resultLen
= ExpandEnvironmentStringsW(flatSource
.get(), nullptr, 0);
326 nsAutoString expandedResult
;
327 // |resultLen| includes the terminating null character
329 expandedResult
.SetLength(resultLen
);
330 nsAString::iterator begin
;
331 expandedResult
.BeginWriting(begin
);
332 if (begin
.size_forward() != resultLen
)
333 return NS_ERROR_OUT_OF_MEMORY
;
335 resultLen
= ExpandEnvironmentStringsW(flatSource
.get(),
338 if (resultLen
<= 0) {
339 rv
= ERROR_UNKNOWN_FEATURE
;
343 result
= expandedResult
;
348 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
352 nsWindowsRegKey::ReadIntValue(const nsAString
&name
, uint32_t *result
)
354 if (NS_WARN_IF(!mKey
))
355 return NS_ERROR_NOT_INITIALIZED
;
357 DWORD size
= sizeof(*result
);
358 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, nullptr,
359 (LPBYTE
) result
, &size
);
361 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
365 nsWindowsRegKey::ReadInt64Value(const nsAString
&name
, uint64_t *result
)
367 if (NS_WARN_IF(!mKey
))
368 return NS_ERROR_NOT_INITIALIZED
;
370 DWORD size
= sizeof(*result
);
371 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, nullptr,
372 (LPBYTE
) result
, &size
);
374 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
378 nsWindowsRegKey::ReadBinaryValue(const nsAString
&name
, nsACString
&result
)
380 if (NS_WARN_IF(!mKey
))
381 return NS_ERROR_NOT_INITIALIZED
;
384 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0,
385 nullptr, nullptr, &size
);
387 if (rv
!= ERROR_SUCCESS
)
388 return NS_ERROR_FAILURE
;
390 result
.SetLength(size
);
391 nsACString::iterator begin
;
392 result
.BeginWriting(begin
);
393 if (begin
.size_forward() != size
)
394 return NS_ERROR_OUT_OF_MEMORY
;
396 rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, nullptr,
397 (LPBYTE
) begin
.get(), &size
);
399 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
403 nsWindowsRegKey::WriteStringValue(const nsAString
&name
, const nsAString
&value
)
405 if (NS_WARN_IF(!mKey
))
406 return NS_ERROR_NOT_INITIALIZED
;
408 // Need to indicate complete size of buffer including null terminator.
409 const nsString
&flatValue
= PromiseFlatString(value
);
411 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_SZ
,
412 (const BYTE
*) flatValue
.get(),
413 (flatValue
.Length() + 1) * sizeof(char16_t
));
415 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
419 nsWindowsRegKey::WriteIntValue(const nsAString
&name
, uint32_t value
)
421 if (NS_WARN_IF(!mKey
))
422 return NS_ERROR_NOT_INITIALIZED
;
424 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_DWORD
,
425 (const BYTE
*) &value
, sizeof(value
));
427 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
431 nsWindowsRegKey::WriteInt64Value(const nsAString
&name
, uint64_t value
)
433 if (NS_WARN_IF(!mKey
))
434 return NS_ERROR_NOT_INITIALIZED
;
436 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_QWORD
,
437 (const BYTE
*) &value
, sizeof(value
));
439 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
443 nsWindowsRegKey::WriteBinaryValue(const nsAString
&name
, const nsACString
&value
)
445 if (NS_WARN_IF(!mKey
))
446 return NS_ERROR_NOT_INITIALIZED
;
448 const nsCString
&flatValue
= PromiseFlatCString(value
);
449 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_BINARY
,
450 (const BYTE
*) flatValue
.get(), flatValue
.Length());
452 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
456 nsWindowsRegKey::StartWatching(bool recurse
)
458 if (NS_WARN_IF(!mKey
))
459 return NS_ERROR_NOT_INITIALIZED
;
464 mWatchEvent
= CreateEvent(nullptr, TRUE
, FALSE
, nullptr);
466 return NS_ERROR_OUT_OF_MEMORY
;
468 DWORD filter
= REG_NOTIFY_CHANGE_NAME
|
469 REG_NOTIFY_CHANGE_ATTRIBUTES
|
470 REG_NOTIFY_CHANGE_LAST_SET
|
471 REG_NOTIFY_CHANGE_SECURITY
;
473 LONG rv
= RegNotifyChangeKeyValue(mKey
, recurse
, filter
, mWatchEvent
, TRUE
);
474 if (rv
!= ERROR_SUCCESS
) {
476 // On older versions of Windows, this call is not implemented, so simply
477 // return NS_OK in those cases and pretend that the watching is happening.
478 return (rv
== ERROR_CALL_NOT_IMPLEMENTED
) ? NS_OK
: NS_ERROR_FAILURE
;
481 mWatchRecursive
= recurse
;
486 nsWindowsRegKey::StopWatching()
489 CloseHandle(mWatchEvent
);
490 mWatchEvent
= nullptr;
496 nsWindowsRegKey::HasChanged(bool *result
)
498 if (mWatchEvent
&& WaitForSingleObject(mWatchEvent
, 0) == WAIT_OBJECT_0
) {
499 // An event only gets signaled once, then it's done, so we have to set up
500 // another event to watch.
502 StartWatching(mWatchRecursive
);
511 nsWindowsRegKey::IsWatching(bool *result
)
513 *result
= (mWatchEvent
!= nullptr);
517 //-----------------------------------------------------------------------------
520 NS_NewWindowsRegKey(nsIWindowsRegKey
**result
)
522 nsRefPtr
<nsWindowsRegKey
> key
= new nsWindowsRegKey();
527 //-----------------------------------------------------------------------------
530 nsWindowsRegKeyConstructor(nsISupports
*delegate
, const nsIID
&iid
,
534 return NS_ERROR_NO_AGGREGATION
;
536 nsCOMPtr
<nsIWindowsRegKey
> key
;
537 nsresult rv
= NS_NewWindowsRegKey(getter_AddRefs(key
));
538 if (NS_SUCCEEDED(rv
))
539 rv
= key
->QueryInterface(iid
, result
);