• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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