Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(645)

Side by Side Diff: base/win/registry.cc

Issue 275103012: Add WOW64 support and DeleteEmptyKey to base::win::registry. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « base/win/registry.h ('k') | base/win/registry_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/win/registry.h" 5 #include "base/win/registry.h"
6 6
7 #include <shlwapi.h> 7 #include <shlwapi.h>
8 #include <algorithm> 8 #include <algorithm>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "base/threading/thread_restrictions.h" 12 #include "base/threading/thread_restrictions.h"
13 13 #include "base/win/windows_version.h"
14 #pragma comment(lib, "shlwapi.lib") // for SHDeleteKey
15 14
16 namespace base { 15 namespace base {
17 namespace win { 16 namespace win {
18 17
19 namespace { 18 namespace {
20 19
21 // RegEnumValue() reports the number of characters from the name that were 20 // RegEnumValue() reports the number of characters from the name that were
22 // written to the buffer, not how many there are. This constant is the maximum 21 // written to the buffer, not how many there are. This constant is the maximum
23 // name size, such that a buffer with this size should read any name. 22 // name size, such that a buffer with this size should read any name.
24 const DWORD MAX_REGISTRY_NAME_SIZE = 16384; 23 const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
25 24
26 // Registry values are read as BYTE* but can have wchar_t* data whose last 25 // Registry values are read as BYTE* but can have wchar_t* data whose last
27 // wchar_t is truncated. This function converts the reported |byte_size| to 26 // wchar_t is truncated. This function converts the reported |byte_size| to
28 // a size in wchar_t that can store a truncated wchar_t if necessary. 27 // a size in wchar_t that can store a truncated wchar_t if necessary.
29 inline DWORD to_wchar_size(DWORD byte_size) { 28 inline DWORD to_wchar_size(DWORD byte_size) {
30 return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t); 29 return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
31 } 30 }
32 31
32 // Mask to pull WOW64 access flags out of REGSAM access.
33 const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
34
33 } // namespace 35 } // namespace
34 36
35 // RegKey ---------------------------------------------------------------------- 37 // RegKey ----------------------------------------------------------------------
36 38
37 RegKey::RegKey() 39 RegKey::RegKey()
38 : key_(NULL), 40 : key_(NULL),
39 watch_event_(0) { 41 watch_event_(0),
42 wow64access_(0) {
40 } 43 }
41 44
42 RegKey::RegKey(HKEY key) 45 RegKey::RegKey(HKEY key)
43 : key_(key), 46 : key_(key),
44 watch_event_(0) { 47 watch_event_(0),
48 wow64access_(0) {
45 } 49 }
46 50
47 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) 51 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
48 : key_(NULL), 52 : key_(NULL),
49 watch_event_(0) { 53 watch_event_(0),
54 wow64access_(0) {
50 if (rootkey) { 55 if (rootkey) {
51 if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) 56 if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
52 Create(rootkey, subkey, access); 57 Create(rootkey, subkey, access);
53 else 58 else
54 Open(rootkey, subkey, access); 59 Open(rootkey, subkey, access);
55 } else { 60 } else {
56 DCHECK(!subkey); 61 DCHECK(!subkey);
62 wow64access_ = access & kWow64AccessMask;
57 } 63 }
58 } 64 }
59 65
60 RegKey::~RegKey() { 66 RegKey::~RegKey() {
61 Close(); 67 Close();
62 } 68 }
63 69
64 LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { 70 LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
65 DWORD disposition_value; 71 DWORD disposition_value;
66 return CreateWithDisposition(rootkey, subkey, &disposition_value, access); 72 return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
67 } 73 }
68 74
69 LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, 75 LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
70 DWORD* disposition, REGSAM access) { 76 DWORD* disposition, REGSAM access) {
71 DCHECK(rootkey && subkey && access && disposition); 77 DCHECK(rootkey && subkey && access && disposition);
72 HKEY subhkey = NULL; 78 HKEY subhkey = NULL;
73 LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL, 79 LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
74 REG_OPTION_NON_VOLATILE, access, NULL, &subhkey, 80 REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
75 disposition); 81 disposition);
76 if (result == ERROR_SUCCESS) { 82 if (result == ERROR_SUCCESS) {
77 Close(); 83 Close();
78 key_ = subhkey; 84 key_ = subhkey;
85 wow64access_ = access & kWow64AccessMask;
79 } 86 }
80 87
81 return result; 88 return result;
82 } 89 }
83 90
84 LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) { 91 LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
85 DCHECK(name && access); 92 DCHECK(name && access);
93 // After the application has accessed an alternate registry view using one of
94 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
95 // (create, delete, or open) on child registry keys must explicitly use the
96 // same flag. Otherwise, there can be unexpected behavior.
97 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
98 if ((access & kWow64AccessMask) != wow64access_) {
99 NOTREACHED();
100 return ERROR_INVALID_PARAMETER;
101 }
86 HKEY subkey = NULL; 102 HKEY subkey = NULL;
87 LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE, 103 LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
88 access, NULL, &subkey, NULL); 104 access, NULL, &subkey, NULL);
89 if (result == ERROR_SUCCESS) { 105 if (result == ERROR_SUCCESS) {
90 Close(); 106 Close();
91 107 key_ = subkey;
92 key_ = subkey;
93 } 108 }
94 109
95 return result; 110 return result;
96 } 111 }
97 112
98 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { 113 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
99 DCHECK(rootkey && subkey && access); 114 DCHECK(rootkey && subkey && access);
100 HKEY subhkey = NULL; 115 HKEY subhkey = NULL;
101 116
102 LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey); 117 LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
103 if (result == ERROR_SUCCESS) { 118 if (result == ERROR_SUCCESS) {
104 Close(); 119 Close();
105 key_ = subhkey; 120 key_ = subhkey;
121 wow64access_ = access & kWow64AccessMask;
106 } 122 }
107 123
108 return result; 124 return result;
109 } 125 }
110 126
111 LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) { 127 LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
112 DCHECK(relative_key_name && access); 128 DCHECK(relative_key_name && access);
129 // After the application has accessed an alternate registry view using one of
130 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
131 // (create, delete, or open) on child registry keys must explicitly use the
132 // same flag. Otherwise, there can be unexpected behavior.
133 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
134 if ((access & kWow64AccessMask) != wow64access_) {
135 NOTREACHED();
136 return ERROR_INVALID_PARAMETER;
137 }
113 HKEY subkey = NULL; 138 HKEY subkey = NULL;
114 LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey); 139 LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
115 140
116 // We have to close the current opened key before replacing it with the new 141 // We have to close the current opened key before replacing it with the new
117 // one. 142 // one.
118 if (result == ERROR_SUCCESS) { 143 if (result == ERROR_SUCCESS) {
119 Close(); 144 Close();
120 key_ = subkey; 145 key_ = subkey;
121 } 146 }
122 return result; 147 return result;
123 } 148 }
124 149
125 void RegKey::Close() { 150 void RegKey::Close() {
126 StopWatching(); 151 StopWatching();
127 if (key_) { 152 if (key_) {
128 ::RegCloseKey(key_); 153 ::RegCloseKey(key_);
129 key_ = NULL; 154 key_ = NULL;
155 wow64access_ = 0;
130 } 156 }
131 } 157 }
132 158
159 // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
133 void RegKey::Set(HKEY key) { 160 void RegKey::Set(HKEY key) {
134 if (key_ != key) { 161 if (key_ != key) {
135 Close(); 162 Close();
136 key_ = key; 163 key_ = key;
164 wow64access_ = 0;
137 } 165 }
138 } 166 }
139 167
140 HKEY RegKey::Take() { 168 HKEY RegKey::Take() {
169 DCHECK(wow64access_ == 0);
141 StopWatching(); 170 StopWatching();
142 HKEY key = key_; 171 HKEY key = key_;
143 key_ = NULL; 172 key_ = NULL;
144 return key; 173 return key;
145 } 174 }
146 175
147 bool RegKey::HasValue(const wchar_t* name) const { 176 bool RegKey::HasValue(const wchar_t* name) const {
148 return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS; 177 return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
149 } 178 }
150 179
(...skipping 10 matching lines...) Expand all
161 LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL); 190 LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
162 if (r == ERROR_SUCCESS) 191 if (r == ERROR_SUCCESS)
163 *name = buf; 192 *name = buf;
164 193
165 return r; 194 return r;
166 } 195 }
167 196
168 LONG RegKey::DeleteKey(const wchar_t* name) { 197 LONG RegKey::DeleteKey(const wchar_t* name) {
169 DCHECK(key_); 198 DCHECK(key_);
170 DCHECK(name); 199 DCHECK(name);
171 LONG result = SHDeleteKey(key_, name); 200 HKEY subkey = NULL;
172 return result; 201
202 // Verify the key exists before attempting delete to replicate previous
203 // behavior.
204 LONG result =
205 RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
206 if (result != ERROR_SUCCESS)
207 return result;
208 RegCloseKey(subkey);
209
210 return RegDelRecurse(key_, std::wstring(name), wow64access_);
211 }
212
213 LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
214 DCHECK(key_);
215 DCHECK(name);
216
217 HKEY target_key = NULL;
218 LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_,
219 &target_key);
220
221 if (result != ERROR_SUCCESS)
222 return result;
223
224 DWORD count = 0;
225 result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
226 NULL, NULL, NULL, NULL);
227
228 RegCloseKey(target_key);
229
230 if (result != ERROR_SUCCESS)
231 return result;
232
233 if (count == 0)
234 return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
235
236 return ERROR_DIR_NOT_EMPTY;
173 } 237 }
174 238
175 LONG RegKey::DeleteValue(const wchar_t* value_name) { 239 LONG RegKey::DeleteValue(const wchar_t* value_name) {
176 DCHECK(key_); 240 DCHECK(key_);
177 LONG result = RegDeleteValue(key_, value_name); 241 LONG result = RegDeleteValue(key_, value_name);
178 return result; 242 return result;
179 } 243 }
180 244
181 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const { 245 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
182 DCHECK(out_value); 246 DCHECK(out_value);
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 LONG RegKey::StopWatching() { 399 LONG RegKey::StopWatching() {
336 LONG result = ERROR_INVALID_HANDLE; 400 LONG result = ERROR_INVALID_HANDLE;
337 if (watch_event_) { 401 if (watch_event_) {
338 CloseHandle(watch_event_); 402 CloseHandle(watch_event_);
339 watch_event_ = 0; 403 watch_event_ = 0;
340 result = ERROR_SUCCESS; 404 result = ERROR_SUCCESS;
341 } 405 }
342 return result; 406 return result;
343 } 407 }
344 408
409 // static
410 LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
411 const wchar_t* lpSubKey,
412 REGSAM samDesired,
413 DWORD Reserved) {
414 typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
415
416 RegDeleteKeyExPtr reg_delete_key_ex_func =
417 reinterpret_cast<RegDeleteKeyExPtr>(
418 GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
419
420 if (reg_delete_key_ex_func)
421 return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
422
423 // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
424 return RegDeleteKey(hKey, lpSubKey);
425 }
426
427 // static
428 LONG RegKey::RegDelRecurse(HKEY root_key,
429 const std::wstring& name,
430 REGSAM access) {
431 // First, see if the key can be deleted without having to recurse.
432 LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
433 if (result == ERROR_SUCCESS)
434 return result;
435
436 HKEY target_key = NULL;
437 result = RegOpenKeyEx(
438 root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key);
439
440 if (result == ERROR_FILE_NOT_FOUND)
441 return ERROR_SUCCESS;
442 if (result != ERROR_SUCCESS)
443 return result;
444
445 std::wstring subkey_name(name);
446
447 // Check for an ending slash and add one if it is missing.
448 if (!name.empty() && subkey_name[name.length() - 1] != L'\\')
449 subkey_name += L"\\";
450
451 // Enumerate the keys
452 result = ERROR_SUCCESS;
453 const DWORD kMaxKeyNameLength = MAX_PATH;
454 const size_t base_key_length = subkey_name.length();
455 std::wstring key_name;
456 while (result == ERROR_SUCCESS) {
457 DWORD key_size = kMaxKeyNameLength;
458 result = RegEnumKeyEx(target_key,
459 0,
460 WriteInto(&key_name, kMaxKeyNameLength),
461 &key_size,
462 NULL,
463 NULL,
464 NULL,
465 NULL);
466
467 if (result != ERROR_SUCCESS)
468 break;
469
470 key_name.resize(key_size);
471 subkey_name.resize(base_key_length);
472 subkey_name += key_name;
473
474 if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
475 break;
476 }
477
478 RegCloseKey(target_key);
479
480 // Try again to delete the key.
481 result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
482
483 return result;
484 }
485
345 // RegistryValueIterator ------------------------------------------------------ 486 // RegistryValueIterator ------------------------------------------------------
346 487
347 RegistryValueIterator::RegistryValueIterator(HKEY root_key, 488 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
348 const wchar_t* folder_key) 489 const wchar_t* folder_key)
349 : name_(MAX_PATH, L'\0'), 490 : name_(MAX_PATH, L'\0'),
350 value_(MAX_PATH, L'\0') { 491 value_(MAX_PATH, L'\0') {
351 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); 492 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
352 if (result != ERROR_SUCCESS) { 493 if (result != ERROR_SUCCESS) {
353 key_ = NULL; 494 key_ = NULL;
354 } else { 495 } else {
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 if (ERROR_SUCCESS == r) 628 if (ERROR_SUCCESS == r)
488 return true; 629 return true;
489 } 630 }
490 631
491 name_[0] = '\0'; 632 name_[0] = '\0';
492 return false; 633 return false;
493 } 634 }
494 635
495 } // namespace win 636 } // namespace win
496 } // namespace base 637 } // namespace base
OLDNEW
« no previous file with comments | « base/win/registry.h ('k') | base/win/registry_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698