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
3 // found in the LICENSE file.
4
5 #include "base/win/registry.h"
6
7 #include <shlwapi.h>
8 #include <algorithm>
9
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/win/windows_version.h"
14
15 namespace base {
16 namespace win {
17
18 namespace {
19
20 // RegEnumValue() reports the number of characters from the name that were
21 // written to the buffer, not how many there are. This constant is the maximum
22 // name size, such that a buffer with this size should read any name.
23 const DWORD MAX_REGISTRY_NAME_SIZE = 16384;
24
25 // Registry values are read as BYTE* but can have wchar_t* data whose last
26 // wchar_t is truncated. This function converts the reported |byte_size| to
27 // a size in wchar_t that can store a truncated wchar_t if necessary.
to_wchar_size(DWORD byte_size)28 inline DWORD to_wchar_size(DWORD byte_size) {
29 return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
30 }
31
32 // Mask to pull WOW64 access flags out of REGSAM access.
33 const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
34
35 } // namespace
36
37 // RegKey ----------------------------------------------------------------------
38
RegKey()39 RegKey::RegKey()
40 : key_(NULL),
41 watch_event_(0),
42 wow64access_(0) {
43 }
44
RegKey(HKEY key)45 RegKey::RegKey(HKEY key)
46 : key_(key),
47 watch_event_(0),
48 wow64access_(0) {
49 }
50
RegKey(HKEY rootkey,const wchar_t * subkey,REGSAM access)51 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
52 : key_(NULL),
53 watch_event_(0),
54 wow64access_(0) {
55 if (rootkey) {
56 if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
57 Create(rootkey, subkey, access);
58 else
59 Open(rootkey, subkey, access);
60 } else {
61 DCHECK(!subkey);
62 wow64access_ = access & kWow64AccessMask;
63 }
64 }
65
~RegKey()66 RegKey::~RegKey() {
67 Close();
68 }
69
Create(HKEY rootkey,const wchar_t * subkey,REGSAM access)70 LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
71 DWORD disposition_value;
72 return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
73 }
74
CreateWithDisposition(HKEY rootkey,const wchar_t * subkey,DWORD * disposition,REGSAM access)75 LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
76 DWORD* disposition, REGSAM access) {
77 DCHECK(rootkey && subkey && access && disposition);
78 HKEY subhkey = NULL;
79 LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
80 REG_OPTION_NON_VOLATILE, access, NULL, &subhkey,
81 disposition);
82 if (result == ERROR_SUCCESS) {
83 Close();
84 key_ = subhkey;
85 wow64access_ = access & kWow64AccessMask;
86 }
87
88 return result;
89 }
90
CreateKey(const wchar_t * name,REGSAM access)91 LONG RegKey::CreateKey(const wchar_t* name, REGSAM 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 }
102 HKEY subkey = NULL;
103 LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
104 access, NULL, &subkey, NULL);
105 if (result == ERROR_SUCCESS) {
106 Close();
107 key_ = subkey;
108 wow64access_ = access & kWow64AccessMask;
109 }
110
111 return result;
112 }
113
Open(HKEY rootkey,const wchar_t * subkey,REGSAM access)114 LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
115 DCHECK(rootkey && subkey && access);
116 HKEY subhkey = NULL;
117
118 LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey);
119 if (result == ERROR_SUCCESS) {
120 Close();
121 key_ = subhkey;
122 wow64access_ = access & kWow64AccessMask;
123 }
124
125 return result;
126 }
127
OpenKey(const wchar_t * relative_key_name,REGSAM access)128 LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) {
129 DCHECK(relative_key_name && access);
130 // After the application has accessed an alternate registry view using one of
131 // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations
132 // (create, delete, or open) on child registry keys must explicitly use the
133 // same flag. Otherwise, there can be unexpected behavior.
134 // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
135 if ((access & kWow64AccessMask) != wow64access_) {
136 NOTREACHED();
137 return ERROR_INVALID_PARAMETER;
138 }
139 HKEY subkey = NULL;
140 LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey);
141
142 // We have to close the current opened key before replacing it with the new
143 // one.
144 if (result == ERROR_SUCCESS) {
145 Close();
146 key_ = subkey;
147 wow64access_ = access & kWow64AccessMask;
148 }
149 return result;
150 }
151
Close()152 void RegKey::Close() {
153 StopWatching();
154 if (key_) {
155 ::RegCloseKey(key_);
156 key_ = NULL;
157 wow64access_ = 0;
158 }
159 }
160
161 // TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400
Set(HKEY key)162 void RegKey::Set(HKEY key) {
163 if (key_ != key) {
164 Close();
165 key_ = key;
166 }
167 }
168
Take()169 HKEY RegKey::Take() {
170 DCHECK(wow64access_ == 0);
171 StopWatching();
172 HKEY key = key_;
173 key_ = NULL;
174 return key;
175 }
176
HasValue(const wchar_t * name) const177 bool RegKey::HasValue(const wchar_t* name) const {
178 return RegQueryValueEx(key_, name, 0, NULL, NULL, NULL) == ERROR_SUCCESS;
179 }
180
GetValueCount() const181 DWORD RegKey::GetValueCount() const {
182 DWORD count = 0;
183 LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
184 NULL, NULL, NULL, NULL);
185 return (result == ERROR_SUCCESS) ? count : 0;
186 }
187
GetValueNameAt(int index,std::wstring * name) const188 LONG RegKey::GetValueNameAt(int index, std::wstring* name) const {
189 wchar_t buf[256];
190 DWORD bufsize = arraysize(buf);
191 LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
192 if (r == ERROR_SUCCESS)
193 *name = buf;
194
195 return r;
196 }
197
DeleteKey(const wchar_t * name)198 LONG RegKey::DeleteKey(const wchar_t* name) {
199 DCHECK(key_);
200 DCHECK(name);
201 HKEY subkey = NULL;
202
203 // Verify the key exists before attempting delete to replicate previous
204 // behavior.
205 LONG result =
206 RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
207 if (result != ERROR_SUCCESS)
208 return result;
209 RegCloseKey(subkey);
210
211 return RegDelRecurse(key_, std::wstring(name), wow64access_);
212 }
213
DeleteEmptyKey(const wchar_t * name)214 LONG RegKey::DeleteEmptyKey(const wchar_t* name) {
215 DCHECK(key_);
216 DCHECK(name);
217
218 HKEY target_key = NULL;
219 LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_,
220 &target_key);
221
222 if (result != ERROR_SUCCESS)
223 return result;
224
225 DWORD count = 0;
226 result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count,
227 NULL, NULL, NULL, NULL);
228
229 RegCloseKey(target_key);
230
231 if (result != ERROR_SUCCESS)
232 return result;
233
234 if (count == 0)
235 return RegDeleteKeyExWrapper(key_, name, wow64access_, 0);
236
237 return ERROR_DIR_NOT_EMPTY;
238 }
239
DeleteValue(const wchar_t * value_name)240 LONG RegKey::DeleteValue(const wchar_t* value_name) {
241 DCHECK(key_);
242 LONG result = RegDeleteValue(key_, value_name);
243 return result;
244 }
245
ReadValueDW(const wchar_t * name,DWORD * out_value) const246 LONG RegKey::ReadValueDW(const wchar_t* name, DWORD* out_value) const {
247 DCHECK(out_value);
248 DWORD type = REG_DWORD;
249 DWORD size = sizeof(DWORD);
250 DWORD local_value = 0;
251 LONG result = ReadValue(name, &local_value, &size, &type);
252 if (result == ERROR_SUCCESS) {
253 if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD))
254 *out_value = local_value;
255 else
256 result = ERROR_CANTREAD;
257 }
258
259 return result;
260 }
261
ReadInt64(const wchar_t * name,int64 * out_value) const262 LONG RegKey::ReadInt64(const wchar_t* name, int64* out_value) const {
263 DCHECK(out_value);
264 DWORD type = REG_QWORD;
265 int64 local_value = 0;
266 DWORD size = sizeof(local_value);
267 LONG result = ReadValue(name, &local_value, &size, &type);
268 if (result == ERROR_SUCCESS) {
269 if ((type == REG_QWORD || type == REG_BINARY) &&
270 size == sizeof(local_value))
271 *out_value = local_value;
272 else
273 result = ERROR_CANTREAD;
274 }
275
276 return result;
277 }
278
ReadValue(const wchar_t * name,std::wstring * out_value) const279 LONG RegKey::ReadValue(const wchar_t* name, std::wstring* out_value) const {
280 DCHECK(out_value);
281 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 wchar_t raw_value[kMaxStringLength];
284 DWORD type = REG_SZ, size = sizeof(raw_value);
285 LONG result = ReadValue(name, raw_value, &size, &type);
286 if (result == ERROR_SUCCESS) {
287 if (type == REG_SZ) {
288 *out_value = raw_value;
289 } else if (type == REG_EXPAND_SZ) {
290 wchar_t expanded[kMaxStringLength];
291 size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
292 // Success: returns the number of wchar_t's copied
293 // Fail: buffer too small, returns the size required
294 // Fail: other, returns 0
295 if (size == 0 || size > kMaxStringLength) {
296 result = ERROR_MORE_DATA;
297 } else {
298 *out_value = expanded;
299 }
300 } else {
301 // Not a string. Oops.
302 result = ERROR_CANTREAD;
303 }
304 }
305
306 return result;
307 }
308
ReadValue(const wchar_t * name,void * data,DWORD * dsize,DWORD * dtype) const309 LONG RegKey::ReadValue(const wchar_t* name,
310 void* data,
311 DWORD* dsize,
312 DWORD* dtype) const {
313 LONG result = RegQueryValueEx(key_, name, 0, dtype,
314 reinterpret_cast<LPBYTE>(data), dsize);
315 return result;
316 }
317
ReadValues(const wchar_t * name,std::vector<std::wstring> * values)318 LONG RegKey::ReadValues(const wchar_t* name,
319 std::vector<std::wstring>* values) {
320 values->clear();
321
322 DWORD type = REG_MULTI_SZ;
323 DWORD size = 0;
324 LONG result = ReadValue(name, NULL, &size, &type);
325 if (FAILED(result) || size == 0)
326 return result;
327
328 if (type != REG_MULTI_SZ)
329 return ERROR_CANTREAD;
330
331 std::vector<wchar_t> buffer(size / sizeof(wchar_t));
332 result = ReadValue(name, &buffer[0], &size, NULL);
333 if (FAILED(result) || size == 0)
334 return result;
335
336 // Parse the double-null-terminated list of strings.
337 // Note: This code is paranoid to not read outside of |buf|, in the case where
338 // it may not be properly terminated.
339 const wchar_t* entry = &buffer[0];
340 const wchar_t* buffer_end = entry + (size / sizeof(wchar_t));
341 while (entry < buffer_end && entry[0] != '\0') {
342 const wchar_t* entry_end = std::find(entry, buffer_end, L'\0');
343 values->push_back(std::wstring(entry, entry_end));
344 entry = entry_end + 1;
345 }
346 return 0;
347 }
348
WriteValue(const wchar_t * name,DWORD in_value)349 LONG RegKey::WriteValue(const wchar_t* name, DWORD in_value) {
350 return WriteValue(
351 name, &in_value, static_cast<DWORD>(sizeof(in_value)), REG_DWORD);
352 }
353
WriteValue(const wchar_t * name,const wchar_t * in_value)354 LONG RegKey::WriteValue(const wchar_t * name, const wchar_t* in_value) {
355 return WriteValue(name, in_value,
356 static_cast<DWORD>(sizeof(*in_value) * (wcslen(in_value) + 1)), REG_SZ);
357 }
358
WriteValue(const wchar_t * name,const void * data,DWORD dsize,DWORD dtype)359 LONG RegKey::WriteValue(const wchar_t* name,
360 const void* data,
361 DWORD dsize,
362 DWORD dtype) {
363 DCHECK(data || !dsize);
364
365 LONG result = RegSetValueEx(key_, name, 0, dtype,
366 reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
367 return result;
368 }
369
StartWatching()370 LONG RegKey::StartWatching() {
371 DCHECK(key_);
372 if (!watch_event_)
373 watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
374
375 DWORD filter = REG_NOTIFY_CHANGE_NAME |
376 REG_NOTIFY_CHANGE_ATTRIBUTES |
377 REG_NOTIFY_CHANGE_LAST_SET |
378 REG_NOTIFY_CHANGE_SECURITY;
379
380 // Watch the registry key for a change of value.
381 LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE);
382 if (result != ERROR_SUCCESS) {
383 CloseHandle(watch_event_);
384 watch_event_ = 0;
385 }
386
387 return result;
388 }
389
HasChanged()390 bool RegKey::HasChanged() {
391 if (watch_event_) {
392 if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
393 StartWatching();
394 return true;
395 }
396 }
397 return false;
398 }
399
StopWatching()400 LONG RegKey::StopWatching() {
401 LONG result = ERROR_INVALID_HANDLE;
402 if (watch_event_) {
403 CloseHandle(watch_event_);
404 watch_event_ = 0;
405 result = ERROR_SUCCESS;
406 }
407 return result;
408 }
409
410 // static
RegDeleteKeyExWrapper(HKEY hKey,const wchar_t * lpSubKey,REGSAM samDesired,DWORD Reserved)411 LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey,
412 const wchar_t* lpSubKey,
413 REGSAM samDesired,
414 DWORD Reserved) {
415 typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD);
416
417 RegDeleteKeyExPtr reg_delete_key_ex_func =
418 reinterpret_cast<RegDeleteKeyExPtr>(
419 GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW"));
420
421 if (reg_delete_key_ex_func)
422 return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved);
423
424 // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey.
425 return RegDeleteKey(hKey, lpSubKey);
426 }
427
428 // static
RegDelRecurse(HKEY root_key,const std::wstring & name,REGSAM access)429 LONG RegKey::RegDelRecurse(HKEY root_key,
430 const std::wstring& name,
431 REGSAM access) {
432 // First, see if the key can be deleted without having to recurse.
433 LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
434 if (result == ERROR_SUCCESS)
435 return result;
436
437 HKEY target_key = NULL;
438 result = RegOpenKeyEx(
439 root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key);
440
441 if (result == ERROR_FILE_NOT_FOUND)
442 return ERROR_SUCCESS;
443 if (result != ERROR_SUCCESS)
444 return result;
445
446 std::wstring subkey_name(name);
447
448 // Check for an ending slash and add one if it is missing.
449 if (!name.empty() && subkey_name[name.length() - 1] != L'\\')
450 subkey_name += L"\\";
451
452 // Enumerate the keys
453 result = ERROR_SUCCESS;
454 const DWORD kMaxKeyNameLength = MAX_PATH;
455 const size_t base_key_length = subkey_name.length();
456 std::wstring key_name;
457 while (result == ERROR_SUCCESS) {
458 DWORD key_size = kMaxKeyNameLength;
459 result = RegEnumKeyEx(target_key,
460 0,
461 WriteInto(&key_name, kMaxKeyNameLength),
462 &key_size,
463 NULL,
464 NULL,
465 NULL,
466 NULL);
467
468 if (result != ERROR_SUCCESS)
469 break;
470
471 key_name.resize(key_size);
472 subkey_name.resize(base_key_length);
473 subkey_name += key_name;
474
475 if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS)
476 break;
477 }
478
479 RegCloseKey(target_key);
480
481 // Try again to delete the key.
482 result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0);
483
484 return result;
485 }
486
487 // RegistryValueIterator ------------------------------------------------------
488
RegistryValueIterator(HKEY root_key,const wchar_t * folder_key)489 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
490 const wchar_t* folder_key)
491 : name_(MAX_PATH, L'\0'),
492 value_(MAX_PATH, L'\0') {
493 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
494 if (result != ERROR_SUCCESS) {
495 key_ = NULL;
496 } else {
497 DWORD count = 0;
498 result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
499 NULL, NULL, NULL, NULL);
500
501 if (result != ERROR_SUCCESS) {
502 ::RegCloseKey(key_);
503 key_ = NULL;
504 } else {
505 index_ = count - 1;
506 }
507 }
508
509 Read();
510 }
511
~RegistryValueIterator()512 RegistryValueIterator::~RegistryValueIterator() {
513 if (key_)
514 ::RegCloseKey(key_);
515 }
516
ValueCount() const517 DWORD RegistryValueIterator::ValueCount() const {
518 DWORD count = 0;
519 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
520 &count, NULL, NULL, NULL, NULL);
521 if (result != ERROR_SUCCESS)
522 return 0;
523
524 return count;
525 }
526
Valid() const527 bool RegistryValueIterator::Valid() const {
528 return key_ != NULL && index_ >= 0;
529 }
530
operator ++()531 void RegistryValueIterator::operator++() {
532 --index_;
533 Read();
534 }
535
Read()536 bool RegistryValueIterator::Read() {
537 if (Valid()) {
538 DWORD capacity = static_cast<DWORD>(name_.capacity());
539 DWORD name_size = capacity;
540 // |value_size_| is in bytes. Reserve the last character for a NUL.
541 value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
542 LONG result = ::RegEnumValue(
543 key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
544 reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
545
546 if (result == ERROR_MORE_DATA) {
547 // Registry key names are limited to 255 characters and fit within
548 // MAX_PATH (which is 260) but registry value names can use up to 16,383
549 // characters and the value itself is not limited
550 // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
551 // ms724872(v=vs.85).aspx).
552 // Resize the buffers and retry if their size caused the failure.
553 DWORD value_size_in_wchars = to_wchar_size(value_size_);
554 if (value_size_in_wchars + 1 > value_.size())
555 value_.resize(value_size_in_wchars + 1, L'\0');
556 value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
557 name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
558 result = ::RegEnumValue(
559 key_, index_, WriteInto(&name_, name_size), &name_size, NULL, &type_,
560 reinterpret_cast<BYTE*>(vector_as_array(&value_)), &value_size_);
561 }
562
563 if (result == ERROR_SUCCESS) {
564 DCHECK_LT(to_wchar_size(value_size_), value_.size());
565 value_[to_wchar_size(value_size_)] = L'\0';
566 return true;
567 }
568 }
569
570 name_[0] = L'\0';
571 value_[0] = L'\0';
572 value_size_ = 0;
573 return false;
574 }
575
576 // RegistryKeyIterator --------------------------------------------------------
577
RegistryKeyIterator(HKEY root_key,const wchar_t * folder_key)578 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
579 const wchar_t* folder_key) {
580 LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
581 if (result != ERROR_SUCCESS) {
582 key_ = NULL;
583 } else {
584 DWORD count = 0;
585 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
586 NULL, NULL, NULL, NULL, NULL);
587
588 if (result != ERROR_SUCCESS) {
589 ::RegCloseKey(key_);
590 key_ = NULL;
591 } else {
592 index_ = count - 1;
593 }
594 }
595
596 Read();
597 }
598
~RegistryKeyIterator()599 RegistryKeyIterator::~RegistryKeyIterator() {
600 if (key_)
601 ::RegCloseKey(key_);
602 }
603
SubkeyCount() const604 DWORD RegistryKeyIterator::SubkeyCount() const {
605 DWORD count = 0;
606 LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
607 NULL, NULL, NULL, NULL, NULL);
608 if (result != ERROR_SUCCESS)
609 return 0;
610
611 return count;
612 }
613
Valid() const614 bool RegistryKeyIterator::Valid() const {
615 return key_ != NULL && index_ >= 0;
616 }
617
operator ++()618 void RegistryKeyIterator::operator++() {
619 --index_;
620 Read();
621 }
622
Read()623 bool RegistryKeyIterator::Read() {
624 if (Valid()) {
625 DWORD ncount = arraysize(name_);
626 FILETIME written;
627 LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
628 NULL, &written);
629 if (ERROR_SUCCESS == r)
630 return true;
631 }
632
633 name_[0] = '\0';
634 return false;
635 }
636
637 } // namespace win
638 } // namespace base
639