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