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_ISUPPORTS(nsWindowsRegKey
, nsIWindowsRegKey
)
50 nsWindowsRegKey::GetKey(HKEY
* aKey
)
57 nsWindowsRegKey::SetKey(HKEY aKey
)
59 // We do not close the older aKey!
67 nsWindowsRegKey::Close()
79 nsWindowsRegKey::Open(uint32_t aRootKey
, const nsAString
& aPath
,
84 LONG rv
= RegOpenKeyExW((HKEY
)(intptr_t)aRootKey
,
85 PromiseFlatString(aPath
).get(), 0, (REGSAM
)aMode
,
87 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
91 nsWindowsRegKey::Create(uint32_t aRootKey
, const nsAString
& aPath
,
97 LONG rv
= RegCreateKeyExW((HKEY
)(intptr_t)aRootKey
,
98 PromiseFlatString(aPath
).get(), 0, nullptr,
99 REG_OPTION_NON_VOLATILE
, (REGSAM
)aMode
, nullptr,
100 &mKey
, &disposition
);
101 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
105 nsWindowsRegKey::OpenChild(const nsAString
& aPath
, uint32_t aMode
,
106 nsIWindowsRegKey
** aResult
)
108 if (NS_WARN_IF(!mKey
)) {
109 return NS_ERROR_NOT_INITIALIZED
;
112 nsCOMPtr
<nsIWindowsRegKey
> child
= new nsWindowsRegKey();
114 nsresult rv
= child
->Open((uintptr_t)mKey
, aPath
, aMode
);
119 child
.swap(*aResult
);
124 nsWindowsRegKey::CreateChild(const nsAString
& aPath
, uint32_t aMode
,
125 nsIWindowsRegKey
** aResult
)
127 if (NS_WARN_IF(!mKey
)) {
128 return NS_ERROR_NOT_INITIALIZED
;
131 nsCOMPtr
<nsIWindowsRegKey
> child
= new nsWindowsRegKey();
133 nsresult rv
= child
->Create((uintptr_t)mKey
, aPath
, aMode
);
138 child
.swap(*aResult
);
143 nsWindowsRegKey::GetChildCount(uint32_t* aResult
)
145 if (NS_WARN_IF(!mKey
)) {
146 return NS_ERROR_NOT_INITIALIZED
;
150 LONG rv
= RegQueryInfoKeyW(mKey
, nullptr, nullptr, nullptr, &numSubKeys
,
151 nullptr, nullptr, nullptr, nullptr, nullptr,
153 if (rv
!= ERROR_SUCCESS
) {
154 return NS_ERROR_FAILURE
;
157 *aResult
= numSubKeys
;
162 nsWindowsRegKey::GetChildName(uint32_t aIndex
, nsAString
& aResult
)
164 if (NS_WARN_IF(!mKey
)) {
165 return NS_ERROR_NOT_INITIALIZED
;
168 FILETIME lastWritten
;
170 wchar_t nameBuf
[MAX_KEY_NAME_LEN
+ 1];
171 DWORD nameLen
= sizeof(nameBuf
) / sizeof(nameBuf
[0]);
173 LONG rv
= RegEnumKeyExW(mKey
, aIndex
, nameBuf
, &nameLen
, nullptr, nullptr,
174 nullptr, &lastWritten
);
175 if (rv
!= ERROR_SUCCESS
) {
176 return NS_ERROR_NOT_AVAILABLE
; // XXX what's the best error code here?
179 aResult
.Assign(nameBuf
, nameLen
);
185 nsWindowsRegKey::HasChild(const nsAString
& aName
, bool* aResult
)
187 if (NS_WARN_IF(!mKey
)) {
188 return NS_ERROR_NOT_INITIALIZED
;
191 // Check for the existence of a child key by opening the key with minimal
192 // rights. Perhaps there is a more efficient way to do this?
195 LONG rv
= RegOpenKeyExW(mKey
, PromiseFlatString(aName
).get(), 0,
196 STANDARD_RIGHTS_READ
, &key
);
198 if ((*aResult
= (rv
== ERROR_SUCCESS
&& key
))) {
206 nsWindowsRegKey::GetValueCount(uint32_t* aResult
)
208 if (NS_WARN_IF(!mKey
)) {
209 return NS_ERROR_NOT_INITIALIZED
;
213 LONG rv
= RegQueryInfoKeyW(mKey
, nullptr, nullptr, nullptr, nullptr,
214 nullptr, nullptr, &numValues
, nullptr, nullptr,
216 if (rv
!= ERROR_SUCCESS
) {
217 return NS_ERROR_FAILURE
;
220 *aResult
= numValues
;
225 nsWindowsRegKey::GetValueName(uint32_t aIndex
, nsAString
& aResult
)
227 if (NS_WARN_IF(!mKey
)) {
228 return NS_ERROR_NOT_INITIALIZED
;
231 wchar_t nameBuf
[MAX_VALUE_NAME_LEN
];
232 DWORD nameLen
= sizeof(nameBuf
) / sizeof(nameBuf
[0]);
234 LONG rv
= RegEnumValueW(mKey
, aIndex
, nameBuf
, &nameLen
, nullptr, nullptr,
236 if (rv
!= ERROR_SUCCESS
) {
237 return NS_ERROR_NOT_AVAILABLE
; // XXX what's the best error code here?
240 aResult
.Assign(nameBuf
, nameLen
);
246 nsWindowsRegKey::HasValue(const nsAString
& aName
, bool* aResult
)
248 if (NS_WARN_IF(!mKey
)) {
249 return NS_ERROR_NOT_INITIALIZED
;
252 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(aName
).get(), 0, nullptr,
255 *aResult
= (rv
== ERROR_SUCCESS
);
260 nsWindowsRegKey::RemoveChild(const nsAString
& aName
)
262 if (NS_WARN_IF(!mKey
)) {
263 return NS_ERROR_NOT_INITIALIZED
;
266 LONG rv
= RegDeleteKeyW(mKey
, PromiseFlatString(aName
).get());
268 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
272 nsWindowsRegKey::RemoveValue(const nsAString
& aName
)
274 if (NS_WARN_IF(!mKey
)) {
275 return NS_ERROR_NOT_INITIALIZED
;
278 LONG rv
= RegDeleteValueW(mKey
, PromiseFlatString(aName
).get());
280 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
284 nsWindowsRegKey::GetValueType(const nsAString
& aName
, uint32_t* aResult
)
286 if (NS_WARN_IF(!mKey
)) {
287 return NS_ERROR_NOT_INITIALIZED
;
290 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(aName
).get(), 0,
291 (LPDWORD
)aResult
, nullptr, nullptr);
292 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
296 nsWindowsRegKey::ReadStringValue(const nsAString
& aName
, nsAString
& aResult
)
298 if (NS_WARN_IF(!mKey
)) {
299 return NS_ERROR_NOT_INITIALIZED
;
304 const nsString
& flatName
= PromiseFlatString(aName
);
306 LONG rv
= RegQueryValueExW(mKey
, flatName
.get(), 0, &type
, nullptr, &size
);
307 if (rv
!= ERROR_SUCCESS
) {
308 return NS_ERROR_FAILURE
;
311 // This must be a string type in order to fetch the value as a string.
312 // We're being a bit forgiving here by allowing types other than REG_SZ.
313 if (type
!= REG_SZ
&& type
== REG_EXPAND_SZ
&& type
== REG_MULTI_SZ
) {
314 return NS_ERROR_FAILURE
;
317 // The buffer size must be a multiple of 2.
319 return NS_ERROR_UNEXPECTED
;
327 // |size| may or may not include the terminating null character.
328 DWORD resultLen
= size
/ 2;
330 aResult
.SetLength(resultLen
);
331 nsAString::iterator begin
;
332 aResult
.BeginWriting(begin
);
333 if (begin
.size_forward() != resultLen
) {
334 return NS_ERROR_OUT_OF_MEMORY
;
337 rv
= RegQueryValueExW(mKey
, flatName
.get(), 0, &type
, (LPBYTE
)begin
.get(),
340 if (!aResult
.CharAt(resultLen
- 1)) {
341 // The string passed to us had a null terminator in the final position.
342 aResult
.Truncate(resultLen
- 1);
345 // Expand the environment variables if needed
346 if (type
== REG_EXPAND_SZ
) {
347 const nsString
& flatSource
= PromiseFlatString(aResult
);
348 resultLen
= ExpandEnvironmentStringsW(flatSource
.get(), nullptr, 0);
350 nsAutoString expandedResult
;
351 // |resultLen| includes the terminating null character
353 expandedResult
.SetLength(resultLen
);
354 nsAString::iterator begin
;
355 expandedResult
.BeginWriting(begin
);
356 if (begin
.size_forward() != resultLen
) {
357 return NS_ERROR_OUT_OF_MEMORY
;
360 resultLen
= ExpandEnvironmentStringsW(flatSource
.get(),
363 if (resultLen
<= 0) {
364 rv
= ERROR_UNKNOWN_FEATURE
;
368 aResult
= expandedResult
;
370 } else if (resultLen
== 1) {
371 // It apparently expands to nothing (just a null terminator).
376 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
380 nsWindowsRegKey::ReadIntValue(const nsAString
& aName
, uint32_t* aResult
)
382 if (NS_WARN_IF(!mKey
)) {
383 return NS_ERROR_NOT_INITIALIZED
;
386 DWORD size
= sizeof(*aResult
);
387 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(aName
).get(), 0, nullptr,
388 (LPBYTE
)aResult
, &size
);
389 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
393 nsWindowsRegKey::ReadInt64Value(const nsAString
& aName
, uint64_t* aResult
)
395 if (NS_WARN_IF(!mKey
)) {
396 return NS_ERROR_NOT_INITIALIZED
;
399 DWORD size
= sizeof(*aResult
);
400 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(aName
).get(), 0, nullptr,
401 (LPBYTE
)aResult
, &size
);
402 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
406 nsWindowsRegKey::ReadBinaryValue(const nsAString
& aName
, nsACString
& aResult
)
408 if (NS_WARN_IF(!mKey
)) {
409 return NS_ERROR_NOT_INITIALIZED
;
413 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(aName
).get(), 0,
414 nullptr, nullptr, &size
);
416 if (rv
!= ERROR_SUCCESS
) {
417 return NS_ERROR_FAILURE
;
425 aResult
.SetLength(size
);
426 nsACString::iterator begin
;
427 aResult
.BeginWriting(begin
);
428 if (begin
.size_forward() != size
) {
429 return NS_ERROR_OUT_OF_MEMORY
;
432 rv
= RegQueryValueExW(mKey
, PromiseFlatString(aName
).get(), 0, nullptr,
433 (LPBYTE
)begin
.get(), &size
);
434 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
438 nsWindowsRegKey::WriteStringValue(const nsAString
& aName
,
439 const nsAString
& aValue
)
441 if (NS_WARN_IF(!mKey
)) {
442 return NS_ERROR_NOT_INITIALIZED
;
445 // Need to indicate complete size of buffer including null terminator.
446 const nsString
& flatValue
= PromiseFlatString(aValue
);
448 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(aName
).get(), 0, REG_SZ
,
449 (const BYTE
*)flatValue
.get(),
450 (flatValue
.Length() + 1) * sizeof(char16_t
));
451 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
455 nsWindowsRegKey::WriteIntValue(const nsAString
& aName
, uint32_t aValue
)
457 if (NS_WARN_IF(!mKey
)) {
458 return NS_ERROR_NOT_INITIALIZED
;
461 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(aName
).get(), 0, REG_DWORD
,
462 (const BYTE
*)&aValue
, sizeof(aValue
));
463 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
467 nsWindowsRegKey::WriteInt64Value(const nsAString
& aName
, uint64_t aValue
)
469 if (NS_WARN_IF(!mKey
)) {
470 return NS_ERROR_NOT_INITIALIZED
;
473 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(aName
).get(), 0, REG_QWORD
,
474 (const BYTE
*)&aValue
, sizeof(aValue
));
475 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
479 nsWindowsRegKey::WriteBinaryValue(const nsAString
& aName
,
480 const nsACString
& aValue
)
482 if (NS_WARN_IF(!mKey
)) {
483 return NS_ERROR_NOT_INITIALIZED
;
486 const nsCString
& flatValue
= PromiseFlatCString(aValue
);
487 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(aName
).get(), 0, REG_BINARY
,
488 (const BYTE
*)flatValue
.get(), flatValue
.Length());
489 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
493 nsWindowsRegKey::StartWatching(bool aRecurse
)
495 if (NS_WARN_IF(!mKey
)) {
496 return NS_ERROR_NOT_INITIALIZED
;
503 mWatchEvent
= CreateEvent(nullptr, TRUE
, FALSE
, nullptr);
505 return NS_ERROR_OUT_OF_MEMORY
;
508 DWORD filter
= REG_NOTIFY_CHANGE_NAME
|
509 REG_NOTIFY_CHANGE_ATTRIBUTES
|
510 REG_NOTIFY_CHANGE_LAST_SET
|
511 REG_NOTIFY_CHANGE_SECURITY
;
513 LONG rv
= RegNotifyChangeKeyValue(mKey
, aRecurse
, filter
, mWatchEvent
, TRUE
);
514 if (rv
!= ERROR_SUCCESS
) {
516 // On older versions of Windows, this call is not implemented, so simply
517 // return NS_OK in those cases and pretend that the watching is happening.
518 return (rv
== ERROR_CALL_NOT_IMPLEMENTED
) ? NS_OK
: NS_ERROR_FAILURE
;
521 mWatchRecursive
= aRecurse
;
526 nsWindowsRegKey::StopWatching()
529 CloseHandle(mWatchEvent
);
530 mWatchEvent
= nullptr;
536 nsWindowsRegKey::HasChanged(bool* aResult
)
538 if (mWatchEvent
&& WaitForSingleObject(mWatchEvent
, 0) == WAIT_OBJECT_0
) {
539 // An event only gets signaled once, then it's done, so we have to set up
540 // another event to watch.
542 StartWatching(mWatchRecursive
);
551 nsWindowsRegKey::IsWatching(bool* aResult
)
553 *aResult
= (mWatchEvent
!= nullptr);
557 //-----------------------------------------------------------------------------
560 NS_NewWindowsRegKey(nsIWindowsRegKey
** aResult
)
562 nsRefPtr
<nsWindowsRegKey
> key
= new nsWindowsRegKey();
567 //-----------------------------------------------------------------------------
570 nsWindowsRegKeyConstructor(nsISupports
* aDelegate
, const nsIID
& aIID
,
574 return NS_ERROR_NO_AGGREGATION
;
577 nsCOMPtr
<nsIWindowsRegKey
> key
;
578 nsresult rv
= NS_NewWindowsRegKey(getter_AddRefs(key
));
579 if (NS_SUCCEEDED(rv
)) {
580 rv
= key
->QueryInterface(aIID
, aResult
);