master
h 100 lines 4.09 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2
3 #pragma once
4
5 #include <functional>
6 #include <windows.h>
7 #include <wil/stl.h>
8 #include <wil/resource.h>
9 #include <wil/registry.h>
10
11 namespace wsl::windows::common {
12
13 constexpr DWORD registry_notify_filter = REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC;
14
15 class slim_registry_watcher
16 {
17 public:
18 slim_registry_watcher() noexcept = default;
19
20 // Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.
21 HRESULT create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, std::function<void(::wil::RegistryChangeKind)>&& callback) noexcept
22 {
23 ::wil::unique_hkey keyToWatch;
24 HRESULT hr = HRESULT_FROM_WIN32(::RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));
25 if (FAILED(hr))
26 {
27 return hr;
28 }
29 return create_common(std::move(keyToWatch), isRecursive, std::move(callback));
30 }
31
32 HRESULT create(::wil::unique_hkey&& keyToWatch, bool isRecursive, std::function<void(::wil::RegistryChangeKind)>&& callback) noexcept
33 {
34 return create_common(std::move(keyToWatch), isRecursive, std::move(callback));
35 }
36
37 private:
38 // using the default d'tor, destruction must occur in this order
39 std::function<void(::wil::RegistryChangeKind)> m_callback;
40 ::wil::unique_hkey m_keyToWatch;
41 ::wil::unique_event_nothrow m_eventHandle;
42 ::wil::unique_threadpool_wait m_threadPoolWait;
43 bool m_isRecursive;
44
45 static void __stdcall callback(PTP_CALLBACK_INSTANCE, void* context, TP_WAIT*, TP_WAIT_RESULT) noexcept
46 {
47 const auto this_ptr = static_cast<slim_registry_watcher*>(context);
48
49 const LSTATUS error = ::RegNotifyChangeKeyValue(
50 this_ptr->m_keyToWatch.get(), this_ptr->m_isRecursive, registry_notify_filter, this_ptr->m_eventHandle.get(), TRUE);
51
52 // Call the client before re-arming to ensure that multiple callbacks don't
53 // run concurrently.
54 switch (error)
55 {
56 case ERROR_SUCCESS:
57 case ERROR_ACCESS_DENIED:
58 // Normal modification: send RegistryChangeKind::Modify and re-arm.
59 this_ptr->m_callback(::wil::RegistryChangeKind::Modify);
60 ::SetThreadpoolWait(this_ptr->m_threadPoolWait.get(), this_ptr->m_eventHandle.get(), nullptr);
61 break;
62
63 case ERROR_KEY_DELETED:
64 // Key deleted: send RegistryChangeKind::Delete but do not re-arm.
65 this_ptr->m_callback(::wil::RegistryChangeKind::Delete);
66 break;
67
68 case ERROR_HANDLE_REVOKED:
69 // Handle revoked. This can occur if the user session ends before the watcher shuts-down.
70 // Do not re-arm since there is generally no way to respond.
71 break;
72
73 default:
74 // failure here is a programming error.
75 FAIL_FAST_HR(HRESULT_FROM_WIN32(error));
76 }
77 }
78
79 HRESULT create_common(::wil::unique_hkey&& keyToWatch, bool isRecursive, std::function<void(::wil::RegistryChangeKind)>&& callback) noexcept
80 {
81 RETURN_IF_FAILED(m_eventHandle.create());
82
83 m_threadPoolWait.reset(CreateThreadpoolWait(&slim_registry_watcher::callback, this, nullptr));
84 RETURN_LAST_ERROR_IF(!m_threadPoolWait);
85
86 // associate the notification handle with the threadpool before passing it to RegNotifyChangeKeyValue so we get immediate callbacks in the tp
87 SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);
88
89 // 'this' object must be fully created before calling RegNotifyChangeKeyValue, as callbacks can start immediately
90 m_keyToWatch = std::move(keyToWatch);
91 m_isRecursive = isRecursive;
92 m_callback = std::move(callback);
93
94 // no failures after RegNotifyChangeKeyValue succeeds,
95 RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(m_keyToWatch.get(), m_isRecursive, registry_notify_filter, m_eventHandle.get(), TRUE));
96 return S_OK;
97 }
98 };
99
100 } // namespace wsl::windows::common