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 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is Google Inc.
19 * Portions created by the Initial Developer are Copyright (C) 2005
20 * the Initial Developer. All Rights Reserved.
23 * Darin Fisher <darin@meer.net>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
42 #include "nsWindowsRegKey.h"
46 //-----------------------------------------------------------------------------
48 // According to MSDN, the following limits apply (in characters excluding room
49 // for terminating null character):
50 #define MAX_KEY_NAME_LEN 255
51 #define MAX_VALUE_NAME_LEN 16383
53 class nsWindowsRegKey
: public nsIWindowsRegKey
57 NS_DECL_NSIWINDOWSREGKEY
62 , mWatchRecursive(FALSE
)
77 NS_IMPL_ISUPPORTS1(nsWindowsRegKey
, nsIWindowsRegKey
)
80 nsWindowsRegKey::GetKey(HKEY
*key
)
87 nsWindowsRegKey::SetKey(HKEY key
)
89 // We do not close the older key!
97 nsWindowsRegKey::Close()
109 nsWindowsRegKey::Open(PRUint32 rootKey
, const nsAString
&path
, PRUint32 mode
)
113 LONG rv
= RegOpenKeyExW((HKEY
) rootKey
, PromiseFlatString(path
).get(), 0,
114 (REGSAM
) mode
, &mKey
);
116 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
120 nsWindowsRegKey::Create(PRUint32 rootKey
, const nsAString
&path
, PRUint32 mode
)
125 LONG rv
= RegCreateKeyExW((HKEY
) rootKey
, PromiseFlatString(path
).get(), 0,
126 NULL
, REG_OPTION_NON_VOLATILE
, (REGSAM
) mode
, NULL
,
127 &mKey
, &disposition
);
129 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
133 nsWindowsRegKey::OpenChild(const nsAString
&path
, PRUint32 mode
,
134 nsIWindowsRegKey
**result
)
136 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
138 nsCOMPtr
<nsIWindowsRegKey
> child
= new nsWindowsRegKey();
140 return NS_ERROR_OUT_OF_MEMORY
;
142 nsresult rv
= child
->Open((uintptr_t) mKey
, path
, mode
);
151 nsWindowsRegKey::CreateChild(const nsAString
&path
, PRUint32 mode
,
152 nsIWindowsRegKey
**result
)
154 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
156 nsCOMPtr
<nsIWindowsRegKey
> child
= new nsWindowsRegKey();
158 return NS_ERROR_OUT_OF_MEMORY
;
160 nsresult rv
= child
->Create((uintptr_t) mKey
, path
, mode
);
169 nsWindowsRegKey::GetChildCount(PRUint32
*result
)
171 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
174 LONG rv
= RegQueryInfoKeyW(mKey
, NULL
, NULL
, NULL
, &numSubKeys
, NULL
, NULL
,
175 NULL
, NULL
, NULL
, NULL
, NULL
);
176 NS_ENSURE_STATE(rv
== ERROR_SUCCESS
);
178 *result
= numSubKeys
;
183 nsWindowsRegKey::GetChildName(PRUint32 index
, nsAString
&result
)
185 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
187 FILETIME lastWritten
;
189 PRUnichar nameBuf
[MAX_KEY_NAME_LEN
+ 1];
190 DWORD nameLen
= sizeof(nameBuf
) / sizeof(nameBuf
[0]);
192 LONG rv
= RegEnumKeyExW(mKey
, index
, nameBuf
, &nameLen
, NULL
, NULL
, NULL
,
194 if (rv
!= ERROR_SUCCESS
)
195 return NS_ERROR_NOT_AVAILABLE
; // XXX what's the best error code here?
197 result
.Assign(nameBuf
, nameLen
);
203 nsWindowsRegKey::HasChild(const nsAString
&name
, PRBool
*result
)
205 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
207 // Check for the existence of a child key by opening the key with minimal
208 // rights. Perhaps there is a more efficient way to do this?
211 LONG rv
= RegOpenKeyExW(mKey
, PromiseFlatString(name
).get(), 0,
212 STANDARD_RIGHTS_READ
, &key
);
214 if (*result
= (rv
== ERROR_SUCCESS
&& key
))
221 nsWindowsRegKey::GetValueCount(PRUint32
*result
)
223 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
226 LONG rv
= RegQueryInfoKeyW(mKey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
,
227 &numValues
, NULL
, NULL
, NULL
, NULL
);
228 NS_ENSURE_STATE(rv
== ERROR_SUCCESS
);
235 nsWindowsRegKey::GetValueName(PRUint32 index
, nsAString
&result
)
237 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
239 PRUnichar nameBuf
[MAX_VALUE_NAME_LEN
];
240 DWORD nameLen
= sizeof(nameBuf
) / sizeof(nameBuf
[0]);
242 LONG rv
= RegEnumValueW(mKey
, index
, nameBuf
, &nameLen
, NULL
, NULL
, NULL
,
244 if (rv
!= ERROR_SUCCESS
)
245 return NS_ERROR_NOT_AVAILABLE
; // XXX what's the best error code here?
247 result
.Assign(nameBuf
, nameLen
);
253 nsWindowsRegKey::HasValue(const nsAString
&name
, PRBool
*result
)
255 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
257 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, NULL
, NULL
,
260 *result
= (rv
== ERROR_SUCCESS
);
265 nsWindowsRegKey::RemoveChild(const nsAString
&name
)
267 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
269 LONG rv
= RegDeleteKeyW(mKey
, PromiseFlatString(name
).get());
271 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
275 nsWindowsRegKey::RemoveValue(const nsAString
&name
)
277 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
279 LONG rv
= RegDeleteValueW(mKey
, PromiseFlatString(name
).get());
281 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
285 nsWindowsRegKey::GetValueType(const nsAString
&name
, PRUint32
*result
)
287 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
289 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0,
290 (LPDWORD
) result
, NULL
, NULL
);
292 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
296 nsWindowsRegKey::ReadStringValue(const nsAString
&name
, nsAString
&result
)
298 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
302 const nsString
&flatName
= PromiseFlatString(name
);
304 LONG rv
= RegQueryValueExW(mKey
, flatName
.get(), 0, &type
, NULL
, &size
);
305 if (rv
!= ERROR_SUCCESS
)
306 return NS_ERROR_FAILURE
;
308 // This must be a string type in order to fetch the value as a string.
309 // We're being a bit forgiving here by allowing types other than REG_SZ.
310 NS_ENSURE_STATE(type
== REG_SZ
||
311 type
== REG_EXPAND_SZ
||
312 type
== REG_MULTI_SZ
);
314 // The buffer size must be a multiple of 2.
315 NS_ENSURE_STATE(size
% 2 == 0);
322 // |size| includes room for the terminating null character
323 DWORD resultLen
= size
/ 2 - 1;
325 result
.SetLength(resultLen
);
326 nsAString::iterator begin
;
327 result
.BeginWriting(begin
);
328 if (begin
.size_forward() != resultLen
)
329 return NS_ERROR_OUT_OF_MEMORY
;
331 rv
= RegQueryValueExW(mKey
, flatName
.get(), 0, NULL
, (LPBYTE
) begin
.get(),
334 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
338 nsWindowsRegKey::ReadIntValue(const nsAString
&name
, PRUint32
*result
)
340 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
342 DWORD size
= sizeof(*result
);
343 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, NULL
,
344 (LPBYTE
) result
, &size
);
346 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
350 nsWindowsRegKey::ReadInt64Value(const nsAString
&name
, PRUint64
*result
)
352 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
354 DWORD size
= sizeof(*result
);
355 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, NULL
,
356 (LPBYTE
) result
, &size
);
358 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
362 nsWindowsRegKey::ReadBinaryValue(const nsAString
&name
, nsACString
&result
)
364 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
367 LONG rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0,
370 if (rv
!= ERROR_SUCCESS
)
371 return NS_ERROR_FAILURE
;
373 result
.SetLength(size
);
374 nsACString::iterator begin
;
375 result
.BeginWriting(begin
);
376 if (begin
.size_forward() != size
)
377 return NS_ERROR_OUT_OF_MEMORY
;
379 rv
= RegQueryValueExW(mKey
, PromiseFlatString(name
).get(), 0, NULL
,
380 (LPBYTE
) begin
.get(), &size
);
382 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
386 nsWindowsRegKey::WriteStringValue(const nsAString
&name
, const nsAString
&value
)
388 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
390 // Need to indicate complete size of buffer including null terminator.
391 const nsString
&flatValue
= PromiseFlatString(value
);
393 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_SZ
,
394 (const BYTE
*) flatValue
.get(),
395 (flatValue
.Length() + 1) * sizeof(PRUnichar
));
397 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
401 nsWindowsRegKey::WriteIntValue(const nsAString
&name
, PRUint32 value
)
403 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
405 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_DWORD
,
406 (const BYTE
*) &value
, sizeof(value
));
408 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
412 nsWindowsRegKey::WriteInt64Value(const nsAString
&name
, PRUint64 value
)
414 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
416 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_QWORD
,
417 (const BYTE
*) &value
, sizeof(value
));
419 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
423 nsWindowsRegKey::WriteBinaryValue(const nsAString
&name
, const nsACString
&value
)
425 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
427 const nsCString
&flatValue
= PromiseFlatCString(value
);
428 LONG rv
= RegSetValueExW(mKey
, PromiseFlatString(name
).get(), 0, REG_BINARY
,
429 (const BYTE
*) flatValue
.get(), flatValue
.Length());
431 return (rv
== ERROR_SUCCESS
) ? NS_OK
: NS_ERROR_FAILURE
;
435 nsWindowsRegKey::StartWatching(PRBool recurse
)
438 return NS_ERROR_NOT_IMPLEMENTED
;
441 NS_ENSURE_TRUE(mKey
, NS_ERROR_NOT_INITIALIZED
);
446 mWatchEvent
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
448 return NS_ERROR_OUT_OF_MEMORY
;
450 DWORD filter
= REG_NOTIFY_CHANGE_NAME
|
451 REG_NOTIFY_CHANGE_ATTRIBUTES
|
452 REG_NOTIFY_CHANGE_LAST_SET
|
453 REG_NOTIFY_CHANGE_SECURITY
;
455 LONG rv
= RegNotifyChangeKeyValue(mKey
, recurse
, filter
, mWatchEvent
, TRUE
);
456 if (rv
!= ERROR_SUCCESS
) {
458 // On older versions of Windows, this call is not implemented, so simply
459 // return NS_OK in those cases and pretend that the watching is happening.
460 return (rv
== ERROR_CALL_NOT_IMPLEMENTED
) ? NS_OK
: NS_ERROR_FAILURE
;
463 mWatchRecursive
= recurse
;
469 nsWindowsRegKey::StopWatching()
472 CloseHandle(mWatchEvent
);
479 nsWindowsRegKey::HasChanged(PRBool
*result
)
481 if (mWatchEvent
&& WaitForSingleObject(mWatchEvent
, 0) == WAIT_OBJECT_0
) {
482 // An event only gets signaled once, then it's done, so we have to set up
483 // another event to watch.
485 StartWatching(mWatchRecursive
);
494 nsWindowsRegKey::IsWatching(PRBool
*result
)
496 *result
= (mWatchEvent
!= NULL
);
500 //-----------------------------------------------------------------------------
503 NS_NewWindowsRegKey(nsIWindowsRegKey
**result
)
505 *result
= new nsWindowsRegKey();
507 return NS_ERROR_OUT_OF_MEMORY
;
513 //-----------------------------------------------------------------------------
516 nsWindowsRegKeyConstructor(nsISupports
*delegate
, const nsIID
&iid
,
520 return NS_ERROR_NO_AGGREGATION
;
522 nsCOMPtr
<nsIWindowsRegKey
> key
;
523 nsresult rv
= NS_NewWindowsRegKey(getter_AddRefs(key
));
524 if (NS_SUCCEEDED(rv
))
525 rv
= key
->QueryInterface(iid
, result
);