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