172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/win/registry.h"
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <shlwapi.h>
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
93345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/logging.h"
103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_restrictions.h"
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
123345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#pragma comment(lib, "shlwapi.lib")  // for SHDeleteKey
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
14731df977c0511bca2206b5f333555b1205ff1f43Iain Merricknamespace base {
15731df977c0511bca2206b5f333555b1205ff1f43Iain Merricknamespace win {
16731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
1772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// RegKey ----------------------------------------------------------------------
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
193345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickRegKey::RegKey()
203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    : key_(NULL),
213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      watch_event_(0) {
223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
243345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickRegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    : key_(NULL),
263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      watch_event_(0) {
27513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (rootkey) {
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK))
303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      Create(rootkey, subkey, access);
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    else
323345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      Open(rootkey, subkey, access);
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else {
343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    DCHECK(!subkey);
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
383345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickRegKey::~RegKey() {
393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  Close();
403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
4272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DWORD disposition_value;
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
4772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey,
48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                   DWORD* disposition, REGSAM access) {
49513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(rootkey && subkey && access && disposition);
513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  Close();
523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
5372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL,
5472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                               REG_OPTION_NON_VOLATILE, access, NULL, &key_,
553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                               disposition);
5672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) {
60513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(rootkey && subkey && access);
623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  Close();
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_);
6572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
6872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::CreateKey(const wchar_t* name, REGSAM access) {
69513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(name && access);
71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  HKEY subkey = NULL;
733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE,
743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                               access, NULL, &subkey, NULL);
753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  Close();
76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  key_ = subkey;
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::OpenKey(const wchar_t* name, REGSAM access) {
82513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(name && access);
84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  HKEY subkey = NULL;
863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  LONG result = RegOpenKeyEx(key_, name, 0, access, &subkey);
87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  Close();
89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  key_ = subkey;
9172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid RegKey::Close() {
9572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
9672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  StopWatching();
9772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (key_) {
9872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    ::RegCloseKey(key_);
9972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    key_ = NULL;
10072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
10321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian MonsenDWORD RegKey::ValueCount() const {
104513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DWORD count = 0;
10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                NULL, NULL, NULL, NULL);
108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return (result != ERROR_SUCCESS) ? 0 : count;
109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::ReadName(int index, std::wstring* name) const {
112513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
1133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  wchar_t buf[256];
1143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DWORD bufsize = arraysize(buf);
11572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, NULL, NULL);
11672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (r == ERROR_SUCCESS)
117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    *name = buf;
11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
11972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return r;
12072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
12172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
12272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::DeleteKey(const wchar_t* name) {
12372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
12472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(key_);
12572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(name);
12672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = SHDeleteKey(key_, name);
12772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
12972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
13072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::DeleteValue(const wchar_t* value_name) {
13172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(key_);
13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(value_name);
13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegDeleteValue(key_, value_name);
13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
13872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool RegKey::ValueExists(const wchar_t* name) const {
139513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
14072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL);
14172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result == ERROR_SUCCESS;
142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
14472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::ReadValue(const wchar_t* name, void* data, DWORD* dsize,
14572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                       DWORD* dtype) const {
146513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
14772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegQueryValueEx(key_, name, 0, dtype,
14872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                reinterpret_cast<LPBYTE>(data), dsize);
14972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
15272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::ReadValue(const wchar_t* name, std::wstring* value) const {
153513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
1543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(value);
1553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const size_t kMaxStringLength = 1024;  // This is after expansion.
156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Use the one of the other forms of ReadValue if 1024 is too small for you.
1573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  wchar_t raw_value[kMaxStringLength];
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DWORD type = REG_SZ, size = sizeof(raw_value);
15972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = ReadValue(name, raw_value, &size, &type);
16072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result == ERROR_SUCCESS) {
161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (type == REG_SZ) {
162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      *value = raw_value;
163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else if (type == REG_EXPAND_SZ) {
1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      wchar_t expanded[kMaxStringLength];
165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
1663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      // Success: returns the number of wchar_t's copied
167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Fail: buffer too small, returns the size required
168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Fail: other, returns 0
16972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      if (size == 0 || size > kMaxStringLength) {
17072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        result = ERROR_MORE_DATA;
17172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      } else {
17272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        *value = expanded;
17372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      }
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else {
175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      // Not a string. Oops.
17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = ERROR_CANTREAD;
177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
18072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
18372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::ReadValueDW(const wchar_t* name, DWORD* value) const {
1843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(value);
1853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DWORD type = REG_DWORD;
1863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DWORD size = sizeof(DWORD);
18772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DWORD local_value = 0;
18872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = ReadValue(name, &local_value, &size, &type);
18972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result == ERROR_SUCCESS) {
19072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD)) {
19172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      *value = local_value;
19272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else {
19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = ERROR_CANTREAD;
19472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
19772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
20072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::ReadInt64(const wchar_t* name, int64* value) const {
20172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(value);
20272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DWORD type = REG_QWORD;
20372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  int64 local_value = 0;
20472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DWORD size = sizeof(local_value);
20572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = ReadValue(name, &local_value, &size, &type);
20672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result == ERROR_SUCCESS) {
20772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if ((type == REG_QWORD || type == REG_BINARY) &&
20872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        size == sizeof(local_value)) {
20972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      *value = local_value;
21072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else {
21172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      result = ERROR_CANTREAD;
21272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
21372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
21472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
21572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
21672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
21772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
21872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::WriteValue(const wchar_t* name, const void * data,
2193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                        DWORD dsize, DWORD dtype) {
220513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  base::ThreadRestrictions::AssertIOAllowed();
22172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(data || !dsize);
2223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
22372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegSetValueEx(key_, name, 0, dtype,
22472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
22572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
22872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::WriteValue(const wchar_t * name, const wchar_t* value) {
2293345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return WriteValue(name, value,
2303345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      static_cast<DWORD>(sizeof(*value) * (wcslen(value) + 1)), REG_SZ);
231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
23372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::WriteValue(const wchar_t* name, DWORD value) {
23472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return WriteValue(name, &value, static_cast<DWORD>(sizeof(value)), REG_DWORD);
235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
23772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::StartWatching() {
23872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(key_);
239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!watch_event_)
240c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
241c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
242c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  DWORD filter = REG_NOTIFY_CHANGE_NAME |
243c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                 REG_NOTIFY_CHANGE_ATTRIBUTES |
244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                 REG_NOTIFY_CHANGE_LAST_SET |
245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                 REG_NOTIFY_CHANGE_SECURITY;
246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
247c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Watch the registry key for a change of value.
24872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE);
24972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result != ERROR_SUCCESS) {
250c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    CloseHandle(watch_event_);
251c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    watch_event_ = 0;
252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
25372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
25472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
255c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
25772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool RegKey::HasChanged() {
258c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (watch_event_) {
25972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) {
26072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      StartWatching();
26172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return true;
26272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
263c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
264c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
26772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenLONG RegKey::StopWatching() {
26872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = ERROR_INVALID_HANDLE;
269c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (watch_event_) {
27072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    CloseHandle(watch_event_);
27172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    watch_event_ = 0;
27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    result = ERROR_SUCCESS;
27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return result;
27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
27772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// RegistryValueIterator ------------------------------------------------------
27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
27972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenRegistryValueIterator::RegistryValueIterator(HKEY root_key,
28072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                             const wchar_t* folder_key) {
28172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
28272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
28372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
28472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result != ERROR_SUCCESS) {
28572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    key_ = NULL;
28672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else {
28772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    DWORD count = 0;
28872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count,
28972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                               NULL, NULL, NULL, NULL);
29072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
29172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (result != ERROR_SUCCESS) {
29272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ::RegCloseKey(key_);
29372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      key_ = NULL;
29472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else {
29572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      index_ = count - 1;
29672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    }
29772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
29872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  Read();
30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenRegistryValueIterator::~RegistryValueIterator() {
30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (key_)
30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    ::RegCloseKey(key_);
30672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenDWORD RegistryValueIterator::ValueCount() const {
30972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
31072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DWORD count = 0;
31172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL,
31272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                  &count, NULL, NULL, NULL, NULL);
31372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result != ERROR_SUCCESS)
31472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return 0;
31572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
31672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return count;
31772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
31872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
31972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool RegistryValueIterator::Valid() const {
32072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return key_ != NULL && index_ >= 0;
32172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
32272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
32372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid RegistryValueIterator::operator++() {
32472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  --index_;
32572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  Read();
32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool RegistryValueIterator::Read() {
32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
33072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (Valid()) {
33172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    DWORD ncount = arraysize(name_);
33272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    value_size_ = sizeof(value_);
33372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    LONG r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_,
33472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                            reinterpret_cast<BYTE*>(value_), &value_size_);
33572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (ERROR_SUCCESS == r)
336c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return true;
33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
33872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
33972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  name_[0] = '\0';
34072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  value_[0] = '\0';
34172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  value_size_ = 0;
34272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return false;
34372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// RegistryKeyIterator --------------------------------------------------------
34672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenRegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                         const wchar_t* folder_key) {
34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_);
35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result != ERROR_SUCCESS) {
35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    key_ = NULL;
35372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else {
35472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    DWORD count = 0;
35572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
35672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                    NULL, NULL, NULL, NULL, NULL);
35772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
35872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (result != ERROR_SUCCESS) {
35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      ::RegCloseKey(key_);
36072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      key_ = NULL;
36172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    } else {
36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      index_ = count - 1;
363c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
364c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
36572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
36672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  Read();
36772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
36872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
36972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenRegistryKeyIterator::~RegistryKeyIterator() {
37072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
37172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (key_)
37272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    ::RegCloseKey(key_);
37372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
37472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
37572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenDWORD RegistryKeyIterator::SubkeyCount() const {
37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DWORD count = 0;
37872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  LONG result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL,
37972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                  NULL, NULL, NULL, NULL, NULL);
38072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (result != ERROR_SUCCESS)
38172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return 0;
38272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
38372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return count;
38472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
38572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
38672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool RegistryKeyIterator::Valid() const {
38772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return key_ != NULL && index_ >= 0;
38872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
38972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
39072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid RegistryKeyIterator::operator++() {
39172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  --index_;
39272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  Read();
39372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
39472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
39572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool RegistryKeyIterator::Read() {
39672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  base::ThreadRestrictions::AssertIOAllowed();
39772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (Valid()) {
39872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    DWORD ncount = arraysize(name_);
39972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    FILETIME written;
40072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL,
40172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                            NULL, &written);
40272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (ERROR_SUCCESS == r)
40372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return true;
40472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
40572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
40672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  name_[0] = '\0';
407c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return false;
408c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
409731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
410731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}  // namespace win
411731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}  // namespace base
412