registry.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. //*********************************************************
  2. //
  3. // Copyright (c) Microsoft. All rights reserved.
  4. // This code is licensed under the MIT License.
  5. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
  6. // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  7. // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  8. // PARTICULAR PURPOSE AND NONINFRINGEMENT.
  9. //
  10. //*********************************************************
  11. #ifndef __WIL_REGISTRY_INCLUDED
  12. #define __WIL_REGISTRY_INCLUDED
  13. #ifdef _KERNEL_MODE
  14. #error This header is not supported in kernel-mode.
  15. #endif
  16. #include <winreg.h>
  17. #include <new.h> // new(std::nothrow)
  18. #include "resource.h" // unique_hkey
  19. namespace wil
  20. {
  21. //! The key name includes the absolute path of the key in the registry, always starting at a
  22. //! base key, for example, HKEY_LOCAL_MACHINE.
  23. size_t const max_registry_key_name_length = 255;
  24. //! The maximum number of characters allowed in a registry value's name.
  25. size_t const max_registry_value_name_length = 16383;
  26. // unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast
  27. // These classes make it easy to execute a provided function when a
  28. // registry key changes (optionally recursively). Specify the key
  29. // either as a root key + path, or an open registry handle as wil::unique_hkey
  30. // or a raw HKEY value (that will be duplicated).
  31. //
  32. // Example use with exceptions base error handling:
  33. // auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[]
  34. // {
  35. // if (changeKind == RegistryChangeKind::Delete)
  36. // {
  37. // watcher.reset();
  38. // }
  39. // // invalidate cached registry data here
  40. // });
  41. //
  42. // Example use with error code base error handling:
  43. // auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[]
  44. // {
  45. // // invalidate cached registry data here
  46. // });
  47. // RETURN_IF_NULL_ALLOC(watcher);
  48. enum class RegistryChangeKind
  49. {
  50. Modify = 0,
  51. Delete = 1,
  52. };
  53. /// @cond
  54. namespace details
  55. {
  56. struct registry_watcher_state
  57. {
  58. registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  59. : m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
  60. {
  61. }
  62. wistd::function<void(RegistryChangeKind)> m_callback;
  63. unique_hkey m_keyToWatch;
  64. unique_event_nothrow m_eventHandle;
  65. // While not strictly needed since this is ref counted the thread pool wait
  66. // should be last to ensure that the other members are valid
  67. // when it is destructed as it will reference them.
  68. unique_threadpool_wait m_threadPoolWait;
  69. bool m_isRecursive;
  70. volatile long m_refCount = 1;
  71. srwlock m_lock;
  72. // Returns true if the refcount can be increased from a non zero value,
  73. // false it was zero impling that the object is in or on the way to the destructor.
  74. // In this case ReleaseFromCallback() should not be called.
  75. bool TryAddRef()
  76. {
  77. return ::InterlockedIncrement(&m_refCount) > 1;
  78. }
  79. void Release()
  80. {
  81. auto lock = m_lock.lock_exclusive();
  82. if (0 == ::InterlockedDecrement(&m_refCount))
  83. {
  84. lock.reset(); // leave the lock before deleting it.
  85. delete this;
  86. }
  87. }
  88. void ReleaseFromCallback(bool rearm)
  89. {
  90. auto lock = m_lock.lock_exclusive();
  91. if (0 == ::InterlockedDecrement(&m_refCount))
  92. {
  93. // Destroy the thread pool wait now to avoid the wait that would occur in the
  94. // destructor. That wait would cause a deadlock since we are doing this from the callback.
  95. ::CloseThreadpoolWait(m_threadPoolWait.release());
  96. lock.reset(); // leave the lock before deleting it.
  97. delete this;
  98. // Sleep(1); // Enable for testing to find use after free bugs.
  99. }
  100. else if (rearm)
  101. {
  102. ::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);
  103. }
  104. }
  105. };
  106. inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); }
  107. typedef resource_policy<registry_watcher_state *, decltype(&details::delete_registry_watcher_state),
  108. details::delete_registry_watcher_state, details::pointer_access_none> registry_watcher_state_resource_policy;
  109. }
  110. /// @endcond
  111. template <typename storage_t, typename err_policy = err_exception_policy>
  112. class registry_watcher_t : public storage_t
  113. {
  114. public:
  115. // forward all base class constructors...
  116. template <typename... args_t>
  117. explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
  118. // HRESULT or void error handling...
  119. typedef typename err_policy::result result;
  120. // Exception-based constructors
  121. registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  122. {
  123. static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
  124. create(rootKey, subKey, isRecursive, wistd::move(callback));
  125. }
  126. registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  127. {
  128. static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
  129. create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
  130. }
  131. // Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.
  132. result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  133. {
  134. // Most use will want to create the key, consider adding an option for open as a future design change.
  135. unique_hkey keyToWatch;
  136. HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));
  137. if (FAILED(hr))
  138. {
  139. return err_policy::HResult(hr);
  140. }
  141. return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
  142. }
  143. result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  144. {
  145. return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
  146. }
  147. private:
  148. // Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
  149. // to __stdcall
  150. static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT)
  151. {
  152. #ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST
  153. #define __WIL_REGISTRY_CHANGE_CALLBACK_TEST
  154. #endif
  155. __WIL_REGISTRY_CHANGE_CALLBACK_TEST
  156. auto watcherState = static_cast<details::registry_watcher_state *>(context);
  157. if (watcherState->TryAddRef())
  158. {
  159. // using auto reset event so don't need to manually reset.
  160. // failure here is a programming error.
  161. const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive,
  162. REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
  163. watcherState->m_eventHandle.get(), TRUE);
  164. // Call the client before re-arming to ensure that multiple callbacks don't
  165. // run concurrently.
  166. switch (error)
  167. {
  168. case ERROR_SUCCESS:
  169. case ERROR_ACCESS_DENIED:
  170. // Normal modification: send RegistryChangeKind::Modify and re-arm.
  171. watcherState->m_callback(RegistryChangeKind::Modify);
  172. watcherState->ReleaseFromCallback(true);
  173. break;
  174. case ERROR_KEY_DELETED:
  175. // Key deleted, send RegistryChangeKind::Delete, do not re-arm.
  176. watcherState->m_callback(RegistryChangeKind::Delete);
  177. watcherState->ReleaseFromCallback(false);
  178. break;
  179. case ERROR_HANDLE_REVOKED:
  180. // Handle revoked. This can occur if the user session ends before
  181. // the watcher shuts-down. Disarm silently since there is generally no way to respond.
  182. watcherState->ReleaseFromCallback(false);
  183. break;
  184. default:
  185. FAIL_FAST_HR(HRESULT_FROM_WIN32(error));
  186. }
  187. }
  188. }
  189. // This function exists to avoid template expansion of this code based on err_policy.
  190. HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  191. {
  192. wistd::unique_ptr<details::registry_watcher_state> watcherState(new(std::nothrow) details::registry_watcher_state(
  193. wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
  194. RETURN_IF_NULL_ALLOC(watcherState);
  195. RETURN_IF_FAILED(watcherState->m_eventHandle.create());
  196. RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(),
  197. watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
  198. watcherState->m_eventHandle.get(), TRUE));
  199. watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&registry_watcher_t::callback, watcherState.get(), nullptr));
  200. RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
  201. storage_t::reset(watcherState.release()); // no more failures after this, pass ownership
  202. SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr);
  203. return S_OK;
  204. }
  205. };
  206. typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_returncode_policy>> unique_registry_watcher_nothrow;
  207. typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_failfast_policy>> unique_registry_watcher_failfast;
  208. inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
  209. {
  210. unique_registry_watcher_nothrow watcher;
  211. watcher.create(rootKey, subKey, isRecursive, wistd::move(callback));
  212. return watcher; // caller must test for success using if (watcher)
  213. }
  214. inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
  215. {
  216. unique_registry_watcher_nothrow watcher;
  217. watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
  218. return watcher; // caller must test for success using if (watcher)
  219. }
  220. inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  221. {
  222. return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback));
  223. }
  224. inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  225. {
  226. return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
  227. }
  228. #ifdef WIL_ENABLE_EXCEPTIONS
  229. typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_exception_policy >> unique_registry_watcher;
  230. inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  231. {
  232. return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback));
  233. }
  234. inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
  235. {
  236. return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
  237. }
  238. #endif // WIL_ENABLE_EXCEPTIONS
  239. } // namespace wil
  240. #endif