Fixed register allocation bug in left-shift operations (bug 606063, r=dmandelin).
[mozilla-central.git] / xpcom / ds / nsWindowsRegKey.cpp
blob7d6b7adf9eef09587a31edb13c261d4ea724ca35
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
14 * License.
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.
22 * Contributor(s):
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 ***** */
39 #include <windows.h>
40 #include <shlwapi.h>
41 #include <stdlib.h>
42 #include "nsWindowsRegKey.h"
43 #include "nsString.h"
44 #include "nsCOMPtr.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
55 public:
56 NS_DECL_ISUPPORTS
57 NS_DECL_NSIWINDOWSREGKEY
59 nsWindowsRegKey()
60 : mKey(NULL)
61 , mWatchEvent(NULL)
62 , mWatchRecursive(FALSE)
66 private:
67 ~nsWindowsRegKey()
69 Close();
72 HKEY mKey;
73 HANDLE mWatchEvent;
74 BOOL mWatchRecursive;
77 NS_IMPL_ISUPPORTS1(nsWindowsRegKey, nsIWindowsRegKey)
79 NS_IMETHODIMP
80 nsWindowsRegKey::GetKey(HKEY *key)
82 *key = mKey;
83 return NS_OK;
86 NS_IMETHODIMP
87 nsWindowsRegKey::SetKey(HKEY key)
89 // We do not close the older key!
90 StopWatching();
92 mKey = key;
93 return NS_OK;
96 NS_IMETHODIMP
97 nsWindowsRegKey::Close()
99 StopWatching();
101 if (mKey) {
102 RegCloseKey(mKey);
103 mKey = NULL;
105 return NS_OK;
108 NS_IMETHODIMP
109 nsWindowsRegKey::Open(PRUint32 rootKey, const nsAString &path, PRUint32 mode)
111 Close();
113 LONG rv = RegOpenKeyExW((HKEY) rootKey, PromiseFlatString(path).get(), 0,
114 (REGSAM) mode, &mKey);
116 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
119 NS_IMETHODIMP
120 nsWindowsRegKey::Create(PRUint32 rootKey, const nsAString &path, PRUint32 mode)
122 Close();
124 DWORD disposition;
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;
132 NS_IMETHODIMP
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();
139 if (!child)
140 return NS_ERROR_OUT_OF_MEMORY;
142 nsresult rv = child->Open((uintptr_t) mKey, path, mode);
143 if (NS_FAILED(rv))
144 return rv;
146 child.swap(*result);
147 return NS_OK;
150 NS_IMETHODIMP
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();
157 if (!child)
158 return NS_ERROR_OUT_OF_MEMORY;
160 nsresult rv = child->Create((uintptr_t) mKey, path, mode);
161 if (NS_FAILED(rv))
162 return rv;
164 child.swap(*result);
165 return NS_OK;
168 NS_IMETHODIMP
169 nsWindowsRegKey::GetChildCount(PRUint32 *result)
171 NS_ENSURE_TRUE(mKey, NS_ERROR_NOT_INITIALIZED);
173 DWORD numSubKeys;
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;
179 return NS_OK;
182 NS_IMETHODIMP
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,
193 &lastWritten);
194 if (rv != ERROR_SUCCESS)
195 return NS_ERROR_NOT_AVAILABLE; // XXX what's the best error code here?
197 result.Assign(nameBuf, nameLen);
199 return NS_OK;
202 NS_IMETHODIMP
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?
210 HKEY key;
211 LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(name).get(), 0,
212 STANDARD_RIGHTS_READ, &key);
214 if (*result = (rv == ERROR_SUCCESS && key))
215 RegCloseKey(key);
217 return NS_OK;
220 NS_IMETHODIMP
221 nsWindowsRegKey::GetValueCount(PRUint32 *result)
223 NS_ENSURE_TRUE(mKey, NS_ERROR_NOT_INITIALIZED);
225 DWORD numValues;
226 LONG rv = RegQueryInfoKeyW(mKey, NULL, NULL, NULL, NULL, NULL, NULL,
227 &numValues, NULL, NULL, NULL, NULL);
228 NS_ENSURE_STATE(rv == ERROR_SUCCESS);
230 *result = numValues;
231 return NS_OK;
234 NS_IMETHODIMP
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,
243 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);
249 return NS_OK;
252 NS_IMETHODIMP
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,
258 NULL);
260 *result = (rv == ERROR_SUCCESS);
261 return NS_OK;
264 NS_IMETHODIMP
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;
274 NS_IMETHODIMP
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;
284 NS_IMETHODIMP
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;
295 NS_IMETHODIMP
296 nsWindowsRegKey::ReadStringValue(const nsAString &name, nsAString &result)
298 NS_ENSURE_TRUE(mKey, NS_ERROR_NOT_INITIALIZED);
300 DWORD type, size;
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);
317 if (size == 0) {
318 result.Truncate();
319 return NS_OK;
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(),
332 &size);
334 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
337 NS_IMETHODIMP
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;
349 NS_IMETHODIMP
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;
361 NS_IMETHODIMP
362 nsWindowsRegKey::ReadBinaryValue(const nsAString &name, nsACString &result)
364 NS_ENSURE_TRUE(mKey, NS_ERROR_NOT_INITIALIZED);
366 DWORD size;
367 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0,
368 NULL, NULL, &size);
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;
385 NS_IMETHODIMP
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;
400 NS_IMETHODIMP
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;
411 NS_IMETHODIMP
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;
422 NS_IMETHODIMP
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;
434 NS_IMETHODIMP
435 nsWindowsRegKey::StartWatching(PRBool recurse)
437 #ifdef WINCE
438 return NS_ERROR_NOT_IMPLEMENTED;
439 #else
441 NS_ENSURE_TRUE(mKey, NS_ERROR_NOT_INITIALIZED);
443 if (mWatchEvent)
444 return NS_OK;
446 mWatchEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
447 if (!mWatchEvent)
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) {
457 StopWatching();
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;
464 return NS_OK;
465 #endif
468 NS_IMETHODIMP
469 nsWindowsRegKey::StopWatching()
471 if (mWatchEvent) {
472 CloseHandle(mWatchEvent);
473 mWatchEvent = NULL;
475 return NS_OK;
478 NS_IMETHODIMP
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.
484 StopWatching();
485 StartWatching(mWatchRecursive);
486 *result = PR_TRUE;
487 } else {
488 *result = PR_FALSE;
490 return NS_OK;
493 NS_IMETHODIMP
494 nsWindowsRegKey::IsWatching(PRBool *result)
496 *result = (mWatchEvent != NULL);
497 return NS_OK;
500 //-----------------------------------------------------------------------------
502 nsresult
503 NS_NewWindowsRegKey(nsIWindowsRegKey **result)
505 *result = new nsWindowsRegKey();
506 if (!*result)
507 return NS_ERROR_OUT_OF_MEMORY;
509 NS_ADDREF(*result);
510 return NS_OK;
513 //-----------------------------------------------------------------------------
515 nsresult
516 nsWindowsRegKeyConstructor(nsISupports *delegate, const nsIID &iid,
517 void **result)
519 if (delegate)
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);
526 return rv;