|
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 } |