Bug 959405 - Please update the Buri Moz-central, 1.3, 1.2 with the latest blobs from...
[gecko.git] / xpcom / ds / nsWindowsRegKey.cpp
blob31859249a63c370e3ce0e3147aa3e960f8d52eb1
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/. */
7 #include <windows.h>
8 #include <shlwapi.h>
9 #include <stdlib.h>
10 #include "nsWindowsRegKey.h"
11 #include "nsString.h"
12 #include "nsCOMPtr.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
25 public:
26 NS_DECL_ISUPPORTS
27 NS_DECL_NSIWINDOWSREGKEY
29 nsWindowsRegKey()
30 : mKey(nullptr)
31 , mWatchEvent(nullptr)
32 , mWatchRecursive(FALSE)
36 private:
37 ~nsWindowsRegKey()
39 Close();
42 HKEY mKey;
43 HANDLE mWatchEvent;
44 BOOL mWatchRecursive;
47 NS_IMPL_ISUPPORTS1(nsWindowsRegKey, nsIWindowsRegKey)
49 NS_IMETHODIMP
50 nsWindowsRegKey::GetKey(HKEY *key)
52 *key = mKey;
53 return NS_OK;
56 NS_IMETHODIMP
57 nsWindowsRegKey::SetKey(HKEY key)
59 // We do not close the older key!
60 StopWatching();
62 mKey = key;
63 return NS_OK;
66 NS_IMETHODIMP
67 nsWindowsRegKey::Close()
69 StopWatching();
71 if (mKey) {
72 RegCloseKey(mKey);
73 mKey = nullptr;
75 return NS_OK;
78 NS_IMETHODIMP
79 nsWindowsRegKey::Open(uint32_t rootKey, const nsAString &path, uint32_t mode)
81 Close();
83 LONG rv = RegOpenKeyExW((HKEY) rootKey, PromiseFlatString(path).get(), 0,
84 (REGSAM) mode, &mKey);
86 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
89 NS_IMETHODIMP
90 nsWindowsRegKey::Create(uint32_t rootKey, const nsAString &path, uint32_t mode)
92 Close();
94 DWORD disposition;
95 LONG rv = RegCreateKeyExW((HKEY) rootKey, PromiseFlatString(path).get(), 0,
96 nullptr, REG_OPTION_NON_VOLATILE, (REGSAM) mode, nullptr,
97 &mKey, &disposition);
99 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
102 NS_IMETHODIMP
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);
112 if (NS_FAILED(rv))
113 return rv;
115 child.swap(*result);
116 return NS_OK;
119 NS_IMETHODIMP
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);
129 if (NS_FAILED(rv))
130 return rv;
132 child.swap(*result);
133 return NS_OK;
136 NS_IMETHODIMP
137 nsWindowsRegKey::GetChildCount(uint32_t *result)
139 if (NS_WARN_IF(!mKey))
140 return NS_ERROR_NOT_INITIALIZED;
142 DWORD numSubKeys;
143 LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, &numSubKeys,
144 nullptr, nullptr, nullptr, nullptr, nullptr,
145 nullptr, nullptr);
146 if (rv != ERROR_SUCCESS)
147 return NS_ERROR_FAILURE;
149 *result = numSubKeys;
150 return NS_OK;
153 NS_IMETHODIMP
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);
171 return NS_OK;
174 NS_IMETHODIMP
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?
183 HKEY key;
184 LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(name).get(), 0,
185 STANDARD_RIGHTS_READ, &key);
187 if ((*result = (rv == ERROR_SUCCESS && key)))
188 RegCloseKey(key);
190 return NS_OK;
193 NS_IMETHODIMP
194 nsWindowsRegKey::GetValueCount(uint32_t *result)
196 if (NS_WARN_IF(!mKey))
197 return NS_ERROR_NOT_INITIALIZED;
199 DWORD numValues;
200 LONG rv = RegQueryInfoKeyW(mKey, nullptr, nullptr, nullptr, nullptr,
201 nullptr, nullptr, &numValues, nullptr, nullptr,
202 nullptr, nullptr);
203 if (rv != ERROR_SUCCESS)
204 return NS_ERROR_FAILURE;
206 *result = numValues;
207 return NS_OK;
210 NS_IMETHODIMP
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,
220 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);
226 return NS_OK;
229 NS_IMETHODIMP
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,
236 nullptr, nullptr);
238 *result = (rv == ERROR_SUCCESS);
239 return NS_OK;
242 NS_IMETHODIMP
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;
253 NS_IMETHODIMP
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;
264 NS_IMETHODIMP
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;
276 NS_IMETHODIMP
277 nsWindowsRegKey::ReadStringValue(const nsAString &name, nsAString &result)
279 if (NS_WARN_IF(!mKey))
280 return NS_ERROR_NOT_INITIALIZED;
282 DWORD type, size;
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.
296 if (size % 2 != 0)
297 return NS_ERROR_UNEXPECTED;
299 if (size == 0) {
300 result.Truncate();
301 return NS_OK;
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(),
314 &size);
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);
325 if (resultLen > 0) {
326 nsAutoString expandedResult;
327 // |resultLen| includes the terminating null character
328 --resultLen;
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(),
336 begin.get(),
337 resultLen + 1);
338 if (resultLen <= 0) {
339 rv = ERROR_UNKNOWN_FEATURE;
340 result.Truncate();
341 } else {
342 rv = ERROR_SUCCESS;
343 result = expandedResult;
348 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
351 NS_IMETHODIMP
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;
364 NS_IMETHODIMP
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;
377 NS_IMETHODIMP
378 nsWindowsRegKey::ReadBinaryValue(const nsAString &name, nsACString &result)
380 if (NS_WARN_IF(!mKey))
381 return NS_ERROR_NOT_INITIALIZED;
383 DWORD size;
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;
402 NS_IMETHODIMP
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;
418 NS_IMETHODIMP
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;
430 NS_IMETHODIMP
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;
442 NS_IMETHODIMP
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;
455 NS_IMETHODIMP
456 nsWindowsRegKey::StartWatching(bool recurse)
458 if (NS_WARN_IF(!mKey))
459 return NS_ERROR_NOT_INITIALIZED;
461 if (mWatchEvent)
462 return NS_OK;
464 mWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
465 if (!mWatchEvent)
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) {
475 StopWatching();
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;
482 return NS_OK;
485 NS_IMETHODIMP
486 nsWindowsRegKey::StopWatching()
488 if (mWatchEvent) {
489 CloseHandle(mWatchEvent);
490 mWatchEvent = nullptr;
492 return NS_OK;
495 NS_IMETHODIMP
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.
501 StopWatching();
502 StartWatching(mWatchRecursive);
503 *result = true;
504 } else {
505 *result = false;
507 return NS_OK;
510 NS_IMETHODIMP
511 nsWindowsRegKey::IsWatching(bool *result)
513 *result = (mWatchEvent != nullptr);
514 return NS_OK;
517 //-----------------------------------------------------------------------------
519 nsresult
520 NS_NewWindowsRegKey(nsIWindowsRegKey **result)
522 nsRefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
523 key.forget(result);
524 return NS_OK;
527 //-----------------------------------------------------------------------------
529 nsresult
530 nsWindowsRegKeyConstructor(nsISupports *delegate, const nsIID &iid,
531 void **result)
533 if (delegate)
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);
540 return rv;