diff --git a/common-install/common-install.vcxitems b/common-install/common-install.vcxitems
new file mode 100644
index 00000000..fa4e370f
--- /dev/null
+++ b/common-install/common-install.vcxitems
@@ -0,0 +1,19 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ {058d66c6-d88b-4fdb-b0e4-0a6fe7483b95}
+
+
+
+ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common-install/external/WinReg.hpp b/common-install/external/WinReg.hpp
new file mode 100644
index 00000000..9ce2396b
--- /dev/null
+++ b/common-install/external/WinReg.hpp
@@ -0,0 +1,2000 @@
+#ifndef GIOVANNI_DICANIO_WINREG_HPP_INCLUDED
+#define GIOVANNI_DICANIO_WINREG_HPP_INCLUDED
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// *** Modern C++ Wrappers Around Windows Registry C API ***
+//
+// Copyright (C) by Giovanni Dicanio
+//
+// First version: 2017, January 22nd
+// Last update: 2020, June 11th
+//
+// E-mail: . AT REMOVE_THIS gmail.com
+//
+// Registry key handles are safely and conveniently wrapped
+// in the RegKey resource manager C++ class.
+//
+// Errors are signaled throwing exceptions of class RegException.
+// In addition, there are also some methods named like TryGet...
+// (e.g. TryGetDwordValue), that _try_ to perform the given query,
+// and return a std::optional value.
+// (In particular, on failure, the returned std::optional object
+// doesn't contain any value).
+//
+// Unicode UTF-16 strings are represented using the std::wstring class;
+// ATL's CString is not used, to avoid dependencies from ATL or MFC.
+//
+// Compiler: Visual Studio 2019
+// Code compiles cleanly at /W4 on both 32-bit and 64-bit builds.
+//
+// Requires building in Unicode mode (which is the default since VS2005).
+//
+// ===========================================================================
+//
+// The MIT License(MIT)
+//
+// Copyright(c) 2017-2020 by Giovanni Dicanio
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files(the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions :
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// 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.IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+
+#include // Windows Platform SDK
+#include // _ASSERTE
+
+#include // std::unique_ptr, std::make_unique
+#include // std::optional
+#include // std::wstring
+#include // std::system_error
+#include // std::swap, std::pair
+#include // std::vector
+
+
+
+namespace winreg
+{
+
+// Forward class declarations
+class RegException;
+class RegResult;
+
+
+//------------------------------------------------------------------------------
+// Safe, efficient and convenient C++ wrapper around HKEY registry key handles.
+//
+// This class is movable but not copyable.
+//
+// This class is designed to be very *efficient* and low-overhead, for example:
+// non-throwing operations are carefully marked as noexcept, so the C++ compiler
+// can emit optimized code.
+//
+// Moreover, this class just wraps a raw HKEY handle, without any
+// shared-ownership overhead like in std::shared_ptr; you can think of this
+// class kind of like a std::unique_ptr for HKEYs.
+//
+// The class is also swappable (defines a custom non-member swap);
+// relational operators are properly overloaded as well.
+//------------------------------------------------------------------------------
+class RegKey
+{
+public:
+
+ //
+ // Construction/Destruction
+ //
+
+ // Initialize as an empty key handle
+ RegKey() noexcept = default;
+
+ // Take ownership of the input key handle
+ explicit RegKey(HKEY hKey) noexcept;
+
+ // Open the given registry key if it exists, else create a new key.
+ // Uses default KEY_READ|KEY_WRITE access.
+ // For finer grained control, call the Create() method overloads.
+ // Throw RegException on failure.
+ RegKey(HKEY hKeyParent, const std::wstring& subKey);
+
+ // Open the given registry key if it exists, else create a new key.
+ // Allow the caller to specify the desired access to the key (e.g. KEY_READ
+ // for read-only access).
+ // For finer grained control, call the Create() method overloads.
+ // Throw RegException on failure.
+ RegKey(HKEY hKeyParent, const std::wstring& subKey, REGSAM desiredAccess);
+
+
+ // Take ownership of the input key handle.
+ // The input key handle wrapper is reset to an empty state.
+ RegKey(RegKey&& other) noexcept;
+
+ // Move-assign from the input key handle.
+ // Properly check against self-move-assign (which is safe and does nothing).
+ RegKey& operator=(RegKey&& other) noexcept;
+
+ // Ban copy
+ RegKey(const RegKey&) = delete;
+ RegKey& operator=(const RegKey&) = delete;
+
+ // Safely close the wrapped key handle (if any)
+ ~RegKey() noexcept;
+
+
+ //
+ // Properties
+ //
+
+ // Access the wrapped raw HKEY handle
+ [[nodiscard]] HKEY Get() const noexcept;
+
+ // Is the wrapped HKEY handle valid?
+ [[nodiscard]] bool IsValid() const noexcept;
+
+ // Same as IsValid(), but allow a short "if (regKey)" syntax
+ [[nodiscard]] explicit operator bool() const noexcept;
+
+ // Is the wrapped handle a predefined handle (e.g.HKEY_CURRENT_USER) ?
+ [[nodiscard]] bool IsPredefined() const noexcept;
+
+
+ //
+ // Operations
+ //
+
+ // Close current HKEY handle.
+ // If there's no valid handle, do nothing.
+ // This method doesn't close predefined HKEY handles (e.g. HKEY_CURRENT_USER).
+ void Close() noexcept;
+
+ // Transfer ownership of current HKEY to the caller.
+ // Note that the caller is responsible for closing the key handle!
+ [[nodiscard]] HKEY Detach() noexcept;
+
+ // Take ownership of the input HKEY handle.
+ // Safely close any previously open handle.
+ // Input key handle can be nullptr.
+ void Attach(HKEY hKey) noexcept;
+
+ // Non-throwing swap;
+ // Note: There's also a non-member swap overload
+ void SwapWith(RegKey& other) noexcept;
+
+
+ //
+ // Wrappers around Windows Registry APIs.
+ // See the official MSDN documentation for these APIs for detailed explanations
+ // of the wrapper method parameters.
+ //
+
+ // Wrapper around RegCreateKeyEx, that allows you to specify desired access
+ void Create(
+ HKEY hKeyParent,
+ const std::wstring& subKey,
+ REGSAM desiredAccess = KEY_READ | KEY_WRITE
+ );
+
+ // Wrapper around RegCreateKeyEx
+ void Create(
+ HKEY hKeyParent,
+ const std::wstring& subKey,
+ REGSAM desiredAccess,
+ DWORD options,
+ SECURITY_ATTRIBUTES* securityAttributes,
+ DWORD* disposition
+ );
+
+ // Wrapper around RegOpenKeyEx
+ void Open(
+ HKEY hKeyParent,
+ const std::wstring& subKey,
+ REGSAM desiredAccess = KEY_READ | KEY_WRITE
+ );
+
+ // Wrapper around RegCreateKeyEx, that allows you to specify desired access
+ [[nodiscard]] RegResult TryCreate(
+ HKEY hKeyParent,
+ const std::wstring& subKey,
+ REGSAM desiredAccess = KEY_READ | KEY_WRITE
+ ) noexcept;
+
+ // Wrapper around RegCreateKeyEx
+ [[nodiscard]] RegResult TryCreate(
+ HKEY hKeyParent,
+ const std::wstring& subKey,
+ REGSAM desiredAccess,
+ DWORD options,
+ SECURITY_ATTRIBUTES* securityAttributes,
+ DWORD* disposition
+ ) noexcept;
+
+ // Wrapper around RegOpenKeyEx
+ [[nodiscard]] RegResult TryOpen(
+ HKEY hKeyParent,
+ const std::wstring& subKey,
+ REGSAM desiredAccess = KEY_READ | KEY_WRITE
+ ) noexcept;
+
+
+ //
+ // Registry Value Setters
+ //
+
+ void SetDwordValue(const std::wstring& valueName, DWORD data);
+ void SetQwordValue(const std::wstring& valueName, const ULONGLONG& data);
+ void SetStringValue(const std::wstring& valueName, const std::wstring& data);
+ void SetExpandStringValue(const std::wstring& valueName, const std::wstring& data);
+ void SetMultiStringValue(const std::wstring& valueName, const std::vector& data);
+ void SetBinaryValue(const std::wstring& valueName, const std::vector& data);
+ void SetBinaryValue(const std::wstring& valueName, const void* data, DWORD dataSize);
+
+
+ //
+ // Registry Value Getters
+ //
+
+ [[nodiscard]] DWORD GetDwordValue(const std::wstring& valueName) const;
+ [[nodiscard]] ULONGLONG GetQwordValue(const std::wstring& valueName) const;
+ [[nodiscard]] std::wstring GetStringValue(const std::wstring& valueName) const;
+
+ enum class ExpandStringOption
+ {
+ DontExpand,
+ Expand
+ };
+
+ [[nodiscard]] std::wstring GetExpandStringValue(
+ const std::wstring& valueName,
+ ExpandStringOption expandOption = ExpandStringOption::DontExpand
+ ) const;
+
+ [[nodiscard]] std::vector GetMultiStringValue(const std::wstring& valueName) const;
+ [[nodiscard]] std::vector GetBinaryValue(const std::wstring& valueName) const;
+
+
+ //
+ // Registry Value Getters Returning std::optional
+ // (instead of throwing RegException on error)
+ //
+
+ [[nodiscard]] std::optional TryGetDwordValue(const std::wstring& valueName) const;
+ [[nodiscard]] std::optional TryGetQwordValue(const std::wstring& valueName) const;
+ [[nodiscard]] std::optional TryGetStringValue(const std::wstring& valueName) const;
+
+ [[nodiscard]] std::optional TryGetExpandStringValue(
+ const std::wstring& valueName,
+ ExpandStringOption expandOption = ExpandStringOption::DontExpand
+ ) const;
+
+ [[nodiscard]] std::optional> TryGetMultiStringValue(const std::wstring& valueName) const;
+ [[nodiscard]] std::optional> TryGetBinaryValue(const std::wstring& valueName) const;
+
+
+ //
+ // Query Operations
+ //
+
+ void QueryInfoKey(DWORD& subKeys, DWORD &values, FILETIME& lastWriteTime) const;
+
+ // Return the DWORD type ID for the input registry value
+ [[nodiscard]] DWORD QueryValueType(const std::wstring& valueName) const;
+
+ // Enumerate the subkeys of the registry key, using RegEnumKeyEx
+ [[nodiscard]] std::vector EnumSubKeys() const;
+
+ // Enumerate the values under the registry key, using RegEnumValue.
+ // Returns a vector of pairs: In each pair, the wstring is the value name,
+ // the DWORD is the value type.
+ [[nodiscard]] std::vector> EnumValues() const;
+
+
+ //
+ // Misc Registry API Wrappers
+ //
+
+ void DeleteValue(const std::wstring& valueName);
+ void DeleteKey(const std::wstring& subKey, REGSAM desiredAccess);
+ void DeleteTree(const std::wstring& subKey);
+ void CopyTree(const std::wstring& sourceSubKey, const RegKey& destKey);
+ void FlushKey();
+ void LoadKey(const std::wstring& subKey, const std::wstring& filename);
+ void SaveKey(const std::wstring& filename, SECURITY_ATTRIBUTES* securityAttributes) const;
+ void EnableReflectionKey();
+ void DisableReflectionKey();
+ [[nodiscard]] bool QueryReflectionKey() const;
+ void ConnectRegistry(const std::wstring& machineName, HKEY hKeyPredefined);
+
+
+ // Return a string representation of Windows registry types
+ [[nodiscard]] static std::wstring RegTypeToString(DWORD regType);
+
+ //
+ // Relational comparison operators are overloaded as non-members
+ // ==, !=, <, <=, >, >=
+ //
+
+
+private:
+ // The wrapped registry key handle
+ HKEY m_hKey{ nullptr };
+};
+
+
+//------------------------------------------------------------------------------
+// An exception representing an error with the registry operations
+//------------------------------------------------------------------------------
+class RegException
+ : public std::system_error
+{
+public:
+ RegException(LONG errorCode, const char* message);
+ RegException(LONG errorCode, const std::string& message);
+};
+
+
+//------------------------------------------------------------------------------
+// A tiny wrapper around LONG return codes used by the Windows Registry API.
+//------------------------------------------------------------------------------
+class RegResult
+{
+public:
+
+ // Initialize to success code (ERROR_SUCCESS)
+ RegResult() noexcept = default;
+
+ // Conversion constructor, *not* marked "explicit" on purpose,
+ // allows easy and convenient conversion from Win32 API return code type
+ // to this C++ wrapper.
+ RegResult(LONG result) noexcept;
+
+ // Is the wrapped code a success code?
+ [[nodiscard]] bool IsOk() const noexcept;
+
+ // Is the wrapped error code a failure code?
+ [[nodiscard]] bool Failed() const noexcept;
+
+ // Is the wrapped code a success code?
+ [[nodiscard]] explicit operator bool() const noexcept;
+
+ // Get the wrapped Win32 code
+ [[nodiscard]] LONG Code() const noexcept;
+
+ // Return the system error message associated to the current error code
+ [[nodiscard]] std::wstring ErrorMessage() const;
+
+ // Return the system error message associated to the current error code,
+ // using the given input language identifier
+ [[nodiscard]] std::wstring ErrorMessage(DWORD languageId) const;
+
+private:
+ // Error code returned by Windows Registry C API;
+ // default initialized to success code.
+ LONG m_result{ ERROR_SUCCESS };
+};
+
+
+//------------------------------------------------------------------------------
+// Overloads of relational comparison operators for RegKey
+//------------------------------------------------------------------------------
+
+inline bool operator==(const RegKey& a, const RegKey& b) noexcept
+{
+ return a.Get() == b.Get();
+}
+
+inline bool operator!=(const RegKey& a, const RegKey& b) noexcept
+{
+ return a.Get() != b.Get();
+}
+
+inline bool operator<(const RegKey& a, const RegKey& b) noexcept
+{
+ return a.Get() < b.Get();
+}
+
+inline bool operator<=(const RegKey& a, const RegKey& b) noexcept
+{
+ return a.Get() <= b.Get();
+}
+
+inline bool operator>(const RegKey& a, const RegKey& b) noexcept
+{
+ return a.Get() > b.Get();
+}
+
+inline bool operator>=(const RegKey& a, const RegKey& b) noexcept
+{
+ return a.Get() >= b.Get();
+}
+
+
+//------------------------------------------------------------------------------
+// Private Helper Classes and Functions
+//------------------------------------------------------------------------------
+
+namespace detail
+{
+
+//------------------------------------------------------------------------------
+// Simple scoped-based RAII wrapper that *automatically* invokes LocalFree()
+// in its destructor.
+//------------------------------------------------------------------------------
+template
+class ScopedLocalFree
+{
+public:
+
+ typedef T Type;
+ typedef T* TypePtr;
+
+
+ // Init wrapped pointer to nullptr
+ ScopedLocalFree() noexcept = default;
+
+ // Automatically and safely invoke ::LocalFree()
+ ~ScopedLocalFree() noexcept
+ {
+ Free();
+ }
+
+ //
+ // Ban copy and move operations
+ //
+ ScopedLocalFree(const ScopedLocalFree&) = delete;
+ ScopedLocalFree(ScopedLocalFree&&) = delete;
+ ScopedLocalFree& operator=(const ScopedLocalFree&) = delete;
+ ScopedLocalFree& operator=(ScopedLocalFree&&) = delete;
+
+
+ // Read-only access to the wrapped pointer
+ [[nodiscard]] T* Get() const noexcept
+ {
+ return m_ptr;
+ }
+
+ // Writable access to the wrapped pointer
+ [[nodiscard]] T** AddressOf() noexcept
+ {
+ return &m_ptr;
+ }
+
+ // Explicit pointer conversion to bool
+ explicit operator bool() const noexcept
+ {
+ return (m_ptr != nullptr);
+ }
+
+ // Safely invoke ::LocalFree() on the wrapped pointer
+ void Free() noexcept
+ {
+ if (m_ptr != nullptr)
+ {
+ LocalFree(m_ptr);
+ m_ptr = nullptr;
+ }
+ }
+
+
+ //
+ // IMPLEMENTATION
+ //
+private:
+ T* m_ptr{ nullptr };
+};
+
+
+//------------------------------------------------------------------------------
+// Helper function to build a multi-string from a vector.
+//
+// A multi-string is a sequence of contiguous NUL-terminated strings,
+// that terminates with an additional NUL.
+// Basically, considered as a whole, the sequence is terminated by two NULs.
+// E.g.:
+// Hello\0World\0\0
+//------------------------------------------------------------------------------
+[[nodiscard]] inline std::vector BuildMultiString(
+ const std::vector& data
+)
+{
+ // Special case of the empty multi-string
+ if (data.empty())
+ {
+ // Build a vector containing just two NULs
+ return std::vector(2, L'\0');
+ }
+
+ // Get the total length in wchar_ts of the multi-string
+ size_t totalLen = 0;
+ for (const auto& s : data)
+ {
+ // Add one to current string's length for the terminating NUL
+ totalLen += (s.length() + 1);
+ }
+
+ // Add one for the last NUL terminator (making the whole structure double-NUL terminated)
+ totalLen++;
+
+ // Allocate a buffer to store the multi-string
+ std::vector multiString;
+
+ // Reserve room in the vector to speed up the following insertion loop
+ multiString.reserve(totalLen);
+
+ // Copy the single strings into the multi-string
+ for (const auto& s : data)
+ {
+ multiString.insert(multiString.end(), s.begin(), s.end());
+
+ // Don't forget to NUL-terminate the current string
+ multiString.emplace_back(L'\0');
+ }
+
+ // Add the last NUL-terminator
+ multiString.emplace_back(L'\0');
+
+ return multiString;
+}
+
+
+} // namespace detail
+
+
+//------------------------------------------------------------------------------
+// RegKey Inline Methods
+//------------------------------------------------------------------------------
+
+inline RegKey::RegKey(const HKEY hKey) noexcept
+ : m_hKey{ hKey }
+{
+}
+
+
+inline RegKey::RegKey(const HKEY hKeyParent, const std::wstring& subKey)
+{
+ Create(hKeyParent, subKey);
+}
+
+
+inline RegKey::RegKey(const HKEY hKeyParent, const std::wstring& subKey, const REGSAM desiredAccess)
+{
+ Create(hKeyParent, subKey, desiredAccess);
+}
+
+
+inline RegKey::RegKey(RegKey&& other) noexcept
+ : m_hKey{ other.m_hKey }
+{
+ // Other doesn't own the handle anymore
+ other.m_hKey = nullptr;
+}
+
+
+inline RegKey& RegKey::operator=(RegKey&& other) noexcept
+{
+ // Prevent self-move-assign
+ if ((this != &other) && (m_hKey != other.m_hKey))
+ {
+ // Close current
+ Close();
+
+ // Move from other (i.e. take ownership of other's raw handle)
+ m_hKey = other.m_hKey;
+ other.m_hKey = nullptr;
+ }
+ return *this;
+}
+
+
+inline RegKey::~RegKey() noexcept
+{
+ // Release the owned handle (if any)
+ Close();
+}
+
+
+inline HKEY RegKey::Get() const noexcept
+{
+ return m_hKey;
+}
+
+
+inline void RegKey::Close() noexcept
+{
+ if (IsValid())
+ {
+ // Do not call RegCloseKey on predefined keys
+ if (! IsPredefined())
+ {
+ RegCloseKey(m_hKey);
+ }
+
+ // Avoid dangling references
+ m_hKey = nullptr;
+ }
+}
+
+
+inline bool RegKey::IsValid() const noexcept
+{
+ return m_hKey != nullptr;
+}
+
+
+inline RegKey::operator bool() const noexcept
+{
+ return IsValid();
+}
+
+
+inline bool RegKey::IsPredefined() const noexcept
+{
+ // Predefined keys
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724836(v=vs.85).aspx
+
+ if ( (m_hKey == HKEY_CURRENT_USER)
+ || (m_hKey == HKEY_LOCAL_MACHINE)
+ || (m_hKey == HKEY_CLASSES_ROOT)
+ || (m_hKey == HKEY_CURRENT_CONFIG)
+ || (m_hKey == HKEY_CURRENT_USER_LOCAL_SETTINGS)
+ || (m_hKey == HKEY_PERFORMANCE_DATA)
+ || (m_hKey == HKEY_PERFORMANCE_NLSTEXT)
+ || (m_hKey == HKEY_PERFORMANCE_TEXT)
+ || (m_hKey == HKEY_USERS))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+inline HKEY RegKey::Detach() noexcept
+{
+ HKEY hKey = m_hKey;
+
+ // We don't own the HKEY handle anymore
+ m_hKey = nullptr;
+
+ // Transfer ownership to the caller
+ return hKey;
+}
+
+
+inline void RegKey::Attach(const HKEY hKey) noexcept
+{
+ // Prevent self-attach
+ if (m_hKey != hKey)
+ {
+ // Close any open registry handle
+ Close();
+
+ // Take ownership of the input hKey
+ m_hKey = hKey;
+ }
+}
+
+
+inline void RegKey::SwapWith(RegKey& other) noexcept
+{
+ // Enable ADL (not necessary in this case, but good practice)
+ using std::swap;
+
+ // Swap the raw handle members
+ swap(m_hKey, other.m_hKey);
+}
+
+
+inline void swap(RegKey& a, RegKey& b) noexcept
+{
+ a.SwapWith(b);
+}
+
+
+inline void RegKey::Create(
+ const HKEY hKeyParent,
+ const std::wstring& subKey,
+ const REGSAM desiredAccess
+)
+{
+ constexpr DWORD kDefaultOptions = REG_OPTION_NON_VOLATILE;
+
+ Create(hKeyParent, subKey, desiredAccess, kDefaultOptions,
+ nullptr, // no security attributes,
+ nullptr // no disposition
+ );
+}
+
+
+inline void RegKey::Create(
+ const HKEY hKeyParent,
+ const std::wstring& subKey,
+ const REGSAM desiredAccess,
+ const DWORD options,
+ SECURITY_ATTRIBUTES* const securityAttributes,
+ DWORD* const disposition
+)
+{
+ HKEY hKey = nullptr;
+ LONG retCode = RegCreateKeyEx(
+ hKeyParent,
+ subKey.c_str(),
+ 0, // reserved
+ REG_NONE, // user-defined class type parameter not supported
+ options,
+ desiredAccess,
+ securityAttributes,
+ &hKey,
+ disposition
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegCreateKeyEx failed." };
+ }
+
+ // Safely close any previously opened key
+ Close();
+
+ // Take ownership of the newly created key
+ m_hKey = hKey;
+}
+
+
+inline void RegKey::Open(
+ const HKEY hKeyParent,
+ const std::wstring& subKey,
+ const REGSAM desiredAccess
+)
+{
+ HKEY hKey = nullptr;
+ LONG retCode = RegOpenKeyEx(
+ hKeyParent,
+ subKey.c_str(),
+ REG_NONE, // default options
+ desiredAccess,
+ &hKey
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegOpenKeyEx failed." };
+ }
+
+ // Safely close any previously opened key
+ Close();
+
+ // Take ownership of the newly created key
+ m_hKey = hKey;
+}
+
+
+inline RegResult RegKey::TryCreate(
+ const HKEY hKeyParent,
+ const std::wstring& subKey,
+ const REGSAM desiredAccess
+) noexcept
+{
+ constexpr DWORD kDefaultOptions = REG_OPTION_NON_VOLATILE;
+
+ return TryCreate(hKeyParent, subKey, desiredAccess, kDefaultOptions,
+ nullptr, // no security attributes,
+ nullptr // no disposition
+ );
+}
+
+
+inline RegResult RegKey::TryCreate(
+ const HKEY hKeyParent,
+ const std::wstring& subKey,
+ const REGSAM desiredAccess,
+ const DWORD options,
+ SECURITY_ATTRIBUTES* const securityAttributes,
+ DWORD* const disposition
+) noexcept
+{
+ HKEY hKey = nullptr;
+ RegResult retCode = RegCreateKeyEx(
+ hKeyParent,
+ subKey.c_str(),
+ 0, // reserved
+ REG_NONE, // user-defined class type parameter not supported
+ options,
+ desiredAccess,
+ securityAttributes,
+ &hKey,
+ disposition
+ );
+ if (retCode.Failed())
+ {
+ return retCode;
+ }
+
+ // Safely close any previously opened key
+ Close();
+
+ // Take ownership of the newly created key
+ m_hKey = hKey;
+
+ _ASSERTE(retCode.IsOk());
+ return retCode;
+}
+
+
+inline RegResult RegKey::TryOpen(
+ const HKEY hKeyParent,
+ const std::wstring& subKey,
+ const REGSAM desiredAccess
+) noexcept
+{
+ HKEY hKey = nullptr;
+ RegResult retCode = RegOpenKeyEx(
+ hKeyParent,
+ subKey.c_str(),
+ REG_NONE, // default options
+ desiredAccess,
+ &hKey
+ );
+ if (retCode.Failed())
+ {
+ return retCode;
+ }
+
+ // Safely close any previously opened key
+ Close();
+
+ // Take ownership of the newly created key
+ m_hKey = hKey;
+
+ _ASSERTE(retCode.IsOk());
+ return retCode;
+}
+
+
+inline void RegKey::SetDwordValue(const std::wstring& valueName, const DWORD data)
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegSetValueEx(
+ m_hKey,
+ valueName.c_str(),
+ 0, // reserved
+ REG_DWORD,
+ reinterpret_cast(&data),
+ sizeof(data)
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot write DWORD value: RegSetValueEx failed." };
+ }
+}
+
+
+inline void RegKey::SetQwordValue(const std::wstring& valueName, const ULONGLONG& data)
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegSetValueEx(
+ m_hKey,
+ valueName.c_str(),
+ 0, // reserved
+ REG_QWORD,
+ reinterpret_cast(&data),
+ sizeof(data)
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot write QWORD value: RegSetValueEx failed." };
+ }
+}
+
+
+inline void RegKey::SetStringValue(const std::wstring& valueName, const std::wstring& data)
+{
+ _ASSERTE(IsValid());
+
+ // String size including the terminating NUL, in bytes
+ const DWORD dataSize = static_cast((data.length() + 1) * sizeof(wchar_t));
+
+ LONG retCode = RegSetValueEx(
+ m_hKey,
+ valueName.c_str(),
+ 0, // reserved
+ REG_SZ,
+ reinterpret_cast(data.c_str()),
+ dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot write string value: RegSetValueEx failed." };
+ }
+}
+
+
+inline void RegKey::SetExpandStringValue(const std::wstring& valueName, const std::wstring& data)
+{
+ _ASSERTE(IsValid());
+
+ // String size including the terminating NUL, in bytes
+ const DWORD dataSize = static_cast((data.length() + 1) * sizeof(wchar_t));
+
+ LONG retCode = RegSetValueEx(
+ m_hKey,
+ valueName.c_str(),
+ 0, // reserved
+ REG_EXPAND_SZ,
+ reinterpret_cast(data.c_str()),
+ dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot write expand string value: RegSetValueEx failed." };
+ }
+}
+
+
+inline void RegKey::SetMultiStringValue(
+ const std::wstring& valueName,
+ const std::vector& data
+)
+{
+ _ASSERTE(IsValid());
+
+ // First, we have to build a double-NUL-terminated multi-string from the input data
+ const std::vector multiString = detail::BuildMultiString(data);
+
+ // Total size, in bytes, of the whole multi-string structure
+ const DWORD dataSize = static_cast(multiString.size() * sizeof(wchar_t));
+
+ LONG retCode = RegSetValueEx(
+ m_hKey,
+ valueName.c_str(),
+ 0, // reserved
+ REG_MULTI_SZ,
+ reinterpret_cast(&multiString[0]),
+ dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot write multi-string value: RegSetValueEx failed." };
+ }
+}
+
+
+inline void RegKey::SetBinaryValue(const std::wstring& valueName, const std::vector& data)
+{
+ _ASSERTE(IsValid());
+
+ // Total data size, in bytes
+ const DWORD dataSize = static_cast(data.size());
+
+ LONG retCode = RegSetValueEx(
+ m_hKey,
+ valueName.c_str(),
+ 0, // reserved
+ REG_BINARY,
+ &data[0],
+ dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot write binary data value: RegSetValueEx failed." };
+ }
+}
+
+
+inline void RegKey::SetBinaryValue(
+ const std::wstring& valueName,
+ const void* const data,
+ const DWORD dataSize
+)
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegSetValueEx(
+ m_hKey,
+ valueName.c_str(),
+ 0, // reserved
+ REG_BINARY,
+ static_cast(data),
+ dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot write binary data value: RegSetValueEx failed." };
+ }
+}
+
+
+inline DWORD RegKey::GetDwordValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ DWORD data = 0; // to be read from the registry
+ DWORD dataSize = sizeof(data); // size of data, in bytes
+
+ constexpr DWORD flags = RRF_RT_REG_DWORD;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &data,
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get DWORD value: RegGetValue failed." };
+ }
+
+ return data;
+}
+
+
+inline ULONGLONG RegKey::GetQwordValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ ULONGLONG data = 0; // to be read from the registry
+ DWORD dataSize = sizeof(data); // size of data, in bytes
+
+ constexpr DWORD flags = RRF_RT_REG_QWORD;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &data,
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get QWORD value: RegGetValue failed." };
+ }
+
+ return data;
+}
+
+
+inline std::wstring RegKey::GetStringValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ // Get the size of the result string
+ DWORD dataSize = 0; // size of data, in bytes
+ constexpr DWORD flags = RRF_RT_REG_SZ;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get size of string value: RegGetValue failed." };
+ }
+
+ // Allocate a string of proper size.
+ // Note that dataSize is in bytes and includes the terminating NUL;
+ // we have to convert the size from bytes to wchar_ts for wstring::resize.
+ std::wstring result(dataSize / sizeof(wchar_t), L' ');
+
+ // Call RegGetValue for the second time to read the string's content
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &result[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get string value: RegGetValue failed." };
+ }
+
+ // Remove the NUL terminator scribbled by RegGetValue from the wstring
+ result.resize((dataSize / sizeof(wchar_t)) - 1);
+
+ return result;
+}
+
+
+inline std::wstring RegKey::GetExpandStringValue(
+ const std::wstring& valueName,
+ const ExpandStringOption expandOption
+) const
+{
+ _ASSERTE(IsValid());
+
+ DWORD flags = RRF_RT_REG_EXPAND_SZ;
+
+ // Adjust the flag for RegGetValue considering the expand string option specified by the caller
+ if (expandOption == ExpandStringOption::DontExpand)
+ {
+ flags |= RRF_NOEXPAND;
+ }
+
+ // Get the size of the result string
+ DWORD dataSize = 0; // size of data, in bytes
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get size of expand string value: RegGetValue failed." };
+ }
+
+ // Allocate a string of proper size.
+ // Note that dataSize is in bytes and includes the terminating NUL.
+ // We must convert from bytes to wchar_ts for wstring::resize.
+ std::wstring result(dataSize / sizeof(wchar_t), L' ');
+
+ // Call RegGetValue for the second time to read the string's content
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &result[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get expand string value: RegGetValue failed." };
+ }
+
+ // Remove the NUL terminator scribbled by RegGetValue from the wstring
+ result.resize((dataSize / sizeof(wchar_t)) - 1);
+
+ return result;
+}
+
+
+inline std::vector RegKey::GetMultiStringValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ // Request the size of the multi-string, in bytes
+ DWORD dataSize = 0;
+ constexpr DWORD flags = RRF_RT_REG_MULTI_SZ;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get size of multi-string value: RegGetValue failed." };
+ }
+
+ // Allocate room for the result multi-string.
+ // Note that dataSize is in bytes, but our vector::resize method requires size
+ // to be expressed in wchar_ts.
+ std::vector data(dataSize / sizeof(wchar_t), L' ');
+
+ // Read the multi-string from the registry into the vector object
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // no type required
+ &data[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get multi-string value: RegGetValue failed." };
+ }
+
+ // Resize vector to the actual size returned by GetRegValue.
+ // Note that the vector is a vector of wchar_ts, instead the size returned by GetRegValue
+ // is in bytes, so we have to scale from bytes to wchar_t count.
+ data.resize(dataSize / sizeof(wchar_t));
+
+ // Parse the double-NUL-terminated string into a vector,
+ // which will be returned to the caller
+ std::vector result;
+ const wchar_t* currStringPtr = &data[0];
+ while (*currStringPtr != L'\0')
+ {
+ // Current string is NUL-terminated, so get its length calling wcslen
+ const size_t currStringLength = wcslen(currStringPtr);
+
+ // Add current string to the result vector
+ result.emplace_back(currStringPtr, currStringLength);
+
+ // Move to the next string
+ currStringPtr += currStringLength + 1;
+ }
+
+ return result;
+}
+
+
+inline std::vector RegKey::GetBinaryValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ // Get the size of the binary data
+ DWORD dataSize = 0; // size of data, in bytes
+ constexpr DWORD flags = RRF_RT_REG_BINARY;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get size of binary data: RegGetValue failed." };
+ }
+
+ // Allocate a buffer of proper size to store the binary data
+ std::vector data(dataSize);
+
+ // Call RegGetValue for the second time to read the data content
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &data[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get binary data: RegGetValue failed." };
+ }
+
+ return data;
+}
+
+
+inline std::optional RegKey::TryGetDwordValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ DWORD data = 0; // to be read from the registry
+ DWORD dataSize = sizeof(data); // size of data, in bytes
+
+ constexpr DWORD flags = RRF_RT_REG_DWORD;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &data,
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ return data;
+}
+
+
+inline std::optional RegKey::TryGetQwordValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ ULONGLONG data = 0; // to be read from the registry
+ DWORD dataSize = sizeof(data); // size of data, in bytes
+
+ constexpr DWORD flags = RRF_RT_REG_QWORD;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &data,
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ return data;
+}
+
+
+inline std::optional RegKey::TryGetStringValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ // Get the size of the result string
+ DWORD dataSize = 0; // size of data, in bytes
+ constexpr DWORD flags = RRF_RT_REG_SZ;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ // Allocate a string of proper size.
+ // Note that dataSize is in bytes and includes the terminating NUL;
+ // we have to convert the size from bytes to wchar_ts for wstring::resize.
+ std::wstring result(dataSize / sizeof(wchar_t), L' ');
+
+ // Call RegGetValue for the second time to read the string's content
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &result[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ // Remove the NUL terminator scribbled by RegGetValue from the wstring
+ result.resize((dataSize / sizeof(wchar_t)) - 1);
+
+ return result;
+}
+
+
+inline std::optional RegKey::TryGetExpandStringValue(
+ const std::wstring& valueName,
+ const ExpandStringOption expandOption
+) const
+{
+ _ASSERTE(IsValid());
+
+ DWORD flags = RRF_RT_REG_EXPAND_SZ;
+
+ // Adjust the flag for RegGetValue considering the expand string option specified by the caller
+ if (expandOption == ExpandStringOption::DontExpand)
+ {
+ flags |= RRF_NOEXPAND;
+ }
+
+ // Get the size of the result string
+ DWORD dataSize = 0; // size of data, in bytes
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ // Allocate a string of proper size.
+ // Note that dataSize is in bytes and includes the terminating NUL.
+ // We must convert from bytes to wchar_ts for wstring::resize.
+ std::wstring result(dataSize / sizeof(wchar_t), L' ');
+
+ // Call RegGetValue for the second time to read the string's content
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &result[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ // Remove the NUL terminator scribbled by RegGetValue from the wstring
+ result.resize((dataSize / sizeof(wchar_t)) - 1);
+
+ return result;
+}
+
+
+inline std::optional> RegKey::TryGetMultiStringValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ // Request the size of the multi-string, in bytes
+ DWORD dataSize = 0;
+ constexpr DWORD flags = RRF_RT_REG_MULTI_SZ;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ // Allocate room for the result multi-string.
+ // Note that dataSize is in bytes, but our vector::resize method requires size
+ // to be expressed in wchar_ts.
+ std::vector data(dataSize / sizeof(wchar_t), L' ');
+
+ // Read the multi-string from the registry into the vector object
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // no type required
+ &data[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ // Resize vector to the actual size returned by GetRegValue.
+ // Note that the vector is a vector of wchar_ts, instead the size returned by GetRegValue
+ // is in bytes, so we have to scale from bytes to wchar_t count.
+ data.resize(dataSize / sizeof(wchar_t));
+
+ // Parse the double-NUL-terminated string into a vector,
+ // which will be returned to the caller
+ std::vector result;
+ const wchar_t* currStringPtr = &data[0];
+ while (*currStringPtr != L'\0')
+ {
+ // Current string is NUL-terminated, so get its length calling wcslen
+ const size_t currStringLength = wcslen(currStringPtr);
+
+ // Add current string to the result vector
+ result.emplace_back(currStringPtr, currStringLength);
+
+ // Move to the next string
+ currStringPtr += currStringLength + 1;
+ }
+
+ return result;
+}
+
+
+inline std::optional> RegKey::TryGetBinaryValue(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ // Get the size of the binary data
+ DWORD dataSize = 0; // size of data, in bytes
+ constexpr DWORD flags = RRF_RT_REG_BINARY;
+ LONG retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ nullptr, // output buffer not needed now
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ // Allocate a buffer of proper size to store the binary data
+ std::vector data(dataSize);
+
+ // Call RegGetValue for the second time to read the data content
+ retCode = RegGetValue(
+ m_hKey,
+ nullptr, // no subkey
+ valueName.c_str(),
+ flags,
+ nullptr, // type not required
+ &data[0], // output buffer
+ &dataSize
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ return {};
+ }
+
+ return data;
+}
+
+
+inline std::vector RegKey::EnumSubKeys() const
+{
+ _ASSERTE(IsValid());
+
+ // Get some useful enumeration info, like the total number of subkeys
+ // and the maximum length of the subkey names
+ DWORD subKeyCount = 0;
+ DWORD maxSubKeyNameLen = 0;
+ LONG retCode = RegQueryInfoKey(
+ m_hKey,
+ nullptr, // no user-defined class
+ nullptr, // no user-defined class size
+ nullptr, // reserved
+ &subKeyCount,
+ &maxSubKeyNameLen,
+ nullptr, // no subkey class length
+ nullptr, // no value count
+ nullptr, // no value name max length
+ nullptr, // no max value length
+ nullptr, // no security descriptor
+ nullptr // no last write time
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{
+ retCode,
+ "RegQueryInfoKey failed while preparing for subkey enumeration."
+ };
+ }
+
+ // NOTE: According to the MSDN documentation, the size returned for subkey name max length
+ // does *not* include the terminating NUL, so let's add +1 to take it into account
+ // when I allocate the buffer for reading subkey names.
+ maxSubKeyNameLen++;
+
+ // Preallocate a buffer for the subkey names
+ auto nameBuffer = std::make_unique(maxSubKeyNameLen);
+
+ // The result subkey names will be stored here
+ std::vector subkeyNames;
+
+ // Reserve room in the vector to speed up the following insertion loop
+ subkeyNames.reserve(subKeyCount);
+
+ // Enumerate all the subkeys
+ for (DWORD index = 0; index < subKeyCount; index++)
+ {
+ // Get the name of the current subkey
+ DWORD subKeyNameLen = maxSubKeyNameLen;
+ retCode = RegEnumKeyEx(
+ m_hKey,
+ index,
+ nameBuffer.get(),
+ &subKeyNameLen,
+ nullptr, // reserved
+ nullptr, // no class
+ nullptr, // no class
+ nullptr // no last write time
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot enumerate subkeys: RegEnumKeyEx failed." };
+ }
+
+ // On success, the ::RegEnumKeyEx API writes the length of the
+ // subkey name in the subKeyNameLen output parameter
+ // (not including the terminating NUL).
+ // So I can build a wstring based on that length.
+ subkeyNames.emplace_back(nameBuffer.get(), subKeyNameLen);
+ }
+
+ return subkeyNames;
+}
+
+
+inline std::vector> RegKey::EnumValues() const
+{
+ _ASSERTE(IsValid());
+
+ // Get useful enumeration info, like the total number of values
+ // and the maximum length of the value names
+ DWORD valueCount = 0;
+ DWORD maxValueNameLen = 0;
+ LONG retCode = RegQueryInfoKey(
+ m_hKey,
+ nullptr, // no user-defined class
+ nullptr, // no user-defined class size
+ nullptr, // reserved
+ nullptr, // no subkey count
+ nullptr, // no subkey max length
+ nullptr, // no subkey class length
+ &valueCount,
+ &maxValueNameLen,
+ nullptr, // no max value length
+ nullptr, // no security descriptor
+ nullptr // no last write time
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{
+ retCode,
+ "RegQueryInfoKey failed while preparing for value enumeration."
+ };
+ }
+
+ // NOTE: According to the MSDN documentation, the size returned for value name max length
+ // does *not* include the terminating NUL, so let's add +1 to take it into account
+ // when I allocate the buffer for reading value names.
+ maxValueNameLen++;
+
+ // Preallocate a buffer for the value names
+ auto nameBuffer = std::make_unique(maxValueNameLen);
+
+ // The value names and types will be stored here
+ std::vector> valueInfo;
+
+ // Reserve room in the vector to speed up the following insertion loop
+ valueInfo.reserve(valueCount);
+
+ // Enumerate all the values
+ for (DWORD index = 0; index < valueCount; index++)
+ {
+ // Get the name and the type of the current value
+ DWORD valueNameLen = maxValueNameLen;
+ DWORD valueType = 0;
+ retCode = RegEnumValue(
+ m_hKey,
+ index,
+ nameBuffer.get(),
+ &valueNameLen,
+ nullptr, // reserved
+ &valueType,
+ nullptr, // no data
+ nullptr // no data size
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot enumerate values: RegEnumValue failed." };
+ }
+
+ // On success, the RegEnumValue API writes the length of the
+ // value name in the valueNameLen output parameter
+ // (not including the terminating NUL).
+ // So we can build a wstring based on that.
+ valueInfo.emplace_back(
+ std::wstring{ nameBuffer.get(), valueNameLen },
+ valueType
+ );
+ }
+
+ return valueInfo;
+}
+
+
+inline DWORD RegKey::QueryValueType(const std::wstring& valueName) const
+{
+ _ASSERTE(IsValid());
+
+ DWORD typeId = 0; // will be returned by RegQueryValueEx
+
+ LONG retCode = RegQueryValueEx(
+ m_hKey,
+ valueName.c_str(),
+ nullptr, // reserved
+ &typeId,
+ nullptr, // not interested
+ nullptr // not interested
+ );
+
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "Cannot get the value type: RegQueryValueEx failed." };
+ }
+
+ return typeId;
+}
+
+
+inline void RegKey::QueryInfoKey(DWORD& subKeys, DWORD &values, FILETIME& lastWriteTime) const
+{
+ _ASSERTE(IsValid());
+
+ subKeys = 0;
+ values = 0;
+ lastWriteTime.dwLowDateTime = lastWriteTime.dwHighDateTime = 0;
+
+ LONG retCode = RegQueryInfoKey(
+ m_hKey,
+ nullptr,
+ nullptr,
+ nullptr,
+ &subKeys,
+ nullptr,
+ nullptr,
+ &values,
+ nullptr,
+ nullptr,
+ nullptr,
+ &lastWriteTime
+ );
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegQueryInfoKey failed." };
+ }
+}
+
+
+inline void RegKey::DeleteValue(const std::wstring& valueName)
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegDeleteValue(m_hKey, valueName.c_str());
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegDeleteValue failed." };
+ }
+}
+
+
+inline void RegKey::DeleteKey(const std::wstring& subKey, const REGSAM desiredAccess)
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegDeleteKeyEx(m_hKey, subKey.c_str(), desiredAccess, 0);
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegDeleteKeyEx failed." };
+ }
+}
+
+
+inline void RegKey::DeleteTree(const std::wstring& subKey)
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegDeleteTree(m_hKey, subKey.c_str());
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegDeleteTree failed." };
+ }
+}
+
+
+inline void RegKey::CopyTree(const std::wstring& sourceSubKey, const RegKey& destKey)
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegCopyTree(m_hKey, sourceSubKey.c_str(), destKey.Get());
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegCopyTree failed." };
+ }
+}
+
+
+inline void RegKey::FlushKey()
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegFlushKey(m_hKey);
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegFlushKey failed." };
+ }
+}
+
+
+inline void RegKey::LoadKey(const std::wstring& subKey, const std::wstring& filename)
+{
+ Close();
+
+ LONG retCode = RegLoadKey(m_hKey, subKey.c_str(), filename.c_str());
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegLoadKey failed." };
+ }
+}
+
+
+inline void RegKey::SaveKey(
+ const std::wstring& filename,
+ SECURITY_ATTRIBUTES* const securityAttributes
+) const
+{
+ _ASSERTE(IsValid());
+
+ LONG retCode = RegSaveKey(m_hKey, filename.c_str(), securityAttributes);
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegSaveKey failed." };
+ }
+}
+
+
+inline void RegKey::EnableReflectionKey()
+{
+ LONG retCode = RegEnableReflectionKey(m_hKey);
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegEnableReflectionKey failed." };
+ }
+}
+
+
+inline void RegKey::DisableReflectionKey()
+{
+ LONG retCode = RegDisableReflectionKey(m_hKey);
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegDisableReflectionKey failed." };
+ }
+}
+
+
+inline bool RegKey::QueryReflectionKey() const
+{
+ BOOL isReflectionDisabled = FALSE;
+ LONG retCode = RegQueryReflectionKey(m_hKey, &isReflectionDisabled);
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegQueryReflectionKey failed." };
+ }
+
+ return (isReflectionDisabled ? true : false);
+}
+
+
+inline void RegKey::ConnectRegistry(const std::wstring& machineName, const HKEY hKeyPredefined)
+{
+ // Safely close any previously opened key
+ Close();
+
+ HKEY hKeyResult = nullptr;
+ LONG retCode = RegConnectRegistry(machineName.c_str(), hKeyPredefined, &hKeyResult);
+ if (retCode != ERROR_SUCCESS)
+ {
+ throw RegException{ retCode, "RegConnectRegistry failed." };
+ }
+
+ // Take ownership of the result key
+ m_hKey = hKeyResult;
+}
+
+
+inline std::wstring RegKey::RegTypeToString(const DWORD regType)
+{
+ switch (regType)
+ {
+ case REG_SZ: return L"REG_SZ";
+ case REG_EXPAND_SZ: return L"REG_EXPAND_SZ";
+ case REG_MULTI_SZ: return L"REG_MULTI_SZ";
+ case REG_DWORD: return L"REG_DWORD";
+ case REG_QWORD: return L"REG_QWORD";
+ case REG_BINARY: return L"REG_BINARY";
+
+ default: return L"Unknown/unsupported registry type";
+ }
+}
+
+
+//------------------------------------------------------------------------------
+// RegException Inline Methods
+//------------------------------------------------------------------------------
+
+inline RegException::RegException(const LONG errorCode, const char* const message)
+ : std::system_error{ errorCode, std::system_category(), message }
+{}
+
+
+inline RegException::RegException(const LONG errorCode, const std::string& message)
+ : std::system_error{ errorCode, std::system_category(), message }
+{}
+
+
+//------------------------------------------------------------------------------
+// RegResult Inline Methods
+//------------------------------------------------------------------------------
+
+inline RegResult::RegResult(const LONG result) noexcept
+ : m_result{ result }
+{}
+
+
+inline bool RegResult::IsOk() const noexcept
+{
+ return m_result == ERROR_SUCCESS;
+}
+
+
+inline bool RegResult::Failed() const noexcept
+{
+ return m_result != ERROR_SUCCESS;
+}
+
+
+inline RegResult::operator bool() const noexcept
+{
+ return IsOk();
+}
+
+
+inline LONG RegResult::Code() const noexcept
+{
+ return m_result;
+}
+
+
+inline std::wstring RegResult::ErrorMessage() const
+{
+ return ErrorMessage(MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT));
+}
+
+
+inline std::wstring RegResult::ErrorMessage(const DWORD languageId) const
+{
+ // Invoke FormatMessage() to retrieve the error message from Windows
+ detail::ScopedLocalFree messagePtr;
+ DWORD retCode = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ m_result,
+ languageId,
+ reinterpret_cast(messagePtr.AddressOf()),
+ 0,
+ nullptr
+ );
+ if (retCode == 0)
+ {
+ // FormatMessage failed: return an empty string
+ return std::wstring{};
+ }
+
+ // Safely copy the C-string returned by FormatMessage() into a std::wstring object,
+ // and return it back to the caller.
+ return std::wstring{ messagePtr.Get() };
+}
+
+
+} // namespace winreg
+
+
+#endif // GIOVANNI_DICANIO_WINREG_HPP_INCLUDED
diff --git a/common-install/utility-install.hpp b/common-install/utility-install.hpp
new file mode 100644
index 00000000..e1823e43
--- /dev/null
+++ b/common-install/utility-install.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include
+#include
+
+#include "external/WinReg.hpp"
+
+namespace fs = std::filesystem;
+namespace wr = winreg;
+
+inline const std::wstring DRIVER_NAME = L"rawaccel";
+inline const std::wstring DRIVER_FILE_NAME = DRIVER_NAME + L".sys";
+
+fs::path get_sys_path() {
+ std::wstring path;
+ path.resize(MAX_PATH);
+
+ UINT chars_copied = GetSystemDirectoryW(path.data(), MAX_PATH);
+ if (chars_copied == 0) throw std::runtime_error("GetSystemDirectory failed");
+
+ path.resize(chars_copied);
+ return path;
+}
+
+fs::path get_target_path() {
+ return get_sys_path() / L"drivers" / DRIVER_FILE_NAME;
+}
+
+fs::path make_temp_path(const fs::path& p) {
+ auto tmp_path = p;
+ tmp_path.concat(".tmp");
+ return tmp_path;
+}
+
+template
+void modify_upper_filters(Func fn) {
+ const std::wstring FILTERS_NAME = L"UpperFilters";
+ wr::RegKey key(
+ HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4d36e96f-e325-11ce-bfc1-08002be10318}"
+ );
+
+ std::vector filters = key.GetMultiStringValue(FILTERS_NAME);
+ fn(filters);
+ key.SetMultiStringValue(FILTERS_NAME, filters);
+}
diff --git a/common/common.vcxitems b/common/common.vcxitems
new file mode 100644
index 00000000..f60f78a6
--- /dev/null
+++ b/common/common.vcxitems
@@ -0,0 +1,22 @@
+
+
+
+ $(MSBuildAllProjects);$(MSBuildThisFileFullPath)
+ true
+ {24b4226f-1461-408f-a1a4-1371c97153ea}
+
+
+
+ %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory)
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/external/clipp.h b/common/external/clipp.h
new file mode 100644
index 00000000..cca15549
--- /dev/null
+++ b/common/external/clipp.h
@@ -0,0 +1,7027 @@
+/*****************************************************************************
+ * ___ _ _ ___ ___
+ * | _|| | | | | _ \ _ \ CLIPP - command line interfaces for modern C++
+ * | |_ | |_ | | | _/ _/ version 1.2.3
+ * |___||___||_| |_| |_| https://github.com/muellan/clipp
+ *
+ * Licensed under the MIT License .
+ * Copyright (c) 2017-2018 André Müller
+ *
+ * ---------------------------------------------------------------------------
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *****************************************************************************/
+
+#ifndef AM_CLIPP_H__
+#define AM_CLIPP_H__
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+/*************************************************************************//**
+ *
+ * @brief primary namespace
+ *
+ *****************************************************************************/
+namespace clipp {
+
+
+
+/*****************************************************************************
+ *
+ * basic constants and datatype definitions
+ *
+ *****************************************************************************/
+using arg_index = int;
+
+using arg_string = std::string;
+using doc_string = std::string;
+
+using arg_list = std::vector;
+
+
+
+/*************************************************************************//**
+ *
+ * @brief tristate
+ *
+ *****************************************************************************/
+enum class tri : char { no, yes, either };
+
+inline constexpr bool operator == (tri t, bool b) noexcept {
+ return b ? t != tri::no : t != tri::yes;
+}
+inline constexpr bool operator == (bool b, tri t) noexcept { return (t == b); }
+inline constexpr bool operator != (tri t, bool b) noexcept { return !(t == b); }
+inline constexpr bool operator != (bool b, tri t) noexcept { return !(t == b); }
+
+
+
+/*************************************************************************//**
+ *
+ * @brief (start,size) index range
+ *
+ *****************************************************************************/
+class subrange {
+public:
+ using size_type = arg_string::size_type;
+
+ /** @brief default: no match */
+ explicit constexpr
+ subrange() noexcept :
+ at_{arg_string::npos}, length_{0}
+ {}
+
+ /** @brief match length & position within subject string */
+ explicit constexpr
+ subrange(size_type pos, size_type len) noexcept :
+ at_{pos}, length_{len}
+ {}
+
+ /** @brief position of the match within the subject string */
+ constexpr size_type at() const noexcept { return at_; }
+ /** @brief length of the matching subsequence */
+ constexpr size_type length() const noexcept { return length_; }
+
+ /** @brief returns true, if query string is a prefix of the subject string */
+ constexpr bool prefix() const noexcept {
+ return at_ == 0;
+ }
+
+ /** @brief returns true, if query is a substring of the query string */
+ constexpr explicit operator bool () const noexcept {
+ return at_ != arg_string::npos;
+ }
+
+private:
+ size_type at_;
+ size_type length_;
+};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief match predicates
+ *
+ *****************************************************************************/
+using match_predicate = std::function;
+using match_function = std::function;
+
+
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief type traits (NOT FOR DIRECT USE IN CLIENT CODE!)
+ * no interface guarantees; might be changed or removed in the future
+ *
+ *****************************************************************************/
+namespace traits {
+
+/*************************************************************************//**
+ *
+ * @brief function (class) signature type trait
+ *
+ *****************************************************************************/
+template
+constexpr auto
+check_is_callable(int) -> decltype(
+ std::declval()(std::declval()...),
+ std::integral_constant>::value>{} );
+
+template
+constexpr auto
+check_is_callable(long) -> std::false_type;
+
+template
+constexpr auto
+check_is_callable_without_arg(int) -> decltype(
+ std::declval()(),
+ std::integral_constant>::value>{} );
+
+template
+constexpr auto
+check_is_callable_without_arg(long) -> std::false_type;
+
+
+
+template
+constexpr auto
+check_is_void_callable(int) -> decltype(
+ std::declval()(std::declval()...), std::true_type{});
+
+template
+constexpr auto
+check_is_void_callable(long) -> std::false_type;
+
+template
+constexpr auto
+check_is_void_callable_without_arg(int) -> decltype(
+ std::declval()(), std::true_type{});
+
+template
+constexpr auto
+check_is_void_callable_without_arg(long) -> std::false_type;
+
+
+
+template
+struct is_callable;
+
+
+template
+struct is_callable :
+ decltype(check_is_callable(0))
+{};
+
+template
+struct is_callable :
+ decltype(check_is_callable_without_arg(0))
+{};
+
+
+template
+struct is_callable :
+ decltype(check_is_void_callable(0))
+{};
+
+template
+struct is_callable :
+ decltype(check_is_void_callable_without_arg(0))
+{};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief input range type trait
+ *
+ *****************************************************************************/
+template
+constexpr auto
+check_is_input_range(int) -> decltype(
+ begin(std::declval()), end(std::declval()),
+ std::true_type{});
+
+template
+constexpr auto
+check_is_input_range(char) -> decltype(
+ std::begin(std::declval()), std::end(std::declval()),
+ std::true_type{});
+
+template
+constexpr auto
+check_is_input_range(long) -> std::false_type;
+
+template
+struct is_input_range :
+ decltype(check_is_input_range(0))
+{};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief size() member type trait
+ *
+ *****************************************************************************/
+template
+constexpr auto
+check_has_size_getter(int) ->
+ decltype(std::declval().size(), std::true_type{});
+
+template
+constexpr auto
+check_has_size_getter(long) -> std::false_type;
+
+template
+struct has_size_getter :
+ decltype(check_has_size_getter(0))
+{};
+
+} // namespace traits
+
+
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
+ * no interface guarantees; might be changed or removed in the future
+ *
+ *****************************************************************************/
+namespace detail {
+
+
+/*************************************************************************//**
+ * @brief forwards string to first non-whitespace char;
+ * std string -> unsigned conv yields max value, but we want 0;
+ * also checks for nullptr
+ *****************************************************************************/
+inline bool
+fwd_to_unsigned_int(const char*& s)
+{
+ if(!s) return false;
+ for(; std::isspace(*s); ++s);
+ if(!s[0] || s[0] == '-') return false;
+ if(s[0] == '-') return false;
+ return true;
+}
+
+
+/*************************************************************************//**
+ *
+ * @brief value limits clamping
+ *
+ *****************************************************************************/
+template sizeof(T))>
+struct limits_clamped {
+ static T from(const V& v) {
+ if(v >= V(std::numeric_limits::max())) {
+ return std::numeric_limits::max();
+ }
+ if(v <= V(std::numeric_limits::lowest())) {
+ return std::numeric_limits::lowest();
+ }
+ return T(v);
+ }
+};
+
+template
+struct limits_clamped {
+ static T from(const V& v) { return T(v); }
+};
+
+
+/*************************************************************************//**
+ *
+ * @brief returns value of v as a T, clamped at T's maximum
+ *
+ *****************************************************************************/
+template
+inline T clamped_on_limits(const V& v) {
+ return limits_clamped::from(v);
+}
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief type conversion helpers
+ *
+ *****************************************************************************/
+template
+struct make {
+ static inline T from(const char* s) {
+ if(!s) return false;
+ //a conversion from const char* to / must exist
+ return static_cast(s);
+ }
+};
+
+template<>
+struct make {
+ static inline bool from(const char* s) {
+ if(!s) return false;
+ return static_cast(s);
+ }
+};
+
+template<>
+struct make {
+ static inline unsigned char from(const char* s) {
+ if(!fwd_to_unsigned_int(s)) return (0);
+ return clamped_on_limits(std::strtoull(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline unsigned short int from(const char* s) {
+ if(!fwd_to_unsigned_int(s)) return (0);
+ return clamped_on_limits(std::strtoull(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline unsigned int from(const char* s) {
+ if(!fwd_to_unsigned_int(s)) return (0);
+ return clamped_on_limits(std::strtoull(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline unsigned long int from(const char* s) {
+ if(!fwd_to_unsigned_int(s)) return (0);
+ return clamped_on_limits(std::strtoull(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline unsigned long long int from(const char* s) {
+ if(!fwd_to_unsigned_int(s)) return (0);
+ return clamped_on_limits(std::strtoull(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline char from(const char* s) {
+ //parse as single character?
+ const auto n = std::strlen(s);
+ if(n == 1) return s[0];
+ //parse as integer
+ return clamped_on_limits(std::strtoll(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline short int from(const char* s) {
+ return clamped_on_limits(std::strtoll(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline int from(const char* s) {
+ return clamped_on_limits(std::strtoll(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline long int from(const char* s) {
+ return clamped_on_limits(std::strtoll(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline long long int from(const char* s) {
+ return (std::strtoll(s,nullptr,10));
+ }
+};
+
+template<>
+struct make {
+ static inline float from(const char* s) {
+ return (std::strtof(s,nullptr));
+ }
+};
+
+template<>
+struct make {
+ static inline double from(const char* s) {
+ return (std::strtod(s,nullptr));
+ }
+};
+
+template<>
+struct make {
+ static inline long double from(const char* s) {
+ return (std::strtold(s,nullptr));
+ }
+};
+
+template<>
+struct make {
+ static inline std::string from(const char* s) {
+ return std::string(s);
+ }
+};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief assigns boolean constant to one or multiple target objects
+ *
+ *****************************************************************************/
+template
+class assign_value
+{
+public:
+ template
+ explicit constexpr
+ assign_value(T& target, X&& value) noexcept :
+ t_{std::addressof(target)}, v_{std::forward(value)}
+ {}
+
+ void operator () () const {
+ if(t_) *t_ = v_;
+ }
+
+private:
+ T* t_;
+ V v_;
+};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief flips bools
+ *
+ *****************************************************************************/
+class flip_bool
+{
+public:
+ explicit constexpr
+ flip_bool(bool& target) noexcept :
+ b_{&target}
+ {}
+
+ void operator () () const {
+ if(b_) *b_ = !*b_;
+ }
+
+private:
+ bool* b_;
+};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief increments using operator ++
+ *
+ *****************************************************************************/
+template
+class increment
+{
+public:
+ explicit constexpr
+ increment(T& target) noexcept : t_{std::addressof(target)} {}
+
+ void operator () () const {
+ if(t_) ++(*t_);
+ }
+
+private:
+ T* t_;
+};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief decrements using operator --
+ *
+ *****************************************************************************/
+template
+class decrement
+{
+public:
+ explicit constexpr
+ decrement(T& target) noexcept : t_{std::addressof(target)} {}
+
+ void operator () () const {
+ if(t_) --(*t_);
+ }
+
+private:
+ T* t_;
+};
+
+
+
+/*************************************************************************//**
+ *
+ * @brief increments by a fixed amount using operator +=
+ *
+ *****************************************************************************/
+template
+class increment_by
+{
+public:
+ explicit constexpr
+ increment_by(T& target, T by) noexcept :
+ t_{std::addressof(target)}, by_{std::move(by)}
+ {}
+
+ void operator () () const {
+ if(t_) (*t_) += by_;
+ }
+
+private:
+ T* t_;
+ T by_;
+};
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief makes a value from a string and assigns it to an object
+ *
+ *****************************************************************************/
+template
+class map_arg_to
+{
+public:
+ explicit constexpr
+ map_arg_to(T& target) noexcept : t_{std::addressof(target)} {}
+
+ void operator () (const char* s) const {
+ if(t_ && s) *t_ = detail::make::from(s);
+ }
+
+private:
+ T* t_;
+};
+
+
+//-------------------------------------------------------------------
+/**
+ * @brief specialization for vectors: append element
+ */
+template
+class map_arg_to>
+{
+public:
+ map_arg_to(std::vector& target): t_{std::addressof(target)} {}
+
+ void operator () (const char* s) const {
+ if(t_ && s) t_->push_back(detail::make::from(s));
+ }
+
+private:
+ std::vector* t_;
+};
+
+
+//-------------------------------------------------------------------
+/**
+ * @brief specialization for bools:
+ * set to true regardless of string content
+ */
+template<>
+class map_arg_to
+{
+public:
+ map_arg_to(bool& target): t_{&target} {}
+
+ void operator () (const char* s) const {
+ if(t_ && s) *t_ = true;
+ }
+
+private:
+ bool* t_;
+};
+
+
+} // namespace detail
+
+
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief string matching and processing tools
+ *
+ *****************************************************************************/
+
+namespace str {
+
+
+/*************************************************************************//**
+ *
+ * @brief converts string to value of target type 'T'
+ *
+ *****************************************************************************/
+template
+T make(const arg_string& s)
+{
+ return detail::make::from(s);
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief removes trailing whitespace from string
+ *
+ *****************************************************************************/
+template
+inline void
+trimr(std::basic_string& s)
+{
+ if(s.empty()) return;
+
+ s.erase(
+ std::find_if_not(s.rbegin(), s.rend(),
+ [](char c) { return std::isspace(c);} ).base(),
+ s.end() );
+}
+
+
+/*************************************************************************//**
+ *
+ * @brief removes leading whitespace from string
+ *
+ *****************************************************************************/
+template
+inline void
+triml(std::basic_string& s)
+{
+ if(s.empty()) return;
+
+ s.erase(
+ s.begin(),
+ std::find_if_not(s.begin(), s.end(),
+ [](char c) { return std::isspace(c);})
+ );
+}
+
+
+/*************************************************************************//**
+ *
+ * @brief removes leading and trailing whitespace from string
+ *
+ *****************************************************************************/
+template
+inline void
+trim(std::basic_string& s)
+{
+ triml(s);
+ trimr(s);
+}
+
+
+/*************************************************************************//**
+ *
+ * @brief removes all whitespaces from string
+ *
+ *****************************************************************************/
+template
+inline void
+remove_ws(std::basic_string& s)
+{
+ if(s.empty()) return;
+
+ s.erase(std::remove_if(s.begin(), s.end(),
+ [](char c) { return std::isspace(c); }),
+ s.end() );
+}
+
+
+/*************************************************************************//**
+ *
+ * @brief returns true, if the 'prefix' argument
+ * is a prefix of the 'subject' argument
+ *
+ *****************************************************************************/
+template
+inline bool
+has_prefix(const std::basic_string& subject,
+ const std::basic_string& prefix)
+{
+ if(prefix.size() > subject.size()) return false;
+ return subject.find(prefix) == 0;
+}
+
+
+/*************************************************************************//**
+ *
+ * @brief returns true, if the 'postfix' argument
+ * is a postfix of the 'subject' argument
+ *
+ *****************************************************************************/
+template
+inline bool
+has_postfix(const std::basic_string& subject,
+ const std::basic_string& postfix)
+{
+ if(postfix.size() > subject.size()) return false;
+ return (subject.size() - postfix.size()) == subject.find(postfix);
+}
+
+
+
+/*************************************************************************//**
+*
+* @brief returns longest common prefix of several
+* sequential random access containers
+*
+* @details InputRange require begin and end (member functions or overloads)
+* the elements of InputRange require a size() member
+*
+*****************************************************************************/
+template
+auto
+longest_common_prefix(const InputRange& strs)
+ -> typename std::decay::type
+{
+ static_assert(traits::is_input_range(),
+ "parameter must satisfy the InputRange concept");
+
+ static_assert(traits::has_size_getter<
+ typename std::decay::type>(),
+ "elements of input range must have a ::size() member function");
+
+ using std::begin;
+ using std::end;
+
+ using item_t = typename std::decay::type;
+ using str_size_t = typename std::decaysize())>::type;
+
+ const auto n = size_t(distance(begin(strs), end(strs)));
+ if(n < 1) return item_t("");
+ if(n == 1) return *begin(strs);
+
+ //length of shortest string
+ auto m = std::min_element(begin(strs), end(strs),
+ [](const item_t& a, const item_t& b) {
+ return a.size() < b.size(); })->size();
+
+ //check each character until we find a mismatch
+ for(str_size_t i = 0; i < m; ++i) {
+ for(str_size_t j = 1; j < n; ++j) {
+ if(strs[j][i] != strs[j-1][i])
+ return strs[0].substr(0, i);
+ }
+ }
+ return strs[0].substr(0, m);
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief returns longest substring range that could be found in 'arg'
+ *
+ * @param arg string to be searched in
+ * @param substrings range of candidate substrings
+ *
+ *****************************************************************************/
+template
+subrange
+longest_substring_match(const std::basic_string& arg,
+ const InputRange& substrings)
+{
+ using string_t = std::basic_string;
+
+ static_assert(traits::is_input_range(),
+ "parameter must satisfy the InputRange concept");
+
+ static_assert(std::is_same::type>(),
+ "substrings must have same type as 'arg'");
+
+ auto i = string_t::npos;
+ auto n = string_t::size_type(0);
+ for(const auto& s : substrings) {
+ auto j = arg.find(s);
+ if(j != string_t::npos && s.size() > n) {
+ i = j;
+ n = s.size();
+ }
+ }
+ return subrange{i,n};
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief returns longest prefix range that could be found in 'arg'
+ *
+ * @param arg string to be searched in
+ * @param prefixes range of candidate prefix strings
+ *
+ *****************************************************************************/
+template
+subrange
+longest_prefix_match(const std::basic_string& arg,
+ const InputRange& prefixes)
+{
+ using string_t = std::basic_string;
+ using s_size_t = typename string_t::size_type;
+
+ static_assert(traits::is_input_range(),
+ "parameter must satisfy the InputRange concept");
+
+ static_assert(std::is_same::type>(),
+ "prefixes must have same type as 'arg'");
+
+ auto i = string_t::npos;
+ auto n = s_size_t(0);
+ for(const auto& s : prefixes) {
+ auto j = arg.find(s);
+ if(j == 0 && s.size() > n) {
+ i = 0;
+ n = s.size();
+ }
+ }
+ return subrange{i,n};
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief returns the first occurrence of 'query' within 'subject'
+ *
+ *****************************************************************************/
+template
+inline subrange
+substring_match(const std::basic_string& subject,
+ const std::basic_string& query)
+{
+ if(subject.empty() && query.empty()) return subrange(0,0);
+ if(subject.empty() || query.empty()) return subrange{};
+ auto i = subject.find(query);
+ if(i == std::basic_string::npos) return subrange{};
+ return subrange{i,query.size()};
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief returns first substring match (pos,len) within the input string
+ * that represents a number
+ * (with at maximum one decimal point and digit separators)
+ *
+ *****************************************************************************/
+template
+subrange
+first_number_match(std::basic_string s,
+ C digitSeparator = C(','),
+ C decimalPoint = C('.'),
+ C exponential = C('e'))
+{
+ using string_t = std::basic_string;
+ str::trim(s);
+ if(s.empty()) return subrange{};
+
+ //auto i = s.find_first_of("0123456789+-");
+ //if(i == string_t::npos) {
+ // i = s.find(decimalPoint);
+ // if(i == string_t::npos) return subrange{};
+ //}
+ //bool point = false;
+
+ // overwritten to match numbers without leading 0,
+ // also commented out call to sanitize_args in parse
+ auto i = s.find_first_of("0123456789+-.");
+ if (i == string_t::npos) return subrange{};
+ bool point = s[i] == decimalPoint;
+
+ bool sep = false;
+ auto exp = string_t::npos;
+ auto j = i + 1;
+ for(; j < s.size(); ++j) {
+ if(s[j] == digitSeparator) {
+ if(!sep) sep = true; else break;
+ }
+ else {
+ sep = false;
+ if(s[j] == decimalPoint) {
+ //only one decimal point before exponent allowed
+ if(!point && exp == string_t::npos) point = true; else break;
+ }
+ else if(std::tolower(s[j]) == std::tolower(exponential)) {
+ //only one exponent separator allowed
+ if(exp == string_t::npos) exp = j; else break;
+ }
+ else if(exp != string_t::npos && (exp+1) == j) {
+ //only sign or digit after exponent separator
+ if(s[j] != '+' && s[j] != '-' && !std::isdigit(s[j])) break;
+ }
+ else if(!std::isdigit(s[j])) {
+ break;
+ }
+ }
+ }
+
+ //if length == 1 then must be a digit
+ if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
+
+ return subrange{i,j-i};
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief returns first substring match (pos,len)
+ * that represents an integer (with optional digit separators)
+ *
+ *****************************************************************************/
+template
+subrange
+first_integer_match(std::basic_string s,
+ C digitSeparator = C(','))
+{
+ using string_t = std::basic_string;
+ str::trim(s);
+ if(s.empty()) return subrange{};
+
+ auto i = s.find_first_of("0123456789+-");
+ if(i == string_t::npos) return subrange{};
+
+ bool sep = false;
+ auto j = i + 1;
+ for(; j < s.size(); ++j) {
+ if(s[j] == digitSeparator) {
+ if(!sep) sep = true; else break;
+ }
+ else {
+ sep = false;
+ if(!std::isdigit(s[j])) break;
+ }
+ }
+
+ //if length == 1 then must be a digit
+ if(j-i == 1 && !std::isdigit(s[i])) return subrange{};
+
+ return subrange{i,j-i};
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief returns true if candidate string represents a number
+ *
+ *****************************************************************************/
+template
+bool represents_number(const std::basic_string& candidate,
+ C digitSeparator = C(','),
+ C decimalPoint = C('.'),
+ C exponential = C('e'))
+{
+ const auto match = str::first_number_match(candidate, digitSeparator,
+ decimalPoint, exponential);
+
+ return (match && match.length() == candidate.size());
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief returns true if candidate string represents an integer
+ *
+ *****************************************************************************/
+template
+bool represents_integer(const std::basic_string& candidate,
+ C digitSeparator = C(','))
+{
+ const auto match = str::first_integer_match(candidate, digitSeparator);
+ return (match && match.length() == candidate.size());
+}
+
+} // namespace str
+
+
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief makes function object with a const char* parameter
+ * that assigns a value to a ref-captured object
+ *
+ *****************************************************************************/
+template
+inline detail::assign_value
+set(T& target, V value) {
+ return detail::assign_value{target, std::move(value)};
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief makes parameter-less function object
+ * that assigns value(s) to a ref-captured object;
+ * value(s) are obtained by converting the const char* argument to
+ * the captured object types;
+ * bools are always set to true if the argument is not nullptr
+ *
+ *****************************************************************************/
+template
+inline detail::map_arg_to
+set(T& target) {
+ return detail::map_arg_to{target};
+}
+
+
+
+/*************************************************************************//**
+ *
+ * @brief makes function object that sets a bool to true
+ *
+ *****************************************************************************/
+inline detail::assign_value
+set(bool& target) {
+ return detail::assign_value{target,true};
+}
+
+/*************************************************************************//**
+ *
+ * @brief makes function object that sets a bool to false
+ *
+ *****************************************************************************/
+inline detail::assign_value
+unset(bool& target) {
+ return detail::assign_value{target,false};
+}
+
+/*************************************************************************//**
+ *
+ * @brief makes function object that flips the value of a ref-captured bool
+ *
+ *****************************************************************************/
+inline detail::flip_bool
+flip(bool& b) {
+ return detail::flip_bool(b);
+}
+
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief makes function object that increments using operator ++
+ *
+ *****************************************************************************/
+template
+inline detail::increment
+increment(T& target) {
+ return detail::increment{target};
+}
+
+/*************************************************************************//**
+ *
+ * @brief makes function object that decrements using operator --
+ *
+ *****************************************************************************/
+template
+inline detail::increment_by
+increment(T& target, T by) {
+ return detail::increment_by{target, std::move(by)};
+}
+
+/*************************************************************************//**
+ *
+ * @brief makes function object that increments by a fixed amount using operator +=
+ *
+ *****************************************************************************/
+template
+inline detail::decrement
+decrement(T& target) {
+ return detail::decrement{target};
+}
+
+
+
+
+
+
+/*************************************************************************//**
+ *
+ * @brief helpers (NOT FOR DIRECT USE IN CLIENT CODE!)
+ *
+ *****************************************************************************/
+namespace detail {
+
+
+/*************************************************************************//**
+ *
+ * @brief mixin that provides action definition and execution
+ *
+ *****************************************************************************/
+template
+class action_provider
+{
+private:
+ //---------------------------------------------------------------
+ using simple_action = std::function;
+ using arg_action = std::function;
+ using index_action = std::function;
+
+ //-----------------------------------------------------
+ class simple_action_adapter {
+ public:
+ simple_action_adapter() = default;
+ simple_action_adapter(const simple_action& a): action_(a) {}
+ simple_action_adapter(simple_action&& a): action_(std::move(a)) {}
+ void operator() (const char*) const { action_(); }
+ void operator() (int) const { action_(); }
+ private:
+ simple_action action_;
+ };
+
+
+public:
+ //---------------------------------------------------------------
+ /** @brief adds an action that has an operator() that is callable
+ * with a 'const char*' argument */
+ Derived&
+ call(arg_action a) {
+ argActions_.push_back(std::move(a));
+ return *static_cast(this);
+ }
+
+ /** @brief adds an action that has an operator()() */
+ Derived&
+ call(simple_action a) {
+ argActions_.push_back(simple_action_adapter(std::move(a)));
+ return *static_cast(this);
+ }
+
+ /** @brief adds an action that has an operator() that is callable
+ * with a 'const char*' argument */
+ Derived& operator () (arg_action a) { return call(std::move(a)); }
+
+ /** @brief adds an action that has an operator()() */
+ Derived& operator () (simple_action a) { return call(std::move(a)); }
+
+
+ //---------------------------------------------------------------
+ /** @brief adds an action that will set the value of 't' from
+ * a 'const char*' arg */
+ template