• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
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 <stddef.h>
8 
9 #include <algorithm>
10 #include <iterator>
11 #include <memory>
12 #include <string>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/check_op.h"
17 #include "base/containers/fixed_flat_map.h"
18 #include "base/functional/callback.h"
19 #include "base/native_library.h"
20 #include "base/notreached.h"
21 #include "base/strings/string_piece.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/string_util_win.h"
24 #include "base/threading/thread_restrictions.h"
25 #include "base/win/object_watcher.h"
26 #include "base/win/pe_image.h"
27 #include "base/win/scoped_handle.h"
28 #include "base/win/shlwapi.h"
29 
30 namespace base::win {
31 
32 namespace {
33 
34 // RegEnumValue() reports the number of characters from the name that were
35 // written to the buffer, not how many there are. This constant is the maximum
36 // name size, such that a buffer with this size should read any name.
37 constexpr DWORD MAX_REGISTRY_NAME_SIZE = 16384;
38 
39 // Registry values are read as BYTE* but can have wchar_t* data whose last
40 // wchar_t is truncated. This function converts the reported |byte_size| to
41 // a size in wchar_t that can store a truncated wchar_t if necessary.
to_wchar_size(DWORD byte_size)42 inline DWORD to_wchar_size(DWORD byte_size) {
43   return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t);
44 }
45 
46 // Mask to pull WOW64 access flags out of REGSAM access.
47 constexpr REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY;
48 
49 constexpr DWORD kInvalidIterValue = static_cast<DWORD>(-1);
50 
51 }  // namespace
52 
53 namespace internal {
54 
55 // A forwarder to the normal delayloaded Windows Registry API.
56 class Standard {
57  public:
CreateKey(HKEY hKey,LPCWSTR lpSubKey,DWORD Reserved,LPWSTR lpClass,DWORD dwOptions,REGSAM samDesired,CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,PHKEY phkResult,LPDWORD lpdwDisposition)58   static inline LSTATUS CreateKey(HKEY hKey,
59                                   LPCWSTR lpSubKey,
60                                   DWORD Reserved,
61                                   LPWSTR lpClass,
62                                   DWORD dwOptions,
63                                   REGSAM samDesired,
64                                   CONST LPSECURITY_ATTRIBUTES
65                                       lpSecurityAttributes,
66                                   PHKEY phkResult,
67                                   LPDWORD lpdwDisposition) {
68     return ::RegCreateKeyExW(hKey, lpSubKey, Reserved, lpClass, dwOptions,
69                              samDesired, lpSecurityAttributes, phkResult,
70                              lpdwDisposition);
71   }
72 
OpenKey(HKEY hKey,LPCWSTR lpSubKey,DWORD ulOptions,REGSAM samDesired,PHKEY phkResult)73   static inline LSTATUS OpenKey(HKEY hKey,
74                                 LPCWSTR lpSubKey,
75                                 DWORD ulOptions,
76                                 REGSAM samDesired,
77                                 PHKEY phkResult) {
78     return ::RegOpenKeyExW(hKey, lpSubKey, ulOptions, samDesired, phkResult);
79   }
80 
DeleteKey(HKEY hKey,LPCWSTR lpSubKey,REGSAM samDesired,DWORD Reserved)81   static inline LSTATUS DeleteKey(HKEY hKey,
82                                   LPCWSTR lpSubKey,
83                                   REGSAM samDesired,
84                                   DWORD Reserved) {
85     return ::RegDeleteKeyExW(hKey, lpSubKey, samDesired, Reserved);
86   }
87 
QueryInfoKey(HKEY hKey,LPWSTR lpClass,LPDWORD lpcchClass,LPDWORD lpReserved,LPDWORD lpcSubKeys,LPDWORD lpcbMaxSubKeyLen,LPDWORD lpcbMaxClassLen,LPDWORD lpcValues,LPDWORD lpcbMaxValueNameLen,LPDWORD lpcbMaxValueLen,LPDWORD lpcbSecurityDescriptor,PFILETIME lpftLastWriteTime)88   static inline LSTATUS QueryInfoKey(HKEY hKey,
89                                      LPWSTR lpClass,
90                                      LPDWORD lpcchClass,
91                                      LPDWORD lpReserved,
92                                      LPDWORD lpcSubKeys,
93                                      LPDWORD lpcbMaxSubKeyLen,
94                                      LPDWORD lpcbMaxClassLen,
95                                      LPDWORD lpcValues,
96                                      LPDWORD lpcbMaxValueNameLen,
97                                      LPDWORD lpcbMaxValueLen,
98                                      LPDWORD lpcbSecurityDescriptor,
99                                      PFILETIME lpftLastWriteTime) {
100     return ::RegQueryInfoKeyW(hKey, lpClass, lpcchClass, lpReserved, lpcSubKeys,
101                               lpcbMaxSubKeyLen, lpcbMaxClassLen, lpcValues,
102                               lpcbMaxValueNameLen, lpcbMaxValueLen,
103                               lpcbSecurityDescriptor, lpftLastWriteTime);
104   }
105 
EnumKey(HKEY hKey,DWORD dwIndex,LPWSTR lpName,LPDWORD lpcchName,LPDWORD lpReserved,LPWSTR lpClass,LPDWORD lpcchClass,PFILETIME lpftLastWriteTime)106   static inline LSTATUS EnumKey(HKEY hKey,
107                                 DWORD dwIndex,
108                                 LPWSTR lpName,
109                                 LPDWORD lpcchName,
110                                 LPDWORD lpReserved,
111                                 LPWSTR lpClass,
112                                 LPDWORD lpcchClass,
113                                 PFILETIME lpftLastWriteTime) {
114     return ::RegEnumKeyExW(hKey, dwIndex, lpName, lpcchName, lpReserved,
115                            lpClass, lpcchClass, lpftLastWriteTime);
116   }
117 
CloseKey(HKEY hKey)118   static inline LSTATUS CloseKey(HKEY hKey) { return ::RegCloseKey(hKey); }
119 
QueryValue(HKEY hKey,LPCWSTR lpValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData)120   static inline LSTATUS QueryValue(HKEY hKey,
121                                    LPCWSTR lpValueName,
122                                    LPDWORD lpReserved,
123                                    LPDWORD lpType,
124                                    LPBYTE lpData,
125                                    LPDWORD lpcbData) {
126     return ::RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData,
127                               lpcbData);
128   }
129 
SetValue(HKEY hKey,LPCWSTR lpValueName,DWORD Reserved,DWORD dwType,CONST BYTE * lpData,DWORD cbData)130   static inline LSTATUS SetValue(HKEY hKey,
131                                  LPCWSTR lpValueName,
132                                  DWORD Reserved,
133                                  DWORD dwType,
134                                  CONST BYTE* lpData,
135                                  DWORD cbData) {
136     return ::RegSetValueExW(hKey, lpValueName, Reserved, dwType, lpData,
137                             cbData);
138   }
139 
DeleteValue(HKEY hKey,LPCWSTR lpValueName)140   static inline LSTATUS DeleteValue(HKEY hKey, LPCWSTR lpValueName) {
141     return ::RegDeleteValueW(hKey, lpValueName);
142   }
143 
EnumValue(HKEY hKey,DWORD dwIndex,LPWSTR lpValueName,LPDWORD lpcchValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData)144   static inline LSTATUS EnumValue(HKEY hKey,
145                                   DWORD dwIndex,
146                                   LPWSTR lpValueName,
147                                   LPDWORD lpcchValueName,
148                                   LPDWORD lpReserved,
149                                   LPDWORD lpType,
150                                   LPBYTE lpData,
151                                   LPDWORD lpcbData) {
152     return ::RegEnumValueW(hKey, dwIndex, lpValueName, lpcchValueName,
153                            lpReserved, lpType, lpData, lpcbData);
154   }
155 };
156 
157 // An implementation derived from the export table of advapi32.
158 class ExportDerived {
159  public:
CreateKey(HKEY hKey,LPCWSTR lpSubKey,DWORD Reserved,LPWSTR lpClass,DWORD dwOptions,REGSAM samDesired,CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,PHKEY phkResult,LPDWORD lpdwDisposition)160   static LSTATUS CreateKey(HKEY hKey,
161                            LPCWSTR lpSubKey,
162                            DWORD Reserved,
163                            LPWSTR lpClass,
164                            DWORD dwOptions,
165                            REGSAM samDesired,
166                            CONST LPSECURITY_ATTRIBUTES lpSecurityAttributes,
167                            PHKEY phkResult,
168                            LPDWORD lpdwDisposition) {
169     if (!ResolveRegistryFunctions() || !reg_create_key_ex_) {
170       return ERROR_ERRORS_ENCOUNTERED;
171     }
172     return reg_create_key_ex_(hKey, lpSubKey, Reserved, lpClass, dwOptions,
173                               samDesired, lpSecurityAttributes, phkResult,
174                               lpdwDisposition);
175   }
176 
OpenKey(HKEY hKey,LPCWSTR lpSubKey,DWORD ulOptions,REGSAM samDesired,PHKEY phkResult)177   static LSTATUS OpenKey(HKEY hKey,
178                          LPCWSTR lpSubKey,
179                          DWORD ulOptions,
180                          REGSAM samDesired,
181                          PHKEY phkResult) {
182     if (!ResolveRegistryFunctions() || !reg_open_key_ex_) {
183       return ERROR_ERRORS_ENCOUNTERED;
184     }
185     return reg_open_key_ex_(hKey, lpSubKey, ulOptions, samDesired, phkResult);
186   }
187 
DeleteKey(HKEY hKey,LPCWSTR lpSubKey,REGSAM samDesired,DWORD Reserved)188   static LSTATUS DeleteKey(HKEY hKey,
189                            LPCWSTR lpSubKey,
190                            REGSAM samDesired,
191                            DWORD Reserved) {
192     if (!ResolveRegistryFunctions() || !reg_delete_key_ex_) {
193       return ERROR_ERRORS_ENCOUNTERED;
194     }
195     return reg_delete_key_ex_(hKey, lpSubKey, samDesired, Reserved);
196   }
197 
QueryInfoKey(HKEY hKey,LPWSTR lpClass,LPDWORD lpcchClass,LPDWORD lpReserved,LPDWORD lpcSubKeys,LPDWORD lpcbMaxSubKeyLen,LPDWORD lpcbMaxClassLen,LPDWORD lpcValues,LPDWORD lpcbMaxValueNameLen,LPDWORD lpcbMaxValueLen,LPDWORD lpcbSecurityDescriptor,PFILETIME lpftLastWriteTime)198   static LSTATUS QueryInfoKey(HKEY hKey,
199                               LPWSTR lpClass,
200                               LPDWORD lpcchClass,
201                               LPDWORD lpReserved,
202                               LPDWORD lpcSubKeys,
203                               LPDWORD lpcbMaxSubKeyLen,
204                               LPDWORD lpcbMaxClassLen,
205                               LPDWORD lpcValues,
206                               LPDWORD lpcbMaxValueNameLen,
207                               LPDWORD lpcbMaxValueLen,
208                               LPDWORD lpcbSecurityDescriptor,
209                               PFILETIME lpftLastWriteTime) {
210     if (!ResolveRegistryFunctions() || !reg_query_info_key_) {
211       return ERROR_ERRORS_ENCOUNTERED;
212     }
213     return reg_query_info_key_(hKey, lpClass, lpcchClass, lpReserved,
214                                lpcSubKeys, lpcbMaxSubKeyLen, lpcbMaxClassLen,
215                                lpcValues, lpcbMaxValueNameLen, lpcbMaxValueLen,
216                                lpcbSecurityDescriptor, lpftLastWriteTime);
217   }
218 
EnumKey(HKEY hKey,DWORD dwIndex,LPWSTR lpName,LPDWORD lpcchName,LPDWORD lpReserved,LPWSTR lpClass,LPDWORD lpcchClass,PFILETIME lpftLastWriteTime)219   static LSTATUS EnumKey(HKEY hKey,
220                          DWORD dwIndex,
221                          LPWSTR lpName,
222                          LPDWORD lpcchName,
223                          LPDWORD lpReserved,
224                          LPWSTR lpClass,
225                          LPDWORD lpcchClass,
226                          PFILETIME lpftLastWriteTime) {
227     if (!ResolveRegistryFunctions() || !reg_enum_key_ex_) {
228       return ERROR_ERRORS_ENCOUNTERED;
229     }
230     return reg_enum_key_ex_(hKey, dwIndex, lpName, lpcchName, lpReserved,
231                             lpClass, lpcchClass, lpftLastWriteTime);
232   }
233 
CloseKey(HKEY hKey)234   static LSTATUS CloseKey(HKEY hKey) {
235     if (!ResolveRegistryFunctions() || !reg_close_key_) {
236       return ERROR_ERRORS_ENCOUNTERED;
237     }
238     return reg_close_key_(hKey);
239   }
240 
QueryValue(HKEY hKey,LPCWSTR lpValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData)241   static LSTATUS QueryValue(HKEY hKey,
242                             LPCWSTR lpValueName,
243                             LPDWORD lpReserved,
244                             LPDWORD lpType,
245                             LPBYTE lpData,
246                             LPDWORD lpcbData) {
247     if (!ResolveRegistryFunctions() || !reg_query_value_ex_) {
248       return ERROR_ERRORS_ENCOUNTERED;
249     }
250     return reg_query_value_ex_(hKey, lpValueName, lpReserved, lpType, lpData,
251                                lpcbData);
252   }
253 
SetValue(HKEY hKey,LPCWSTR lpValueName,DWORD Reserved,DWORD dwType,CONST BYTE * lpData,DWORD cbData)254   static LSTATUS SetValue(HKEY hKey,
255                           LPCWSTR lpValueName,
256                           DWORD Reserved,
257                           DWORD dwType,
258                           CONST BYTE* lpData,
259                           DWORD cbData) {
260     if (!ResolveRegistryFunctions() || !reg_set_value_ex_) {
261       return ERROR_ERRORS_ENCOUNTERED;
262     }
263     return reg_set_value_ex_(hKey, lpValueName, Reserved, dwType, lpData,
264                              cbData);
265   }
266 
DeleteValue(HKEY hKey,LPCWSTR lpValueName)267   static LSTATUS DeleteValue(HKEY hKey, LPCWSTR lpValueName) {
268     if (!ResolveRegistryFunctions() || !reg_delete_value_) {
269       return ERROR_ERRORS_ENCOUNTERED;
270     }
271     return reg_delete_value_(hKey, lpValueName);
272   }
EnumValue(HKEY hKey,DWORD dwIndex,LPWSTR lpValueName,LPDWORD lpcchValueName,LPDWORD lpReserved,LPDWORD lpType,LPBYTE lpData,LPDWORD lpcbData)273   static LSTATUS EnumValue(HKEY hKey,
274                            DWORD dwIndex,
275                            LPWSTR lpValueName,
276                            LPDWORD lpcchValueName,
277                            LPDWORD lpReserved,
278                            LPDWORD lpType,
279                            LPBYTE lpData,
280                            LPDWORD lpcbData) {
281     if (!ResolveRegistryFunctions() || !reg_enum_value_) {
282       return ERROR_ERRORS_ENCOUNTERED;
283     }
284 
285     return reg_enum_value_(hKey, dwIndex, lpValueName, lpcchValueName,
286                            lpReserved, lpType, lpData, lpcbData);
287   }
288 
289  private:
ProcessOneExport(const base::win::PEImage & image,DWORD ordinal,DWORD hint,LPCSTR name,PVOID function_addr,LPCSTR forward,PVOID cookie)290   static bool ProcessOneExport(const base::win::PEImage& image,
291                                DWORD ordinal,
292                                DWORD hint,
293                                LPCSTR name,
294                                PVOID function_addr,
295                                LPCSTR forward,
296                                PVOID cookie) {
297     if (!name || !function_addr) {
298       return true;
299     }
300 
301     static const auto kMap =
302         base::MakeFixedFlatMapSorted<base::StringPiece, void**>({
303             {"RegCloseKey", reinterpret_cast<void**>(&reg_close_key_)},
304             {"RegCreateKeyExW", reinterpret_cast<void**>(&reg_create_key_ex_)},
305             {"RegDeleteKeyExW", reinterpret_cast<void**>(&reg_delete_key_ex_)},
306             {"RegDeleteValueW", reinterpret_cast<void**>(&reg_delete_value_)},
307             {"RegEnumKeyExW", reinterpret_cast<void**>(&reg_enum_key_ex_)},
308             {"RegEnumValueW", reinterpret_cast<void**>(&reg_enum_value_)},
309             {"RegOpenKeyExW", reinterpret_cast<void**>(&reg_open_key_ex_)},
310             {"RegQueryInfoKeyW",
311              reinterpret_cast<void**>(&reg_query_info_key_)},
312             {"RegQueryValueExW",
313              reinterpret_cast<void**>(&reg_query_value_ex_)},
314             {"RegSetValueExW", reinterpret_cast<void**>(&reg_set_value_ex_)},
315         });
316 
317     auto* entry = kMap.find(name);
318     if (entry == kMap.end()) {
319       return true;
320     }
321 
322     static size_t num_init_functions = 0;
323     if (!std::exchange(*(entry->second), function_addr)) {
324       ++num_init_functions;
325     }
326 
327     bool& fully_resolved = *static_cast<bool*>(cookie);
328     fully_resolved = num_init_functions == kMap.size();
329     return !fully_resolved;
330   }
331 
ResolveRegistryFunctions()332   static bool ResolveRegistryFunctions() {
333     static bool initialized = []() {
334       base::NativeLibraryLoadError error;
335       HMODULE advapi32 = base::PinSystemLibrary(L"advapi32.dll", &error);
336       if (!advapi32 || error.code) {
337         return false;
338       }
339       bool fully_resolved = false;
340       base::win::PEImage(advapi32).EnumExports(&ProcessOneExport,
341                                                &fully_resolved);
342       return fully_resolved;
343     }();
344     return initialized;
345   }
346 
347   static decltype(::RegCreateKeyExW)* reg_create_key_ex_;
348   static decltype(::RegOpenKeyExW)* reg_open_key_ex_;
349   static decltype(::RegDeleteKeyExW)* reg_delete_key_ex_;
350   static decltype(::RegQueryInfoKeyW)* reg_query_info_key_;
351   static decltype(::RegEnumKeyExW)* reg_enum_key_ex_;
352   static decltype(::RegCloseKey)* reg_close_key_;
353   static decltype(::RegQueryValueExW)* reg_query_value_ex_;
354   static decltype(::RegSetValueExW)* reg_set_value_ex_;
355   static decltype(::RegDeleteValueW)* reg_delete_value_;
356   static decltype(::RegEnumValueW)* reg_enum_value_;
357 };
358 
359 decltype(::RegCreateKeyEx)* ExportDerived::reg_create_key_ex_ = nullptr;
360 decltype(::RegOpenKeyExW)* ExportDerived::reg_open_key_ex_ = nullptr;
361 decltype(::RegDeleteKeyExW)* ExportDerived::reg_delete_key_ex_ = nullptr;
362 decltype(::RegQueryInfoKeyW)* ExportDerived::reg_query_info_key_ = nullptr;
363 decltype(::RegEnumKeyExW)* ExportDerived::reg_enum_key_ex_ = nullptr;
364 decltype(::RegCloseKey)* ExportDerived::reg_close_key_ = nullptr;
365 decltype(::RegQueryValueEx)* ExportDerived::reg_query_value_ex_ = nullptr;
366 decltype(::RegSetValueExW)* ExportDerived::reg_set_value_ex_ = nullptr;
367 decltype(::RegDeleteValueW)* ExportDerived::reg_delete_value_ = nullptr;
368 decltype(::RegEnumValueW)* ExportDerived::reg_enum_value_ = nullptr;
369 
370 // Watches for modifications to a key.
371 template <typename Reg>
372 class GenericRegKey<Reg>::Watcher : public ObjectWatcher::Delegate {
373  public:
374   Watcher() = default;
375 
376   Watcher(const Watcher&) = delete;
377   Watcher& operator=(const Watcher&) = delete;
378 
379   ~Watcher() override = default;
380 
381   bool StartWatching(HKEY key, ChangeCallback callback);
382 
383   // ObjectWatcher::Delegate:
OnObjectSignaled(HANDLE object)384   void OnObjectSignaled(HANDLE object) override {
385     DCHECK(watch_event_.is_valid());
386     DCHECK_EQ(watch_event_.get(), object);
387     std::move(callback_).Run();
388   }
389 
390  private:
391   ScopedHandle watch_event_;
392   ObjectWatcher object_watcher_;
393   ChangeCallback callback_;
394 };
395 
396 template <typename Reg>
StartWatching(HKEY key,ChangeCallback callback)397 bool GenericRegKey<Reg>::Watcher::StartWatching(HKEY key,
398                                                 ChangeCallback callback) {
399   DCHECK(key);
400   DCHECK(callback_.is_null());
401 
402   if (!watch_event_.is_valid()) {
403     watch_event_.Set(CreateEvent(nullptr, TRUE, FALSE, nullptr));
404   }
405 
406   if (!watch_event_.is_valid()) {
407     return false;
408   }
409 
410   DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES |
411                  REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY |
412                  REG_NOTIFY_THREAD_AGNOSTIC;
413   // Watch the registry key for a change of value.
414   LONG result =
415       RegNotifyChangeKeyValue(key, /*bWatchSubtree=*/TRUE, filter,
416                               watch_event_.get(), /*fAsynchronous=*/TRUE);
417   if (result != ERROR_SUCCESS) {
418     watch_event_.Close();
419     return false;
420   }
421 
422   callback_ = std::move(callback);
423   return object_watcher_.StartWatchingOnce(watch_event_.get(), this);
424 }
425 
426 // GenericRegKey<Reg>
427 // ----------------------------------------------------------------------
428 
429 template <typename Reg>
430 GenericRegKey<Reg>::GenericRegKey() = default;
431 
432 template <typename Reg>
GenericRegKey(HKEY key)433 GenericRegKey<Reg>::GenericRegKey(HKEY key) : key_(key) {}
434 
435 template <typename Reg>
GenericRegKey(HKEY rootkey,const wchar_t * subkey,REGSAM access)436 GenericRegKey<Reg>::GenericRegKey(HKEY rootkey,
437                                   const wchar_t* subkey,
438                                   REGSAM access) {
439   if (rootkey) {
440     if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) {
441       Create(rootkey, subkey, access);
442     } else {
443       Open(rootkey, subkey, access);
444     }
445   } else {
446     DCHECK(!subkey);
447     wow64access_ = access & kWow64AccessMask;
448   }
449 }
450 
451 template <typename Reg>
GenericRegKey(GenericRegKey<Reg> && other)452 GenericRegKey<Reg>::GenericRegKey(GenericRegKey<Reg>&& other) noexcept
453     : key_(other.key_),
454       wow64access_(other.wow64access_),
455       key_watcher_(std::move(other.key_watcher_)) {
456   other.key_ = nullptr;
457   other.wow64access_ = 0;
458 }
459 
460 template <typename Reg>
operator =(GenericRegKey<Reg> && other)461 GenericRegKey<Reg>& GenericRegKey<Reg>::operator=(GenericRegKey<Reg>&& other) {
462   Close();
463   std::swap(key_, other.key_);
464   std::swap(wow64access_, other.wow64access_);
465   key_watcher_ = std::move(other.key_watcher_);
466   return *this;
467 }
468 
469 template <typename Reg>
~GenericRegKey()470 GenericRegKey<Reg>::~GenericRegKey() {
471   Close();
472 }
473 
474 template <typename Reg>
Create(HKEY rootkey,const wchar_t * subkey,REGSAM access)475 LONG GenericRegKey<Reg>::Create(HKEY rootkey,
476                                 const wchar_t* subkey,
477                                 REGSAM access) {
478   DWORD disposition_value;
479   return CreateWithDisposition(rootkey, subkey, &disposition_value, access);
480 }
481 
482 template <typename Reg>
CreateWithDisposition(HKEY rootkey,const wchar_t * subkey,DWORD * disposition,REGSAM access)483 LONG GenericRegKey<Reg>::CreateWithDisposition(HKEY rootkey,
484                                                const wchar_t* subkey,
485                                                DWORD* disposition,
486                                                REGSAM access) {
487   DCHECK(rootkey && subkey && access && disposition);
488   HKEY subhkey = nullptr;
489   LONG result =
490       Reg::CreateKey(rootkey, subkey, 0, nullptr, REG_OPTION_NON_VOLATILE,
491                      access, nullptr, &subhkey, disposition);
492   if (result == ERROR_SUCCESS) {
493     Close();
494     key_ = subhkey;
495     wow64access_ = access & kWow64AccessMask;
496   }
497 
498   return result;
499 }
500 
501 template <typename Reg>
CreateKey(const wchar_t * name,REGSAM access)502 LONG GenericRegKey<Reg>::CreateKey(const wchar_t* name, REGSAM access) {
503   DCHECK(name && access);
504   // After the application has accessed an alternate registry view using one
505   // of the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent
506   // operations (create, delete, or open) on child registry keys must
507   // explicitly use the same flag. Otherwise, there can be unexpected
508   // behavior.
509   // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
510   if ((access & kWow64AccessMask) != wow64access_) {
511     NOTREACHED();
512     return ERROR_INVALID_PARAMETER;
513   }
514   HKEY subkey = nullptr;
515   LONG result = Reg::CreateKey(key_, name, 0, nullptr, REG_OPTION_NON_VOLATILE,
516                                access, nullptr, &subkey, nullptr);
517   if (result == ERROR_SUCCESS) {
518     Close();
519     key_ = subkey;
520     wow64access_ = access & kWow64AccessMask;
521   }
522 
523   return result;
524 }
525 
526 template <typename Reg>
Open(HKEY rootkey,const wchar_t * subkey,REGSAM access)527 LONG GenericRegKey<Reg>::Open(HKEY rootkey,
528                               const wchar_t* subkey,
529                               REGSAM access) {
530   DCHECK(rootkey && subkey && access);
531   HKEY subhkey = nullptr;
532 
533   LONG result = Reg::OpenKey(rootkey, subkey, 0, access, &subhkey);
534   if (result == ERROR_SUCCESS) {
535     Close();
536     key_ = subhkey;
537     wow64access_ = access & kWow64AccessMask;
538   }
539 
540   return result;
541 }
542 
543 template <typename Reg>
OpenKey(const wchar_t * relative_key_name,REGSAM access)544 LONG GenericRegKey<Reg>::OpenKey(const wchar_t* relative_key_name,
545                                  REGSAM access) {
546   DCHECK(relative_key_name && access);
547   // After the application has accessed an alternate registry view using one
548   // of the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent
549   // operations (create, delete, or open) on child registry keys must
550   // explicitly use the same flag. Otherwise, there can be unexpected
551   // behavior.
552   // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx.
553   if ((access & kWow64AccessMask) != wow64access_) {
554     NOTREACHED();
555     return ERROR_INVALID_PARAMETER;
556   }
557   HKEY subkey = nullptr;
558   LONG result = Reg::OpenKey(key_, relative_key_name, 0, access, &subkey);
559 
560   // We have to close the current opened key before replacing it with the new
561   // one.
562   if (result == ERROR_SUCCESS) {
563     Close();
564     key_ = subkey;
565     wow64access_ = access & kWow64AccessMask;
566   }
567   return result;
568 }
569 
570 template <typename Reg>
Close()571 void GenericRegKey<Reg>::Close() {
572   if (key_) {
573     Reg::CloseKey(key_);
574     key_ = nullptr;
575     wow64access_ = 0;
576   }
577 }
578 
579 // TODO(wfh): Remove this and other unsafe methods. See
580 // http://crbug.com/375400
581 template <typename Reg>
Set(HKEY key)582 void GenericRegKey<Reg>::Set(HKEY key) {
583   if (key_ != key) {
584     Close();
585     key_ = key;
586   }
587 }
588 
589 template <typename Reg>
Take()590 HKEY GenericRegKey<Reg>::Take() {
591   DCHECK_EQ(wow64access_, 0u);
592   HKEY key = key_;
593   key_ = nullptr;
594   return key;
595 }
596 
597 template <typename Reg>
HasValue(const wchar_t * name) const598 bool GenericRegKey<Reg>::HasValue(const wchar_t* name) const {
599   return Reg::QueryValue(key_, name, nullptr, nullptr, nullptr, nullptr) ==
600          ERROR_SUCCESS;
601 }
602 
603 template <typename Reg>
GetValueCount() const604 DWORD GenericRegKey<Reg>::GetValueCount() const {
605   DWORD count = 0;
606   LONG result =
607       Reg::QueryInfoKey(key_, nullptr, nullptr, nullptr, nullptr, nullptr,
608                         nullptr, &count, nullptr, nullptr, nullptr, nullptr);
609   return (result == ERROR_SUCCESS) ? count : 0;
610 }
611 
612 template <typename Reg>
GetLastWriteTime() const613 FILETIME GenericRegKey<Reg>::GetLastWriteTime() const {
614   FILETIME last_write_time;
615   LONG result = Reg::QueryInfoKey(key_, nullptr, nullptr, nullptr, nullptr,
616                                   nullptr, nullptr, nullptr, nullptr, nullptr,
617                                   nullptr, &last_write_time);
618   return (result == ERROR_SUCCESS) ? last_write_time : FILETIME{};
619 }
620 
621 template <typename Reg>
GetValueNameAt(DWORD index,std::wstring * name) const622 LONG GenericRegKey<Reg>::GetValueNameAt(DWORD index, std::wstring* name) const {
623   wchar_t buf[256];
624   DWORD bufsize = std::size(buf);
625   LONG r = Reg::EnumValue(key_, index, buf, &bufsize, nullptr, nullptr, nullptr,
626                           nullptr);
627   if (r == ERROR_SUCCESS) {
628     name->assign(buf, bufsize);
629   }
630 
631   return r;
632 }
633 
634 template <typename Reg>
DeleteKey(const wchar_t * name)635 LONG GenericRegKey<Reg>::DeleteKey(const wchar_t* name) {
636   DCHECK(name);
637 
638   // Verify the key exists before attempting delete to replicate previous
639   // behavior.
640   // `RegOpenKeyEx()` will return an error if `key_` is invalid.
641   HKEY subkey = nullptr;
642   LONG result =
643       Reg::OpenKey(key_, name, 0, READ_CONTROL | wow64access_, &subkey);
644   if (result != ERROR_SUCCESS) {
645     return result;
646   }
647   Reg::CloseKey(subkey);
648 
649   return RegDelRecurse(key_, name, wow64access_);
650 }
651 
652 template <typename Reg>
DeleteEmptyKey(const wchar_t * name)653 LONG GenericRegKey<Reg>::DeleteEmptyKey(const wchar_t* name) {
654   DCHECK(name);
655 
656   // `RegOpenKeyEx()` will return an error if `key_` is invalid.
657   HKEY target_key = nullptr;
658   LONG result =
659       Reg::OpenKey(key_, name, 0, KEY_READ | wow64access_, &target_key);
660 
661   if (result != ERROR_SUCCESS) {
662     return result;
663   }
664 
665   DWORD count = 0;
666   result =
667       Reg::QueryInfoKey(target_key, nullptr, nullptr, nullptr, nullptr, nullptr,
668                         nullptr, &count, nullptr, nullptr, nullptr, nullptr);
669 
670   Reg::CloseKey(target_key);
671 
672   if (result != ERROR_SUCCESS) {
673     return result;
674   }
675 
676   if (count == 0) {
677     return RegDeleteKeyEx(key_, name, wow64access_, 0);
678   }
679 
680   return ERROR_DIR_NOT_EMPTY;
681 }
682 
683 template <typename Reg>
DeleteValue(const wchar_t * value_name)684 LONG GenericRegKey<Reg>::DeleteValue(const wchar_t* value_name) {
685   // `RegDeleteValue()` will return an error if `key_` is invalid.
686   LONG result = Reg::DeleteValue(key_, value_name);
687   return result;
688 }
689 
690 template <typename Reg>
ReadValueDW(const wchar_t * name,DWORD * out_value) const691 LONG GenericRegKey<Reg>::ReadValueDW(const wchar_t* name,
692                                      DWORD* out_value) const {
693   DCHECK(out_value);
694   DWORD type = REG_DWORD;
695   DWORD size = sizeof(DWORD);
696   DWORD local_value = 0;
697   LONG result = ReadValue(name, &local_value, &size, &type);
698   if (result == ERROR_SUCCESS) {
699     if ((type == REG_DWORD || type == REG_BINARY) && size == sizeof(DWORD)) {
700       *out_value = local_value;
701     } else {
702       result = ERROR_CANTREAD;
703     }
704   }
705 
706   return result;
707 }
708 
709 template <typename Reg>
ReadInt64(const wchar_t * name,int64_t * out_value) const710 LONG GenericRegKey<Reg>::ReadInt64(const wchar_t* name,
711                                    int64_t* out_value) const {
712   DCHECK(out_value);
713   DWORD type = REG_QWORD;
714   int64_t local_value = 0;
715   DWORD size = sizeof(local_value);
716   LONG result = ReadValue(name, &local_value, &size, &type);
717   if (result == ERROR_SUCCESS) {
718     if ((type == REG_QWORD || type == REG_BINARY) &&
719         size == sizeof(local_value)) {
720       *out_value = local_value;
721     } else {
722       result = ERROR_CANTREAD;
723     }
724   }
725 
726   return result;
727 }
728 
729 template <typename Reg>
ReadValue(const wchar_t * name,std::wstring * out_value) const730 LONG GenericRegKey<Reg>::ReadValue(const wchar_t* name,
731                                    std::wstring* out_value) const {
732   DCHECK(out_value);
733   const size_t kMaxStringLength = 1024;  // This is after expansion.
734   // Use the one of the other forms of ReadValue if 1024 is too small for you.
735   wchar_t raw_value[kMaxStringLength];
736   DWORD type = REG_SZ, size = sizeof(raw_value);
737   LONG result = ReadValue(name, raw_value, &size, &type);
738   if (result == ERROR_SUCCESS) {
739     if (type == REG_SZ) {
740       *out_value = raw_value;
741     } else if (type == REG_EXPAND_SZ) {
742       wchar_t expanded[kMaxStringLength];
743       size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength);
744       // Success: returns the number of wchar_t's copied
745       // Fail: buffer too small, returns the size required
746       // Fail: other, returns 0
747       if (size == 0 || size > kMaxStringLength) {
748         result = ERROR_MORE_DATA;
749       } else {
750         *out_value = expanded;
751       }
752     } else {
753       // Not a string. Oops.
754       result = ERROR_CANTREAD;
755     }
756   }
757 
758   return result;
759 }
760 
761 template <typename Reg>
ReadValue(const wchar_t * name,void * data,DWORD * dsize,DWORD * dtype) const762 LONG GenericRegKey<Reg>::ReadValue(const wchar_t* name,
763                                    void* data,
764                                    DWORD* dsize,
765                                    DWORD* dtype) const {
766   LONG result = Reg::QueryValue(key_, name, nullptr, dtype,
767                                 reinterpret_cast<LPBYTE>(data), dsize);
768   return result;
769 }
770 
771 template <typename Reg>
ReadValues(const wchar_t * name,std::vector<std::wstring> * values)772 LONG GenericRegKey<Reg>::ReadValues(const wchar_t* name,
773                                     std::vector<std::wstring>* values) {
774   values->clear();
775 
776   DWORD type = REG_MULTI_SZ;
777   DWORD size = 0;
778   LONG result = ReadValue(name, nullptr, &size, &type);
779   if (result != ERROR_SUCCESS || size == 0) {
780     return result;
781   }
782 
783   if (type != REG_MULTI_SZ) {
784     return ERROR_CANTREAD;
785   }
786 
787   std::vector<wchar_t> buffer(size / sizeof(wchar_t));
788   result = ReadValue(name, buffer.data(), &size, nullptr);
789   if (result != ERROR_SUCCESS || size == 0) {
790     return result;
791   }
792 
793   // Parse the double-null-terminated list of strings.
794   // Note: This code is paranoid to not read outside of |buf|, in the case
795   // where it may not be properly terminated.
796   auto entry = buffer.cbegin();
797   auto buffer_end = buffer.cend();
798   while (entry < buffer_end && *entry != '\0') {
799     auto entry_end = std::find(entry, buffer_end, '\0');
800     values->emplace_back(entry, entry_end);
801     entry = entry_end + 1;
802   }
803   return 0;
804 }
805 
806 template <typename Reg>
WriteValue(const wchar_t * name,DWORD in_value)807 LONG GenericRegKey<Reg>::WriteValue(const wchar_t* name, DWORD in_value) {
808   return WriteValue(name, &in_value, static_cast<DWORD>(sizeof(in_value)),
809                     REG_DWORD);
810 }
811 
812 template <typename Reg>
WriteValue(const wchar_t * name,const wchar_t * in_value)813 LONG GenericRegKey<Reg>::WriteValue(const wchar_t* name,
814                                     const wchar_t* in_value) {
815   return WriteValue(
816       name, in_value,
817       static_cast<DWORD>(sizeof(*in_value) *
818                          (std::char_traits<wchar_t>::length(in_value) + 1)),
819       REG_SZ);
820 }
821 
822 template <typename Reg>
WriteValue(const wchar_t * name,const void * data,DWORD dsize,DWORD dtype)823 LONG GenericRegKey<Reg>::WriteValue(const wchar_t* name,
824                                     const void* data,
825                                     DWORD dsize,
826                                     DWORD dtype) {
827   DCHECK(data || !dsize);
828 
829   LONG result =
830       Reg::SetValue(key_, name, 0, dtype,
831                     reinterpret_cast<LPBYTE>(const_cast<void*>(data)), dsize);
832   return result;
833 }
834 
835 template <typename Reg>
StartWatching(ChangeCallback callback)836 bool GenericRegKey<Reg>::StartWatching(ChangeCallback callback) {
837   if (!key_watcher_) {
838     key_watcher_ = std::make_unique<Watcher>();
839   }
840 
841   if (!key_watcher_->StartWatching(key_, std::move(callback))) {
842     return false;
843   }
844 
845   return true;
846 }
847 
848 // static
849 template <typename Reg>
RegDelRecurse(HKEY root_key,const wchar_t * name,REGSAM access)850 LONG GenericRegKey<Reg>::RegDelRecurse(HKEY root_key,
851                                        const wchar_t* name,
852                                        REGSAM access) {
853   // First, see if the key can be deleted without having to recurse.
854   LONG result = Reg::DeleteKey(root_key, name, access, 0);
855   if (result == ERROR_SUCCESS) {
856     return result;
857   }
858 
859   HKEY target_key = nullptr;
860   result = Reg::OpenKey(root_key, name, 0, KEY_ENUMERATE_SUB_KEYS | access,
861                         &target_key);
862 
863   if (result == ERROR_FILE_NOT_FOUND) {
864     return ERROR_SUCCESS;
865   }
866   if (result != ERROR_SUCCESS)
867     return result;
868 
869   std::wstring subkey_name(name);
870 
871   // Check for an ending slash and add one if it is missing.
872   if (!subkey_name.empty() && subkey_name.back() != '\\') {
873     subkey_name.push_back('\\');
874   }
875 
876   // Enumerate the keys
877   result = ERROR_SUCCESS;
878   const DWORD kMaxKeyNameLength = MAX_PATH;
879   const size_t base_key_length = subkey_name.length();
880   std::wstring key_name;
881   while (result == ERROR_SUCCESS) {
882     DWORD key_size = kMaxKeyNameLength;
883     result =
884         Reg::EnumKey(target_key, 0, WriteInto(&key_name, kMaxKeyNameLength),
885                      &key_size, nullptr, nullptr, nullptr, nullptr);
886 
887     if (result != ERROR_SUCCESS) {
888       break;
889     }
890 
891     key_name.resize(key_size);
892     subkey_name.resize(base_key_length);
893     subkey_name += key_name;
894 
895     if (RegDelRecurse(root_key, subkey_name.c_str(), access) != ERROR_SUCCESS) {
896       break;
897     }
898   }
899 
900   Reg::CloseKey(target_key);
901 
902   // Try again to delete the key.
903   result = Reg::DeleteKey(root_key, name, access, 0);
904 
905   return result;
906 }
907 
908 // Instantiate the only two allowed versions of GenericRegKey for use by the
909 // public base::win::RegKey and base::win::ExportDerivedRegKey.
910 template class GenericRegKey<internal::Standard>;
911 template class GenericRegKey<internal::ExportDerived>;
912 
913 }  // namespace internal
914 
RegKey()915 RegKey::RegKey() : GenericRegKey<internal::Standard>() {}
RegKey(HKEY key)916 RegKey::RegKey(HKEY key) : GenericRegKey<internal::Standard>(key) {}
RegKey(HKEY rootkey,const wchar_t * subkey,REGSAM access)917 RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access)
918     : GenericRegKey<internal::Standard>(rootkey, subkey, access) {}
919 
RegKey(RegKey && other)920 RegKey::RegKey(RegKey&& other) noexcept
921     : GenericRegKey<internal::Standard>(std::move(other)) {}
operator =(RegKey && other)922 RegKey& RegKey::operator=(RegKey&& other) {
923   GenericRegKey<internal::Standard>::operator=(std::move(other));
924   return *this;
925 }
926 
927 RegKey::~RegKey() = default;
928 
ExportDerivedRegKey()929 ExportDerivedRegKey::ExportDerivedRegKey()
930     : GenericRegKey<internal::ExportDerived>() {}
ExportDerivedRegKey(HKEY key)931 ExportDerivedRegKey::ExportDerivedRegKey(HKEY key)
932     : GenericRegKey<internal::ExportDerived>(key) {}
ExportDerivedRegKey(HKEY rootkey,const wchar_t * subkey,REGSAM access)933 ExportDerivedRegKey::ExportDerivedRegKey(HKEY rootkey,
934                                          const wchar_t* subkey,
935                                          REGSAM access)
936     : GenericRegKey<internal::ExportDerived>(rootkey, subkey, access) {}
937 
ExportDerivedRegKey(ExportDerivedRegKey && other)938 ExportDerivedRegKey::ExportDerivedRegKey(ExportDerivedRegKey&& other) noexcept
939     : GenericRegKey<internal::ExportDerived>(std::move(other)) {}
operator =(ExportDerivedRegKey && other)940 ExportDerivedRegKey& ExportDerivedRegKey::operator=(
941     ExportDerivedRegKey&& other) {
942   GenericRegKey<internal::ExportDerived>::operator=(std::move(other));
943   return *this;
944 }
945 
946 ExportDerivedRegKey::~ExportDerivedRegKey() = default;
947 
948 // RegistryValueIterator ------------------------------------------------------
949 
RegistryValueIterator(HKEY root_key,const wchar_t * folder_key,REGSAM wow64access)950 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
951                                              const wchar_t* folder_key,
952                                              REGSAM wow64access)
953     : name_(MAX_PATH, '\0'), value_(MAX_PATH, '\0') {
954   Initialize(root_key, folder_key, wow64access);
955 }
956 
RegistryValueIterator(HKEY root_key,const wchar_t * folder_key)957 RegistryValueIterator::RegistryValueIterator(HKEY root_key,
958                                              const wchar_t* folder_key)
959     : name_(MAX_PATH, '\0'), value_(MAX_PATH, '\0') {
960   Initialize(root_key, folder_key, 0);
961 }
962 
Initialize(HKEY root_key,const wchar_t * folder_key,REGSAM wow64access)963 void RegistryValueIterator::Initialize(HKEY root_key,
964                                        const wchar_t* folder_key,
965                                        REGSAM wow64access) {
966   DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
967   LONG result =
968       RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
969   if (result != ERROR_SUCCESS) {
970     key_ = nullptr;
971   } else {
972     DWORD count = 0;
973     result =
974         ::RegQueryInfoKey(key_, nullptr, nullptr, nullptr, nullptr, nullptr,
975                           nullptr, &count, nullptr, nullptr, nullptr, nullptr);
976 
977     if (result != ERROR_SUCCESS) {
978       ::RegCloseKey(key_);
979       key_ = nullptr;
980     } else {
981       index_ = count - 1;
982     }
983   }
984 
985   Read();
986 }
987 
~RegistryValueIterator()988 RegistryValueIterator::~RegistryValueIterator() {
989   if (key_)
990     ::RegCloseKey(key_);
991 }
992 
ValueCount() const993 DWORD RegistryValueIterator::ValueCount() const {
994   DWORD count = 0;
995   LONG result =
996       ::RegQueryInfoKey(key_, nullptr, nullptr, nullptr, nullptr, nullptr,
997                         nullptr, &count, nullptr, nullptr, nullptr, nullptr);
998   if (result != ERROR_SUCCESS)
999     return 0;
1000 
1001   return count;
1002 }
1003 
Valid() const1004 bool RegistryValueIterator::Valid() const {
1005   return key_ != nullptr && index_ != kInvalidIterValue;
1006 }
1007 
operator ++()1008 void RegistryValueIterator::operator++() {
1009   if (index_ != kInvalidIterValue)
1010     --index_;
1011   Read();
1012 }
1013 
Read()1014 bool RegistryValueIterator::Read() {
1015   if (Valid()) {
1016     DWORD capacity = static_cast<DWORD>(name_.capacity());
1017     DWORD name_size = capacity;
1018     // |value_size_| is in bytes. Reserve the last character for a NUL.
1019     value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
1020     LONG result = ::RegEnumValue(
1021         key_, index_, WriteInto(&name_, name_size), &name_size, nullptr, &type_,
1022         reinterpret_cast<BYTE*>(value_.data()), &value_size_);
1023 
1024     if (result == ERROR_MORE_DATA) {
1025       // Registry key names are limited to 255 characters and fit within
1026       // MAX_PATH (which is 260) but registry value names can use up to 16,383
1027       // characters and the value itself is not limited
1028       // (from http://msdn.microsoft.com/en-us/library/windows/desktop/
1029       // ms724872(v=vs.85).aspx).
1030       // Resize the buffers and retry if their size caused the failure.
1031       DWORD value_size_in_wchars = to_wchar_size(value_size_);
1032       if (value_size_in_wchars + 1 > value_.size())
1033         value_.resize(value_size_in_wchars + 1, '\0');
1034       value_size_ = static_cast<DWORD>((value_.size() - 1) * sizeof(wchar_t));
1035       name_size = name_size == capacity ? MAX_REGISTRY_NAME_SIZE : capacity;
1036       result = ::RegEnumValue(
1037           key_, index_, WriteInto(&name_, name_size), &name_size, nullptr,
1038           &type_, reinterpret_cast<BYTE*>(value_.data()), &value_size_);
1039     }
1040 
1041     if (result == ERROR_SUCCESS) {
1042       DCHECK_LT(to_wchar_size(value_size_), value_.size());
1043       value_[to_wchar_size(value_size_)] = '\0';
1044       return true;
1045     }
1046   }
1047 
1048   name_[0] = '\0';
1049   value_[0] = '\0';
1050   value_size_ = 0;
1051   return false;
1052 }
1053 
1054 // RegistryKeyIterator --------------------------------------------------------
1055 
RegistryKeyIterator(HKEY root_key,const wchar_t * folder_key)1056 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
1057                                          const wchar_t* folder_key) {
1058   Initialize(root_key, folder_key, 0);
1059 }
1060 
RegistryKeyIterator(HKEY root_key,const wchar_t * folder_key,REGSAM wow64access)1061 RegistryKeyIterator::RegistryKeyIterator(HKEY root_key,
1062                                          const wchar_t* folder_key,
1063                                          REGSAM wow64access) {
1064   Initialize(root_key, folder_key, wow64access);
1065 }
1066 
~RegistryKeyIterator()1067 RegistryKeyIterator::~RegistryKeyIterator() {
1068   if (key_)
1069     ::RegCloseKey(key_);
1070 }
1071 
SubkeyCount() const1072 DWORD RegistryKeyIterator::SubkeyCount() const {
1073   DWORD count = 0;
1074   LONG result =
1075       ::RegQueryInfoKey(key_, nullptr, nullptr, nullptr, &count, nullptr,
1076                         nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
1077   if (result != ERROR_SUCCESS)
1078     return 0;
1079 
1080   return count;
1081 }
1082 
Valid() const1083 bool RegistryKeyIterator::Valid() const {
1084   return key_ != nullptr && index_ != kInvalidIterValue;
1085 }
1086 
operator ++()1087 void RegistryKeyIterator::operator++() {
1088   if (index_ != kInvalidIterValue)
1089     --index_;
1090   Read();
1091 }
1092 
Read()1093 bool RegistryKeyIterator::Read() {
1094   if (Valid()) {
1095     DWORD ncount = static_cast<DWORD>(std::size(name_));
1096     FILETIME written;
1097     LONG r = ::RegEnumKeyEx(key_, index_, name_, &ncount, nullptr, nullptr,
1098                             nullptr, &written);
1099     if (ERROR_SUCCESS == r)
1100       return true;
1101   }
1102 
1103   name_[0] = '\0';
1104   return false;
1105 }
1106 
Initialize(HKEY root_key,const wchar_t * folder_key,REGSAM wow64access)1107 void RegistryKeyIterator::Initialize(HKEY root_key,
1108                                      const wchar_t* folder_key,
1109                                      REGSAM wow64access) {
1110   DCHECK_EQ(wow64access & ~kWow64AccessMask, static_cast<REGSAM>(0));
1111   LONG result =
1112       RegOpenKeyEx(root_key, folder_key, 0, KEY_READ | wow64access, &key_);
1113   if (result != ERROR_SUCCESS) {
1114     key_ = nullptr;
1115   } else {
1116     DWORD count = 0;
1117     result =
1118         ::RegQueryInfoKey(key_, nullptr, nullptr, nullptr, &count, nullptr,
1119                           nullptr, nullptr, nullptr, nullptr, nullptr, nullptr);
1120 
1121     if (result != ERROR_SUCCESS) {
1122       ::RegCloseKey(key_);
1123       key_ = nullptr;
1124     } else {
1125       index_ = count - 1;
1126     }
1127   }
1128 
1129   Read();
1130 }
1131 
1132 }  // namespace base::win
1133