xpcom/ds/nsWindowsRegKey.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:a28447a250ef
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/. */
6
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"
15
16 //-----------------------------------------------------------------------------
17
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
22
23 class nsWindowsRegKey MOZ_FINAL : public nsIWindowsRegKey
24 {
25 public:
26 NS_DECL_ISUPPORTS
27 NS_DECL_NSIWINDOWSREGKEY
28
29 nsWindowsRegKey()
30 : mKey(nullptr)
31 , mWatchEvent(nullptr)
32 , mWatchRecursive(FALSE)
33 {
34 }
35
36 private:
37 ~nsWindowsRegKey()
38 {
39 Close();
40 }
41
42 HKEY mKey;
43 HANDLE mWatchEvent;
44 BOOL mWatchRecursive;
45 };
46
47 NS_IMPL_ISUPPORTS(nsWindowsRegKey, nsIWindowsRegKey)
48
49 NS_IMETHODIMP
50 nsWindowsRegKey::GetKey(HKEY *key)
51 {
52 *key = mKey;
53 return NS_OK;
54 }
55
56 NS_IMETHODIMP
57 nsWindowsRegKey::SetKey(HKEY key)
58 {
59 // We do not close the older key!
60 StopWatching();
61
62 mKey = key;
63 return NS_OK;
64 }
65
66 NS_IMETHODIMP
67 nsWindowsRegKey::Close()
68 {
69 StopWatching();
70
71 if (mKey) {
72 RegCloseKey(mKey);
73 mKey = nullptr;
74 }
75 return NS_OK;
76 }
77
78 NS_IMETHODIMP
79 nsWindowsRegKey::Open(uint32_t rootKey, const nsAString &path, uint32_t mode)
80 {
81 Close();
82
83 LONG rv = RegOpenKeyExW((HKEY)(intptr_t) rootKey, PromiseFlatString(path).get(), 0,
84 (REGSAM) mode, &mKey);
85
86 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
87 }
88
89 NS_IMETHODIMP
90 nsWindowsRegKey::Create(uint32_t rootKey, const nsAString &path, uint32_t mode)
91 {
92 Close();
93
94 DWORD disposition;
95 LONG rv = RegCreateKeyExW((HKEY)(intptr_t) rootKey, PromiseFlatString(path).get(), 0,
96 nullptr, REG_OPTION_NON_VOLATILE, (REGSAM) mode, nullptr,
97 &mKey, &disposition);
98
99 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
100 }
101
102 NS_IMETHODIMP
103 nsWindowsRegKey::OpenChild(const nsAString &path, uint32_t mode,
104 nsIWindowsRegKey **result)
105 {
106 if (NS_WARN_IF(!mKey))
107 return NS_ERROR_NOT_INITIALIZED;
108
109 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
110
111 nsresult rv = child->Open((uintptr_t) mKey, path, mode);
112 if (NS_FAILED(rv))
113 return rv;
114
115 child.swap(*result);
116 return NS_OK;
117 }
118
119 NS_IMETHODIMP
120 nsWindowsRegKey::CreateChild(const nsAString &path, uint32_t mode,
121 nsIWindowsRegKey **result)
122 {
123 if (NS_WARN_IF(!mKey))
124 return NS_ERROR_NOT_INITIALIZED;
125
126 nsCOMPtr<nsIWindowsRegKey> child = new nsWindowsRegKey();
127
128 nsresult rv = child->Create((uintptr_t) mKey, path, mode);
129 if (NS_FAILED(rv))
130 return rv;
131
132 child.swap(*result);
133 return NS_OK;
134 }
135
136 NS_IMETHODIMP
137 nsWindowsRegKey::GetChildCount(uint32_t *result)
138 {
139 if (NS_WARN_IF(!mKey))
140 return NS_ERROR_NOT_INITIALIZED;
141
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;
148
149 *result = numSubKeys;
150 return NS_OK;
151 }
152
153 NS_IMETHODIMP
154 nsWindowsRegKey::GetChildName(uint32_t index, nsAString &result)
155 {
156 if (NS_WARN_IF(!mKey))
157 return NS_ERROR_NOT_INITIALIZED;
158
159 FILETIME lastWritten;
160
161 wchar_t nameBuf[MAX_KEY_NAME_LEN + 1];
162 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
163
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?
168
169 result.Assign(nameBuf, nameLen);
170
171 return NS_OK;
172 }
173
174 NS_IMETHODIMP
175 nsWindowsRegKey::HasChild(const nsAString &name, bool *result)
176 {
177 if (NS_WARN_IF(!mKey))
178 return NS_ERROR_NOT_INITIALIZED;
179
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?
182
183 HKEY key;
184 LONG rv = RegOpenKeyExW(mKey, PromiseFlatString(name).get(), 0,
185 STANDARD_RIGHTS_READ, &key);
186
187 if ((*result = (rv == ERROR_SUCCESS && key)))
188 RegCloseKey(key);
189
190 return NS_OK;
191 }
192
193 NS_IMETHODIMP
194 nsWindowsRegKey::GetValueCount(uint32_t *result)
195 {
196 if (NS_WARN_IF(!mKey))
197 return NS_ERROR_NOT_INITIALIZED;
198
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;
205
206 *result = numValues;
207 return NS_OK;
208 }
209
210 NS_IMETHODIMP
211 nsWindowsRegKey::GetValueName(uint32_t index, nsAString &result)
212 {
213 if (NS_WARN_IF(!mKey))
214 return NS_ERROR_NOT_INITIALIZED;
215
216 wchar_t nameBuf[MAX_VALUE_NAME_LEN];
217 DWORD nameLen = sizeof(nameBuf) / sizeof(nameBuf[0]);
218
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?
223
224 result.Assign(nameBuf, nameLen);
225
226 return NS_OK;
227 }
228
229 NS_IMETHODIMP
230 nsWindowsRegKey::HasValue(const nsAString &name, bool *result)
231 {
232 if (NS_WARN_IF(!mKey))
233 return NS_ERROR_NOT_INITIALIZED;
234
235 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
236 nullptr, nullptr);
237
238 *result = (rv == ERROR_SUCCESS);
239 return NS_OK;
240 }
241
242 NS_IMETHODIMP
243 nsWindowsRegKey::RemoveChild(const nsAString &name)
244 {
245 if (NS_WARN_IF(!mKey))
246 return NS_ERROR_NOT_INITIALIZED;
247
248 LONG rv = RegDeleteKeyW(mKey, PromiseFlatString(name).get());
249
250 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
251 }
252
253 NS_IMETHODIMP
254 nsWindowsRegKey::RemoveValue(const nsAString &name)
255 {
256 if (NS_WARN_IF(!mKey))
257 return NS_ERROR_NOT_INITIALIZED;
258
259 LONG rv = RegDeleteValueW(mKey, PromiseFlatString(name).get());
260
261 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
262 }
263
264 NS_IMETHODIMP
265 nsWindowsRegKey::GetValueType(const nsAString &name, uint32_t *result)
266 {
267 if (NS_WARN_IF(!mKey))
268 return NS_ERROR_NOT_INITIALIZED;
269
270 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0,
271 (LPDWORD) result, nullptr, nullptr);
272
273 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
274 }
275
276 NS_IMETHODIMP
277 nsWindowsRegKey::ReadStringValue(const nsAString &name, nsAString &result)
278 {
279 if (NS_WARN_IF(!mKey))
280 return NS_ERROR_NOT_INITIALIZED;
281
282 DWORD type, size;
283
284 const nsString &flatName = PromiseFlatString(name);
285
286 LONG rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, nullptr, &size);
287 if (rv != ERROR_SUCCESS)
288 return NS_ERROR_FAILURE;
289
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;
294
295 // The buffer size must be a multiple of 2.
296 if (size % 2 != 0)
297 return NS_ERROR_UNEXPECTED;
298
299 if (size == 0) {
300 result.Truncate();
301 return NS_OK;
302 }
303
304 // |size| may or may not include the terminating null character.
305 DWORD resultLen = size / 2;
306
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;
312
313 rv = RegQueryValueExW(mKey, flatName.get(), 0, &type, (LPBYTE) begin.get(),
314 &size);
315
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);
319 }
320
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 > 1) {
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;
334
335 resultLen = ExpandEnvironmentStringsW(flatSource.get(),
336 wwc(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;
344 }
345 } else if (resultLen == 1) {
346 // It apparently expands to nothing (just a null terminator).
347 result.Truncate();
348 }
349 }
350
351 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
352 }
353
354 NS_IMETHODIMP
355 nsWindowsRegKey::ReadIntValue(const nsAString &name, uint32_t *result)
356 {
357 if (NS_WARN_IF(!mKey))
358 return NS_ERROR_NOT_INITIALIZED;
359
360 DWORD size = sizeof(*result);
361 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
362 (LPBYTE) result, &size);
363
364 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
365 }
366
367 NS_IMETHODIMP
368 nsWindowsRegKey::ReadInt64Value(const nsAString &name, uint64_t *result)
369 {
370 if (NS_WARN_IF(!mKey))
371 return NS_ERROR_NOT_INITIALIZED;
372
373 DWORD size = sizeof(*result);
374 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
375 (LPBYTE) result, &size);
376
377 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
378 }
379
380 NS_IMETHODIMP
381 nsWindowsRegKey::ReadBinaryValue(const nsAString &name, nsACString &result)
382 {
383 if (NS_WARN_IF(!mKey))
384 return NS_ERROR_NOT_INITIALIZED;
385
386 DWORD size;
387 LONG rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0,
388 nullptr, nullptr, &size);
389
390 if (rv != ERROR_SUCCESS)
391 return NS_ERROR_FAILURE;
392
393 if (!size) {
394 result.Truncate();
395 return NS_OK;
396 }
397
398 result.SetLength(size);
399 nsACString::iterator begin;
400 result.BeginWriting(begin);
401 if (begin.size_forward() != size)
402 return NS_ERROR_OUT_OF_MEMORY;
403
404 rv = RegQueryValueExW(mKey, PromiseFlatString(name).get(), 0, nullptr,
405 (LPBYTE) begin.get(), &size);
406
407 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
408 }
409
410 NS_IMETHODIMP
411 nsWindowsRegKey::WriteStringValue(const nsAString &name, const nsAString &value)
412 {
413 if (NS_WARN_IF(!mKey))
414 return NS_ERROR_NOT_INITIALIZED;
415
416 // Need to indicate complete size of buffer including null terminator.
417 const nsString &flatValue = PromiseFlatString(value);
418
419 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_SZ,
420 (const BYTE *) flatValue.get(),
421 (flatValue.Length() + 1) * sizeof(char16_t));
422
423 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
424 }
425
426 NS_IMETHODIMP
427 nsWindowsRegKey::WriteIntValue(const nsAString &name, uint32_t value)
428 {
429 if (NS_WARN_IF(!mKey))
430 return NS_ERROR_NOT_INITIALIZED;
431
432 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_DWORD,
433 (const BYTE *) &value, sizeof(value));
434
435 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
436 }
437
438 NS_IMETHODIMP
439 nsWindowsRegKey::WriteInt64Value(const nsAString &name, uint64_t value)
440 {
441 if (NS_WARN_IF(!mKey))
442 return NS_ERROR_NOT_INITIALIZED;
443
444 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_QWORD,
445 (const BYTE *) &value, sizeof(value));
446
447 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
448 }
449
450 NS_IMETHODIMP
451 nsWindowsRegKey::WriteBinaryValue(const nsAString &name, const nsACString &value)
452 {
453 if (NS_WARN_IF(!mKey))
454 return NS_ERROR_NOT_INITIALIZED;
455
456 const nsCString &flatValue = PromiseFlatCString(value);
457 LONG rv = RegSetValueExW(mKey, PromiseFlatString(name).get(), 0, REG_BINARY,
458 (const BYTE *) flatValue.get(), flatValue.Length());
459
460 return (rv == ERROR_SUCCESS) ? NS_OK : NS_ERROR_FAILURE;
461 }
462
463 NS_IMETHODIMP
464 nsWindowsRegKey::StartWatching(bool recurse)
465 {
466 if (NS_WARN_IF(!mKey))
467 return NS_ERROR_NOT_INITIALIZED;
468
469 if (mWatchEvent)
470 return NS_OK;
471
472 mWatchEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
473 if (!mWatchEvent)
474 return NS_ERROR_OUT_OF_MEMORY;
475
476 DWORD filter = REG_NOTIFY_CHANGE_NAME |
477 REG_NOTIFY_CHANGE_ATTRIBUTES |
478 REG_NOTIFY_CHANGE_LAST_SET |
479 REG_NOTIFY_CHANGE_SECURITY;
480
481 LONG rv = RegNotifyChangeKeyValue(mKey, recurse, filter, mWatchEvent, TRUE);
482 if (rv != ERROR_SUCCESS) {
483 StopWatching();
484 // On older versions of Windows, this call is not implemented, so simply
485 // return NS_OK in those cases and pretend that the watching is happening.
486 return (rv == ERROR_CALL_NOT_IMPLEMENTED) ? NS_OK : NS_ERROR_FAILURE;
487 }
488
489 mWatchRecursive = recurse;
490 return NS_OK;
491 }
492
493 NS_IMETHODIMP
494 nsWindowsRegKey::StopWatching()
495 {
496 if (mWatchEvent) {
497 CloseHandle(mWatchEvent);
498 mWatchEvent = nullptr;
499 }
500 return NS_OK;
501 }
502
503 NS_IMETHODIMP
504 nsWindowsRegKey::HasChanged(bool *result)
505 {
506 if (mWatchEvent && WaitForSingleObject(mWatchEvent, 0) == WAIT_OBJECT_0) {
507 // An event only gets signaled once, then it's done, so we have to set up
508 // another event to watch.
509 StopWatching();
510 StartWatching(mWatchRecursive);
511 *result = true;
512 } else {
513 *result = false;
514 }
515 return NS_OK;
516 }
517
518 NS_IMETHODIMP
519 nsWindowsRegKey::IsWatching(bool *result)
520 {
521 *result = (mWatchEvent != nullptr);
522 return NS_OK;
523 }
524
525 //-----------------------------------------------------------------------------
526
527 nsresult
528 NS_NewWindowsRegKey(nsIWindowsRegKey **result)
529 {
530 nsRefPtr<nsWindowsRegKey> key = new nsWindowsRegKey();
531 key.forget(result);
532 return NS_OK;
533 }
534
535 //-----------------------------------------------------------------------------
536
537 nsresult
538 nsWindowsRegKeyConstructor(nsISupports *delegate, const nsIID &iid,
539 void **result)
540 {
541 if (delegate)
542 return NS_ERROR_NO_AGGREGATION;
543
544 nsCOMPtr<nsIWindowsRegKey> key;
545 nsresult rv = NS_NewWindowsRegKey(getter_AddRefs(key));
546 if (NS_SUCCEEDED(rv))
547 rv = key->QueryInterface(iid, result);
548 return rv;
549 }

mercurial