mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Add Windows Implementation Libraries
This commit is contained in:
277
Externals/WIL/include/wil/registry.h
vendored
Normal file
277
Externals/WIL/include/wil/registry.h
vendored
Normal file
@ -0,0 +1,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
|
Reference in New Issue
Block a user