registry.cc revision 21d179b334e59e9a3bfcaed4c4430bef1bc5759d
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/win/registry.h"
6
7#include <shlwapi.h>
8
9#include "base/logging.h"
10#include "base/thread_restrictions.h"
11
12#pragma comment(lib, "shlwapi.lib")  // for SHDeleteKey
13
14namespace base {
15namespace win {
16
17RegistryValueIterator::RegistryValueIterator(HKEY root_key,
18                                             const wchar_t* folder_key) {
19  base::ThreadRestrictions::AssertIOAllowed();
20
21  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
22  if (result != ERROR_SUCCESS) {
23    key_ = NULL;
24  } else {
25    DWORD count = 0;
26    result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
27                               NULL, NULL, NULL, NULL);
28
29    if (result != ERROR_SUCCESS) {
30      ::RegCloseKey(key_);
31      key_ = NULL;
32    } else {
33      index_ = count - 1;
34    }
35  }
36
37  Read();
38}
39
40RegistryValueIterator::~RegistryValueIterator() {
41  base::ThreadRestrictions::AssertIOAllowed();
42  if (key_)
43    ::RegCloseKey(key_);
44}
45
46bool RegistryValueIterator::Valid() const {
47  return key_ != NULL && index_ >= 0;
48}
49
50void RegistryValueIterator::operator++() {
51  --index_;
52  Read();
53}
54
55bool RegistryValueIterator::Read() {
56  base::ThreadRestrictions::AssertIOAllowed();
57  if (Valid()) {
58    DWORD ncount = arraysize(name_);
59    value_size_ = sizeof(value_);
60    LRESULT r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_,
61                               reinterpret_cast<BYTE*>(value_), &value_size_);
62    if (ERROR_SUCCESS == r)
63      return true;
64  }
65
66  name_[0] = '\0';
67  value_[0] = '\0';
68  value_size_ = 0;
69  return false;
70}
71
72DWORD RegistryValueIterator::ValueCount() const {
73  base::ThreadRestrictions::AssertIOAllowed();
74  DWORD count = 0;
75  HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
76                                     &count, NULL, NULL, NULL, NULL);
77
78  if (result != ERROR_SUCCESS)
79    return 0;
80
81  return count;
82}
83
84RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
85                                         const wchar_t* folder_key) {
86  base::ThreadRestrictions::AssertIOAllowed();
87  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
88  if (result != ERROR_SUCCESS) {
89    key_ = NULL;
90  } else {
91    DWORD count = 0;
92    HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
93                                       NULL, NULL, NULL, NULL, NULL);
94
95    if (result != ERROR_SUCCESS) {
96      ::RegCloseKey(key_);
97      key_ = NULL;
98    } else {
99      index_ = count - 1;
100    }
101  }
102
103  Read();
104}
105
106RegistryKeyIterator::~RegistryKeyIterator() {
107  base::ThreadRestrictions::AssertIOAllowed();
108  if (key_)
109    ::RegCloseKey(key_);
110}
111
112bool RegistryKeyIterator::Valid() const {
113  return key_ != NULL && index_ >= 0;
114}
115
116void RegistryKeyIterator::operator++() {
117  --index_;
118  Read();
119}
120
121bool RegistryKeyIterator::Read() {
122  base::ThreadRestrictions::AssertIOAllowed();
123  if (Valid()) {
124    DWORD ncount = arraysize(name_);
125    FILETIME written;
126    LRESULT r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
127                               NULL, &written);
128    if (ERROR_SUCCESS == r)
129      return true;
130  }
131
132  name_[0] = '\0';
133  return false;
134}
135
136DWORD RegistryKeyIterator::SubkeyCount() const {
137  base::ThreadRestrictions::AssertIOAllowed();
138  DWORD count = 0;
139  HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
140                                     NULL, NULL, NULL, NULL, NULL);
141
142  if (result != ERROR_SUCCESS)
143    return 0;
144
145  return count;
146}
147
148RegKey::RegKey()
149    : key_(NULL),
150      watch_event_(0) {
151}
152
153RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
154    : key_(NULL),
155      watch_event_(0) {
156  base::ThreadRestrictions::AssertIOAllowed();
157  if (rootkey) {
158    if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
159      Create(rootkey, subkey, access);
160    else
161      Open(rootkey, subkey, access);
162  } else {
163    DCHECK(!subkey);
164  }
165}
166
167RegKey::~RegKey() {
168  Close();
169}
170
171void RegKey::Close() {
172  base::ThreadRestrictions::AssertIOAllowed();
173  StopWatching();
174  if (key_) {
175    ::RegCloseKey(key_);
176    key_ = NULL;
177  }
178}
179
180bool RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
181  DWORD disposition_value;
182  return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
183}
184
185bool RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
186                                   DWORD* disposition, REGSAM access) {
187  base::ThreadRestrictions::AssertIOAllowed();
188  DCHECK(rootkey && subkey && access && disposition);
189  Close();
190
191  LONG result = RegCreateKeyEx(rootkey,
192                               subkey,
193                               0,
194                               NULL,
195                               REG_OPTION_NON_VOLATILE,
196                               access,
197                               NULL,
198                               &key_,
199                               disposition);
200  if (result != ERROR_SUCCESS) {
201    key_ = NULL;
202    return false;
203  }
204
205  return true;
206}
207
208bool RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
209  base::ThreadRestrictions::AssertIOAllowed();
210  DCHECK(rootkey && subkey && access);
211  Close();
212
213  LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_);
214  if (result != ERROR_SUCCESS) {
215    key_ = NULL;
216    return false;
217  }
218  return true;
219}
220
221bool RegKey::CreateKey(const wchar_t* name, REGSAM access) {
222  base::ThreadRestrictions::AssertIOAllowed();
223  DCHECK(name && access);
224
225  HKEY subkey = NULL;
226  LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
227                               access, NULL, &subkey, NULL);
228  Close();
229
230  key_ = subkey;
231  return (result == ERROR_SUCCESS);
232}
233
234bool RegKey::OpenKey(const wchar_t* name, REGSAM access) {
235  base::ThreadRestrictions::AssertIOAllowed();
236  DCHECK(name && access);
237
238  HKEY subkey = NULL;
239  LONG result = RegOpenKeyEx(key_, name, 0, access, &subkey);
240
241  Close();
242
243  key_ = subkey;
244  return (result == ERROR_SUCCESS);
245}
246
247DWORD RegKey::ValueCount() const {
248  base::ThreadRestrictions::AssertIOAllowed();
249  DWORD count = 0;
250  HRESULT result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL,
251                                   NULL, &count, NULL, NULL, NULL, NULL);
252  return (result != ERROR_SUCCESS) ? 0 : count;
253}
254
255bool RegKey::ReadName(int index, std::wstring* name) const {
256  base::ThreadRestrictions::AssertIOAllowed();
257  wchar_t buf[256];
258  DWORD bufsize = arraysize(buf);
259  LRESULT r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL,
260                             NULL, NULL);
261  if (r != ERROR_SUCCESS)
262    return false;
263  if (name)
264    *name = buf;
265  return true;
266}
267
268bool RegKey::ValueExists(const wchar_t* name) {
269  base::ThreadRestrictions::AssertIOAllowed();
270  if (!key_)
271    return false;
272  HRESULT result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL);
273  return (result == ERROR_SUCCESS);
274}
275
276bool RegKey::ReadValue(const wchar_t* name, void* data,
277                       DWORD* dsize, DWORD* dtype) const {
278  base::ThreadRestrictions::AssertIOAllowed();
279  if (!key_)
280    return false;
281  HRESULT result = RegQueryValueEx(key_, name, 0, dtype,
282                                   reinterpret_cast<LPBYTE>(data), dsize);
283  return (result == ERROR_SUCCESS);
284}
285
286bool RegKey::ReadValue(const wchar_t* name, std::wstring* value) const {
287  base::ThreadRestrictions::AssertIOAllowed();
288  DCHECK(value);
289  const size_t kMaxStringLength = 1024;  // This is after expansion.
290  // Use the one of the other forms of ReadValue if 1024 is too small for you.
291  wchar_t raw_value[kMaxStringLength];
292  DWORD type = REG_SZ, size = sizeof(raw_value);
293  if (ReadValue(name, raw_value, &size, &type)) {
294    if (type == REG_SZ) {
295      *value = raw_value;
296    } else if (type == REG_EXPAND_SZ) {
297      wchar_t expanded[kMaxStringLength];
298      size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
299      // Success: returns the number of wchar_t's copied
300      // Fail: buffer too small, returns the size required
301      // Fail: other, returns 0
302      if (size == 0 || size > kMaxStringLength)
303        return false;
304      *value = expanded;
305    } else {
306      // Not a string. Oops.
307      return false;
308    }
309    return true;
310  }
311
312  return false;
313}
314
315bool RegKey::ReadValueDW(const wchar_t* name, DWORD* value) const {
316  DCHECK(value);
317  DWORD type = REG_DWORD;
318  DWORD size = sizeof(DWORD);
319  DWORD result = 0;
320  if (ReadValue(name, &result, &size, &type) &&
321      (type == REG_DWORD || type == REG_BINARY) &&
322      size == sizeof(DWORD)) {
323    *value = result;
324    return true;
325  }
326
327  return false;
328}
329
330bool RegKey::WriteValue(const wchar_t* name, const void * data,
331                        DWORD dsize, DWORD dtype) {
332  base::ThreadRestrictions::AssertIOAllowed();
333  DCHECK(data);
334
335  if (!key_)
336    return false;
337
338  HRESULT result = RegSetValueEx(
339      key_,
340      name,
341      0,
342      dtype,
343      reinterpret_cast<LPBYTE>(const_cast<void*>(data)),
344      dsize);
345  return (result == ERROR_SUCCESS);
346}
347
348bool RegKey::WriteValue(const wchar_t * name, const wchar_t* value) {
349  return WriteValue(name, value,
350      static_cast<DWORD>(sizeof(*value) * (wcslen(value) + 1)), REG_SZ);
351}
352
353bool RegKey::WriteValue(const wchar_t* name, DWORD value) {
354  return WriteValue(name, &value,
355      static_cast<DWORD>(sizeof(value)), REG_DWORD);
356}
357
358bool RegKey::DeleteKey(const wchar_t* name) {
359  base::ThreadRestrictions::AssertIOAllowed();
360  if (!key_)
361    return false;
362  LSTATUS ret = SHDeleteKey(key_, name);
363  if (ERROR_SUCCESS != ret)
364    SetLastError(ret);
365  return ERROR_SUCCESS == ret;
366}
367
368bool RegKey::DeleteValue(const wchar_t* value_name) {
369  base::ThreadRestrictions::AssertIOAllowed();
370  DCHECK(value_name);
371  HRESULT result = RegDeleteValue(key_, value_name);
372  return (result == ERROR_SUCCESS);
373}
374
375bool RegKey::StartWatching() {
376  if (!watch_event_)
377    watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
378
379  DWORD filter = REG_NOTIFY_CHANGE_NAME |
380                 REG_NOTIFY_CHANGE_ATTRIBUTES |
381                 REG_NOTIFY_CHANGE_LAST_SET |
382                 REG_NOTIFY_CHANGE_SECURITY;
383
384  // Watch the registry key for a change of value.
385  HRESULT result = RegNotifyChangeKeyValue(key_, TRUE, filter,
386                                           watch_event_, TRUE);
387  if (SUCCEEDED(result)) {
388    return true;
389  } else {
390    CloseHandle(watch_event_);
391    watch_event_ = 0;
392    return false;
393  }
394}
395
396bool RegKey::StopWatching() {
397  if (watch_event_) {
398    CloseHandle(watch_event_);
399    watch_event_ = 0;
400    return true;
401  }
402  return false;
403}
404
405bool RegKey::HasChanged() {
406  if (watch_event_) {
407    if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
408      StartWatching();
409      return true;
410    }
411  }
412  return false;
413}
414
415}  // namespace win
416}  // namespace base
417