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