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

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: add DeleteEmptyKey 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
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 return ERROR_INVALID_PARAMETER;
grt (UTC plus 2) 2014/05/20 15:28:52 i had a thought about this yesterday after sending
Will Harris 2014/05/20 17:03:34 Done.
86 HKEY subkey = NULL; 100 HKEY subkey = NULL;
87 LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE, 101 LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
88 access, NULL, &subkey, NULL); 102 access, NULL, &subkey, NULL);
89 if (result == ERROR_SUCCESS) { 103 if (result == ERROR_SUCCESS) {
90 Close(); 104 Close();
91 105 key_ = subkey;
92 key_ = subkey;
93 } 106 }
94 107
95 return result; 108 return result;
96 } 109 }
97 110
98 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { 111 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
99 DCHECK(rootkey && subkey && access); 112 DCHECK(rootkey && subkey && access);
100 HKEY subhkey = NULL; 113 HKEY subhkey = NULL;
101 114
102 LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey); 115 LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
103 if (result == ERROR_SUCCESS) { 116 if (result == ERROR_SUCCESS) {
104 Close(); 117 Close();
105 key_ = subhkey; 118 key_ = subhkey;
119 wow64access_ = access & kWow64AccessMask;
106 } 120 }
107 121
108 return result; 122 return result;
109 } 123 }
110 124
111 LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) { 125 LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
112 DCHECK(relative_key_name && access); 126 DCHECK(relative_key_name && access);
127 // After the application has accessed an alternate registry view using one of
128 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
129 // (create, delete, or open) on child registry keys must explicitly use the
130 // same flag. Otherwise, there can be unexpected behavior.
131 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
132 if ((access & kWow64AccessMask) != wow64access_)
133 return ERROR_INVALID_PARAMETER;
grt (UTC plus 2) 2014/05/20 15:28:52 NOTREACHED(); here as well?
Will Harris 2014/05/20 17:03:34 Done.
113 HKEY subkey = NULL; 134 HKEY subkey = NULL;
114 LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey); 135 LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
115 136
116 // We have to close the current opened key before replacing it with the new 137 // We have to close the current opened key before replacing it with the new
117 // one. 138 // one.
118 if (result == ERROR_SUCCESS) { 139 if (result == ERROR_SUCCESS) {
119 Close(); 140 Close();
120 key_ = subkey; 141 key_ = subkey;
121 } 142 }
122 return result; 143 return result;
123 } 144 }
124 145
125 void RegKey::Close() { 146 void RegKey::Close() {
126 StopWatching(); 147 StopWatching();
127 if (key_) { 148 if (key_) {
128 ::RegCloseKey(key_); 149 ::RegCloseKey(key_);
129 key_ = NULL; 150 key_ = NULL;
151 wow64access_ = 0;
130 } 152 }
131 } 153 }
132 154
133 void RegKey::Set(HKEY key) { 155 void RegKey::Set(HKEY key) {
134 if (key_ != key) { 156 if (key_ != key) {
135 Close(); 157 Close();
136 key_ = key; 158 key_ = key;
159 wow64access_ = 0;
137 } 160 }
138 } 161 }
139 162
140 HKEY RegKey::Take() { 163 HKEY RegKey::Take() {
164 DCHECK(wow64access_ == 0);
141 StopWatching(); 165 StopWatching();
142 HKEY key = key_; 166 HKEY key = key_;
143 key_ = NULL; 167 key_ = NULL;
144 return key; 168 return key;
145 } 169 }
146 170
147 bool RegKey::HasValue(const wchar_t* name) const { 171 bool RegKey::HasValue(const wchar_t* name) const {
148 return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS; 172 return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
149 } 173 }
150 174
(...skipping 10 matching lines...) Expand all
161 LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL); 185 LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
162 if (r == ERROR_SUCCESS) 186 if (r == ERROR_SUCCESS)
163 *name = buf; 187 *name = buf;
164 188
165 return r; 189 return r;
166 } 190 }
167 191
168 LONG RegKey::DeleteKey(const wchar_t* name) { 192 LONG RegKey::DeleteKey(const wchar_t* name) {
169 DCHECK(key_); 193 DCHECK(key_);
170 DCHECK(name); 194 DCHECK(name);
171 LONG result = SHDeleteKey(key_, name); 195 HKEY subkey;
grt (UTC plus 2) 2014/05/20 15:28:52 nit: initialize this to NULL.
Will Harris 2014/05/20 17:03:34 Done.
172 return result; 196
197 // Verify the key exists before attempting delete to replicate previous
198 // behavior.
199 LONG result =
200 RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
201 if (result != ERROR_SUCCESS)
202 return result;
203 RegCloseKey(subkey);
204
205 return RegDelRecurse(key_, std::wstring(name), wow64access_);
206 }
207
208 LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
Will Harris 2014/05/19 23:47:25 added DeleteEmptyKey as a WOW64 compliant replacem
grt (UTC plus 2) 2014/05/20 15:28:52 According to MSDN, SHDeleteEmptyKey won't delete t
Will Harris 2014/05/20 17:03:34 Done.
209 DCHECK(key_);
210 DCHECK(name);
211
212 return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
173 } 213 }
174 214
175 LONG RegKey::DeleteValue(const wchar_t* value_name) { 215 LONG RegKey::DeleteValue(const wchar_t* value_name) {
176 DCHECK(key_); 216 DCHECK(key_);
177 LONG result = RegDeleteValue(key_, value_name); 217 LONG result = RegDeleteValue(key_, value_name);
178 return result; 218 return result;
179 } 219 }
180 220
181 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const { 221 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
182 DCHECK(out_value); 222 DCHECK(out_value);
(...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after
335 LONG RegKey::StopWatching() { 375 LONG RegKey::StopWatching() {
336 LONG result = ERROR_INVALID_HANDLE; 376 LONG result = ERROR_INVALID_HANDLE;
337 if (watch_event_) { 377 if (watch_event_) {
338 CloseHandle(watch_event_); 378 CloseHandle(watch_event_);
339 watch_event_ = 0; 379 watch_event_ = 0;
340 result = ERROR_SUCCESS; 380 result = ERROR_SUCCESS;
341 } 381 }
342 return result; 382 return result;
343 } 383 }
344 384
385 // static
386 LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
387 const wchar_t* lpSubKey,
388 REGSAM samDesired,
389 DWORD Reserved) {
390 typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
391
392 RegDeleteKeyExPtr reg_delete_key_ex_func =
393 reinterpret_cast<RegDeleteKeyExPtr>(
394 GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
395
396 if (reg_delete_key_ex_func)
397 return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
398
399 // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
400 return RegDeleteKey(hKey, lpSubKey);
401 }
402
403 // static
404 LONG RegKey::RegDelRecurse(HKEY root_key,
405 const std::wstring& name,
406 REGSAM access) {
407 if (name.empty()) {
408 NOTREACHED();
409 return ERROR_INVALID_PARAMETER;
410 }
411
412 // First, see if the key can be deleted without having to recurse.
413 LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
414
grt (UTC plus 2) 2014/05/20 15:28:52 nit: remove empty line
Will Harris 2014/05/20 17:03:34 Done.
415 if (result == ERROR_SUCCESS)
416 return result;
417
418 HKEY target_key;
grt (UTC plus 2) 2014/05/20 15:28:52 nit: initialize to NULL
Will Harris 2014/05/20 17:03:34 Done.
419 result = RegOpenKeyEx(
420 root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key);
421
422 if (result == ERROR_FILE_NOT_FOUND)
423 return ERROR_SUCCESS;
424 if (result != ERROR_SUCCESS)
425 return result;
426
427 // Copy string before modifying it.
grt (UTC plus 2) 2014/05/20 15:28:52 remove unneeded comment (name is const, so it can'
Will Harris 2014/05/20 17:03:34 Done.
428 std::wstring subkey_name(name);
429
430 // Check for an ending slash and add one if it is missing.
431 if (subkey_name[name.length() - 1] != L'\\') {
grt (UTC plus 2) 2014/05/20 15:28:52 nit: no braces for single-liner (did "git cl forma
Will Harris 2014/05/20 17:03:34 I left those in by accident and git cl format didn
432 subkey_name += L"\\";
433 }
434
435 // Enumerate the keys
436 result = ERROR_SUCCESS;
437 const DWORD kMaxKeyNameLength = MAX_PATH;
438 const size_t base_key_length = subkey_name.length();
439
grt (UTC plus 2) 2014/05/20 15:28:52 nit: remove empty line
Will Harris 2014/05/20 17:03:34 Done.
440 std::wstring key_name;
441 while (result == ERROR_SUCCESS) {
442 DWORD key_size = kMaxKeyNameLength;
443 result = RegEnumKeyEx(target_key,
444 0,
445 WriteInto(&key_name, kMaxKeyNameLength),
446 &key_size,
447 NULL,
448 NULL,
449 NULL,
450 NULL);
451
452 if (result != ERROR_SUCCESS)
453 break;
454
455 key_name.resize(key_size);
456 subkey_name.resize(base_key_length);
457 subkey_name += key_name;
458
459 if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
460 break;
461 }
462
463 RegCloseKey(target_key);
464
465 // Try again to delete the key.
466 result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
467
468 return result;
469 }
470
345 // RegistryValueIterator ------------------------------------------------------ 471 // RegistryValueIterator ------------------------------------------------------
346 472
347 RegistryValueIterator::RegistryValueIterator(HKEY root_key, 473 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
348 const wchar_t* folder_key) 474 const wchar_t* folder_key)
349 : name_(MAX_PATH, L'\0'), 475 : name_(MAX_PATH, L'\0'),
350 value_(MAX_PATH, L'\0') { 476 value_(MAX_PATH, L'\0') {
351 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); 477 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
352 if (result != ERROR_SUCCESS) { 478 if (result != ERROR_SUCCESS) {
353 key_ = NULL; 479 key_ = NULL;
354 } else { 480 } else {
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
487 if (ERROR_SUCCESS == r) 613 if (ERROR_SUCCESS == r)
488 return true; 614 return true;
489 } 615 }
490 616
491 name_[0] = '\0'; 617 name_[0] = '\0';
492 return false; 618 return false;
493 } 619 }
494 620
495 } // namespace win 621 } // namespace win
496 } // namespace base 622 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698