From 2718800bc688bd8cf518cb075b31c75027c9e517 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Sun, 29 Nov 2020 18:04:40 -0500 Subject: match debug and release lang std --- driver/driver.vcxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/driver.vcxproj b/driver/driver.vcxproj index 7df16a0..f9b7b67 100644 --- a/driver/driver.vcxproj +++ b/driver/driver.vcxproj @@ -101,7 +101,7 @@ /Kernel %(AdditionalOptions) - stdcpplatest + stdcpp17 $(IntDir);%(AdditionalIncludeDirectories);$(SolutionDir)\external;$(MSBuildThisFileDirectory) -- cgit v1.2.3 From 7e1bd8c5e53c4d419b9f43cf22bd92621f040323 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Mon, 30 Nov 2020 23:37:18 -0500 Subject: fix dbgprint warning --- driver/driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver/driver.cpp b/driver/driver.cpp index 778f3be..b158df6 100644 --- a/driver/driver.cpp +++ b/driver/driver.cpp @@ -404,7 +404,7 @@ Error: WdfObjectDelete(controlDevice); } - DebugPrint(("CreateControlDevice failed\n", status)); + DebugPrint(("CreateControlDevice failed with status code 0x%x\n", status)); } -- cgit v1.2.3 From b7bd9f5950d9712217e2dd5b40f2aeb82294e3c7 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Tue, 1 Dec 2020 01:41:22 -0500 Subject: refactor io --- common/common.vcxitems | 1 + common/rawaccel-io-def.h | 12 ++++++++ common/rawaccel-io.hpp | 53 ++++++++++----------------------- driver/driver.cpp | 77 +++++++++++++++++++++--------------------------- 4 files changed, 61 insertions(+), 82 deletions(-) create mode 100644 common/rawaccel-io-def.h diff --git a/common/common.vcxitems b/common/common.vcxitems index 1eabfd7..9c0a208 100644 --- a/common/common.vcxitems +++ b/common/common.vcxitems @@ -23,6 +23,7 @@ + diff --git a/common/rawaccel-io-def.h b/common/rawaccel-io-def.h new file mode 100644 index 0000000..791addb --- /dev/null +++ b/common/rawaccel-io-def.h @@ -0,0 +1,12 @@ +#pragma once + +#ifdef _KERNEL_MODE +#include +#else +#include +#endif + +#define RA_DEV_TYPE 0x8888u + +#define RA_READ CTL_CODE(RA_DEV_TYPE, 0x888, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) +#define RA_WRITE CTL_CODE(RA_DEV_TYPE, 0x889, METHOD_BUFFERED, FILE_ANY_ACCESS) diff --git a/common/rawaccel-io.hpp b/common/rawaccel-io.hpp index e8641d1..4159b60 100644 --- a/common/rawaccel-io.hpp +++ b/common/rawaccel-io.hpp @@ -5,18 +5,16 @@ #define NOMINMAX #include +#include "rawaccel-io-def.h" #include "rawaccel-settings.h" #include "rawaccel-error.hpp" -#define RA_READ CTL_CODE(0x8888, 0x888, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) -#define RA_WRITE CTL_CODE(0x8888, 0x889, METHOD_BUFFERED, FILE_ANY_ACCESS) - #pragma warning(push) #pragma warning(disable:4245) // int -> DWORD conversion while passing CTL_CODE namespace rawaccel { - settings read() { + void io_control(DWORD code, void* in, DWORD in_size, void* out, DWORD out_size) { HANDLE ra_handle = INVALID_HANDLE_VALUE; ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0); @@ -25,18 +23,17 @@ namespace rawaccel { throw install_error(); } - settings args; DWORD dummy; BOOL success = DeviceIoControl( ra_handle, - RA_READ, - NULL, // input buffer - 0, // input buffer size - &args, // output buffer - sizeof(settings), // output buffer size - &dummy, // bytes returned - NULL // overlapped structure + code, + in, + in_size, + out, + out_size, + &dummy, // bytes returned + NULL // overlapped structure ); CloseHandle(ra_handle); @@ -44,38 +41,18 @@ namespace rawaccel { if (!success) { throw std::system_error(GetLastError(), std::system_category(), "DeviceIoControl failed"); } + } + settings read() { + settings args; + io_control(RA_READ, NULL, 0, &args, sizeof(settings)); return args; } void write(const settings& args) { - HANDLE ra_handle = INVALID_HANDLE_VALUE; - - ra_handle = CreateFileW(L"\\\\.\\rawaccel", 0, 0, 0, OPEN_EXISTING, 0, 0); - - if (ra_handle == INVALID_HANDLE_VALUE) { - throw install_error(); - } - - DWORD dummy; - - BOOL success = DeviceIoControl( - ra_handle, - RA_WRITE, - const_cast(&args), // input buffer - sizeof(settings), // input buffer size - NULL, // output buffer - 0, // output buffer size - &dummy, // bytes returned - NULL // overlapped structure - ); - - CloseHandle(ra_handle); - - if (!success) { - throw std::system_error(GetLastError(), std::system_category(), "DeviceIoControl failed"); - } + auto in_ptr = const_cast(&args); + io_control(RA_WRITE, in_ptr, sizeof(settings), NULL, 0); } } diff --git a/driver/driver.cpp b/driver/driver.cpp index b158df6..a4de824 100644 --- a/driver/driver.cpp +++ b/driver/driver.cpp @@ -1,10 +1,8 @@ #include +#include #include "driver.h" -#define RA_READ CTL_CODE(0x8888, 0x888, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) -#define RA_WRITE CTL_CODE(0x8888, 0x889, METHOD_BUFFERED, FILE_ANY_ACCESS) - #ifdef ALLOC_PRAGMA #pragma alloc_text (INIT, DriverEntry) #pragma alloc_text (PAGE, EvtDeviceAdd) @@ -144,70 +142,61 @@ Return Value: { NTSTATUS status; void* buffer; - size_t size; UNREFERENCED_PARAMETER(Queue); - - + UNREFERENCED_PARAMETER(OutputBufferLength); + UNREFERENCED_PARAMETER(InputBufferLength); PAGED_CODE(); DebugPrint(("Ioctl received into filter control object.\n")); - if (IoControlCode == RA_WRITE && InputBufferLength == sizeof(ra::settings)) { - LARGE_INTEGER interval; - interval.QuadPart = static_cast(ra::WRITE_DELAY) * -10000; - KeDelayExecutionThread(KernelMode, FALSE, &interval); - - status = WdfRequestRetrieveInputBuffer( + switch (IoControlCode) { + case RA_READ: + status = WdfRequestRetrieveOutputBuffer( Request, sizeof(ra::settings), &buffer, - &size + NULL ); - if (!NT_SUCCESS(status)) { - DebugPrint(("RetrieveInputBuffer failed: 0x%x\n", status)); - // status maps to win32 error code 1359: ERROR_INTERNAL_ERROR - WdfRequestComplete(Request, STATUS_MESSAGE_LOST); - return; + DebugPrint(("RetrieveOutputBuffer failed: 0x%x\n", status)); } - - ra::settings new_settings = *reinterpret_cast(buffer); - - if (new_settings.time_min <= 0 || _isnanf(static_cast(new_settings.time_min))) { - new_settings.time_min = ra::settings{}.time_min; + else { + *reinterpret_cast(buffer) = global.args; } - - global.args = new_settings; - global.modifier = { global.args, global.lookups }; - - WdfRequestComplete(Request, STATUS_SUCCESS); - } - else if (IoControlCode == RA_READ && OutputBufferLength == sizeof(ra::settings)) { - status = WdfRequestRetrieveOutputBuffer( + break; + case RA_WRITE: + status = WdfRequestRetrieveInputBuffer( Request, sizeof(ra::settings), &buffer, - &size + NULL ); - if (!NT_SUCCESS(status)) { - DebugPrint(("RetrieveOutputBuffer failed: 0x%x\n", status)); - // status maps to win32 error code 1359: ERROR_INTERNAL_ERROR - WdfRequestComplete(Request, STATUS_MESSAGE_LOST); - return; + DebugPrint(("RetrieveInputBuffer failed: 0x%x\n", status)); } + else { + LARGE_INTEGER interval; + interval.QuadPart = static_cast(ra::WRITE_DELAY) * -10000; + KeDelayExecutionThread(KernelMode, FALSE, &interval); - *reinterpret_cast(buffer) = global.args; + ra::settings new_settings = *reinterpret_cast(buffer); - WdfRequestComplete(Request, STATUS_SUCCESS); - } - else { - DebugPrint(("Received unknown request: in %uB, out %uB\n", InputBufferLength, OutputBufferLength)); - // status maps to win32 error code 1784: ERROR_INVALID_USER_BUFFER - WdfRequestComplete(Request, STATUS_INVALID_BUFFER_SIZE); + if (new_settings.time_min <= 0 || _isnanf(static_cast(new_settings.time_min))) { + new_settings.time_min = ra::settings{}.time_min; + } + + global.args = new_settings; + global.modifier = { global.args, global.lookups }; + } + break; + default: + status = STATUS_INVALID_DEVICE_REQUEST; + break; } + WdfRequestComplete(Request, status); + } #pragma warning(pop) // enable 28118 again -- cgit v1.2.3 From 9bcfbd0687565b58ab2b955a37d9edab76f90a20 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Wed, 2 Dec 2020 05:00:57 -0500 Subject: merge common-install with common --- common-install/common-install.vcxitems | 19 - common-install/external/WinReg.hpp | 2000 -------------------------------- common-install/utility-install.hpp | 46 - common/common.vcxitems | 1 + common/external/WinReg.hpp | 2000 ++++++++++++++++++++++++++++++++ common/utility-install.hpp | 46 + installer/installer.vcxproj | 2 +- rawaccel.sln | 7 +- uninstaller/uninstaller.vcxproj | 2 +- 9 files changed, 2051 insertions(+), 2072 deletions(-) delete mode 100644 common-install/common-install.vcxitems delete mode 100644 common-install/external/WinReg.hpp delete mode 100644 common-install/utility-install.hpp create mode 100644 common/external/WinReg.hpp create mode 100644 common/utility-install.hpp diff --git a/common-install/common-install.vcxitems b/common-install/common-install.vcxitems deleted file mode 100644 index fa4e370..0000000 --- a/common-install/common-install.vcxitems +++ /dev/null @@ -1,19 +0,0 @@ - - - - $(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 deleted file mode 100644 index 9ce2396..0000000 --- a/common-install/external/WinReg.hpp +++ /dev/null @@ -1,2000 +0,0 @@ -#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 deleted file mode 100644 index e1823e4..0000000 --- a/common-install/utility-install.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#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 index 9c0a208..69b4a69 100644 --- a/common/common.vcxitems +++ b/common/common.vcxitems @@ -27,6 +27,7 @@ + diff --git a/common/external/WinReg.hpp b/common/external/WinReg.hpp new file mode 100644 index 0000000..9ce2396 --- /dev/null +++ b/common/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/utility-install.hpp b/common/utility-install.hpp new file mode 100644 index 0000000..e1823e4 --- /dev/null +++ b/common/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/installer/installer.vcxproj b/installer/installer.vcxproj index 8ff13a6..fec4c1e 100644 --- a/installer/installer.vcxproj +++ b/installer/installer.vcxproj @@ -35,7 +35,7 @@ - + diff --git a/rawaccel.sln b/rawaccel.sln index aa226c1..9b3978c 100644 --- a/rawaccel.sln +++ b/rawaccel.sln @@ -9,8 +9,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcx EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "installer", "installer\installer.vcxproj", "{896950D1-520A-420A-B6B1-73014B92A68C}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common-install", "common-install\common-install.vcxitems", "{058D66C6-D88B-4FDB-B0E4-0A6FE7483B95}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "uninstaller", "uninstaller\uninstaller.vcxproj", "{A4097FF6-A6F0-44E8-B8D0-538D0FB75936}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wrapper", "wrapper\wrapper.vcxproj", "{28A3656F-A1DE-405C-B547-191C32EC555F}" @@ -31,13 +29,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "converter", "converter\conv EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution - common-install\common-install.vcxitems*{058d66c6-d88b-4fdb-b0e4-0a6fe7483b95}*SharedItemsImports = 9 common\common.vcxitems*{24b4226f-1461-408f-a1a4-1371c97153ea}*SharedItemsImports = 9 common\common.vcxitems*{28a3656f-a1de-405c-b547-191c32ec555f}*SharedItemsImports = 4 common\common.vcxitems*{4c421992-9a27-4860-a40c-add76fbace55}*SharedItemsImports = 4 common\common.vcxitems*{60d6c942-ac20-4c05-a2be-54b5c966534d}*SharedItemsImports = 4 - common-install\common-install.vcxitems*{896950d1-520a-420a-b6b1-73014b92a68c}*SharedItemsImports = 4 - common-install\common-install.vcxitems*{a4097ff6-a6f0-44e8-b8d0-538d0fb75936}*SharedItemsImports = 4 + common\common.vcxitems*{896950d1-520a-420a-b6b1-73014b92a68c}*SharedItemsImports = 4 + common\common.vcxitems*{a4097ff6-a6f0-44e8-b8d0-538d0fb75936}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 diff --git a/uninstaller/uninstaller.vcxproj b/uninstaller/uninstaller.vcxproj index f7e9f75..f094c52 100644 --- a/uninstaller/uninstaller.vcxproj +++ b/uninstaller/uninstaller.vcxproj @@ -35,7 +35,7 @@ - + -- cgit v1.2.3 From 7d14daf1d5fce4d09471a3abe2aca49cf7680816 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Wed, 2 Dec 2020 05:25:19 -0500 Subject: embed version info into assemblies check app versions against lib, lib against driver add an 'about' dialog which displays version details, accessible from menu refactor error handling + add check for negative offset --- common/common.vcxitems | 1 + common/rawaccel-error.hpp | 6 +- common/rawaccel-io-def.h | 1 + common/rawaccel-io.hpp | 7 + common/rawaccel-version.h | 26 ++++ converter/AssemblyInfo.cpp | 12 ++ converter/converter.cpp | 28 +++- converter/converter.rc | 100 ++++++++++++ converter/converter.vcxproj | 13 ++ converter/converter.vcxproj.filters | 13 ++ converter/resource.h | 14 ++ driver/driver.cpp | 15 ++ driver/driver.rc | 47 ++++++ driver/driver.vcxproj | 9 ++ grapher/AboutBox.Designer.cs | 138 ++++++++++++++++ grapher/AboutBox.cs | 51 ++++++ grapher/AboutBox.resx | 225 +++++++++++++++++++++++++++ grapher/Form1.cs | 21 ++- grapher/Models/AccelGUI.cs | 2 +- grapher/Models/Serialized/SettingsManager.cs | 29 ---- grapher/Properties/AssemblyInfo.cs | 10 +- grapher/grapher.csproj | 9 ++ installer/installer.rc | 100 ++++++++++++ installer/installer.vcxproj | 12 ++ installer/resource.h | 14 ++ uninstaller/resource.h | 14 ++ uninstaller/uninstaller.rc | 97 ++++++++++++ uninstaller/uninstaller.vcxproj | 12 ++ wrapper/AssemblyInfo.cpp | 12 ++ wrapper/resource.h | 14 ++ wrapper/wrapper.cpp | 107 ++++++++++++- wrapper/wrapper.rc | 100 ++++++++++++ wrapper/wrapper.vcxproj | 19 ++- wrapper/wrapper.vcxproj.filters | 11 ++ wrapper/wrapper_io.cpp | 34 ++-- wrapper/wrapper_io.hpp | 8 +- writer/Program.cs | 25 ++- writer/Properties/AssemblyInfo.cs | 10 +- 38 files changed, 1273 insertions(+), 93 deletions(-) create mode 100644 common/rawaccel-version.h create mode 100644 converter/AssemblyInfo.cpp create mode 100644 converter/converter.rc create mode 100644 converter/resource.h create mode 100644 driver/driver.rc create mode 100644 grapher/AboutBox.Designer.cs create mode 100644 grapher/AboutBox.cs create mode 100644 grapher/AboutBox.resx create mode 100644 installer/installer.rc create mode 100644 installer/resource.h create mode 100644 uninstaller/resource.h create mode 100644 uninstaller/uninstaller.rc create mode 100644 wrapper/AssemblyInfo.cpp create mode 100644 wrapper/resource.h create mode 100644 wrapper/wrapper.rc diff --git a/common/common.vcxitems b/common/common.vcxitems index 69b4a69..2b03405 100644 --- a/common/common.vcxitems +++ b/common/common.vcxitems @@ -26,6 +26,7 @@ + diff --git a/common/rawaccel-error.hpp b/common/rawaccel-error.hpp index ecee526..cdbe1e5 100644 --- a/common/rawaccel-error.hpp +++ b/common/rawaccel-error.hpp @@ -8,17 +8,13 @@ namespace rawaccel { using std::runtime_error::runtime_error; }; - class invalid_argument : public error { - using error::error; - }; - class io_error : public error { using error::error; }; class install_error : public io_error { public: - install_error() : io_error("rawaccel is not installed") {} + install_error() : io_error("Raw Accel driver is not installed, run installer.exe") {} }; } diff --git a/common/rawaccel-io-def.h b/common/rawaccel-io-def.h index 791addb..d8d4088 100644 --- a/common/rawaccel-io-def.h +++ b/common/rawaccel-io-def.h @@ -10,3 +10,4 @@ #define RA_READ CTL_CODE(RA_DEV_TYPE, 0x888, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #define RA_WRITE CTL_CODE(RA_DEV_TYPE, 0x889, METHOD_BUFFERED, FILE_ANY_ACCESS) +#define RA_GET_VERSION CTL_CODE(RA_DEV_TYPE, 0x88a, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) diff --git a/common/rawaccel-io.hpp b/common/rawaccel-io.hpp index 4159b60..703ea92 100644 --- a/common/rawaccel-io.hpp +++ b/common/rawaccel-io.hpp @@ -7,6 +7,7 @@ #include "rawaccel-io-def.h" #include "rawaccel-settings.h" +#include "rawaccel-version.h" #include "rawaccel-error.hpp" #pragma warning(push) @@ -55,6 +56,12 @@ namespace rawaccel { io_control(RA_WRITE, in_ptr, sizeof(settings), NULL, 0); } + version_t get_version() { + version_t ver; + io_control(RA_GET_VERSION, NULL, 0, &ver, sizeof(version_t)); + return ver; + } + } #pragma warning(pop) diff --git a/common/rawaccel-version.h b/common/rawaccel-version.h new file mode 100644 index 0000000..c9828a0 --- /dev/null +++ b/common/rawaccel-version.h @@ -0,0 +1,26 @@ +#pragma once + +#define RA_VER_MAJOR 1 +#define RA_VER_MINOR 3 +#define RA_VER_PATCH 0 + +#define RA_MIN_OS "Win10" + +#define M_STR_HELPER(x) #x +#define M_STR(x) M_STR_HELPER(x) + +#define RA_VER_STRING M_STR(RA_VER_MAJOR) "." M_STR(RA_VER_MINOR) "." M_STR(RA_VER_PATCH) + +namespace rawaccel { + + struct version_t { + int major; + int minor; + int patch; + }; + +#ifndef _KERNEL_MODE + inline constexpr version_t min_driver_version = { 1, 3, 0 }; +#endif + +} diff --git a/converter/AssemblyInfo.cpp b/converter/AssemblyInfo.cpp new file mode 100644 index 0000000..cbe3aec --- /dev/null +++ b/converter/AssemblyInfo.cpp @@ -0,0 +1,12 @@ +#include + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +[assembly:AssemblyVersion(RA_VER_STRING)] + +[assembly:ComVisible(false)] ; +[assembly:CLSCompliantAttribute(true)] ; diff --git a/converter/converter.cpp b/converter/converter.cpp index af2699b..230e1be 100644 --- a/converter/converter.cpp +++ b/converter/converter.cpp @@ -192,7 +192,7 @@ bool try_convert(const ia_settings_t& ia_settings) { auto errors = DriverInterop::GetSettingsErrors(new_settings); if (!errors->Empty()) { - Console::WriteLine("Bad settings: " + errors->x->ToArray()[0]); + Console::WriteLine("Bad settings: {0}", errors); return false; } @@ -208,8 +208,26 @@ bool try_convert(const ia_settings_t& ia_settings) { return true; } +public ref struct ASSEMBLY { + static initonly Version^ VERSION = ASSEMBLY::typeid->Assembly->GetName()->Version; +}; + int main() { + auto close_prompt = [] { + std::cout << "Press any key to close this window . . ." << std::endl; + _getwch(); + std::exit(0); + }; + + try { + VersionHelper::ValidateAndGetDriverVersion(ASSEMBLY::VERSION); + } + catch (VersionException^ ex) { + Console::WriteLine(ex->Message); + close_prompt(); + } + std::optional opt_path; if (fs::exists(IA_SETTINGS_NAME)) { @@ -235,11 +253,8 @@ int main() if (!try_convert(parse_ia_settings(opt_path.value()))) std::cout << "Unable to convert settings.\n"; } - catch (DriverNotInstalledException^) { - Console::WriteLine("\nDriver is not installed."); - } catch (Exception^ e) { - Console::WriteLine("\nError: " + e->ToString()); + Console::WriteLine("\nError: {0}", e); } catch (const std::exception& e) { std::cout << "Error: " << e.what() << '\n'; @@ -251,6 +266,5 @@ int main() "Then run this program to generate the equivalent Raw Accel settings.\n"; } - std::cout << "Press any key to close this window . . ." << std::endl; - _getwch(); + close_prompt(); } diff --git a/converter/converter.rc b/converter/converter.rc new file mode 100644 index 0000000..94bc8b5 --- /dev/null +++ b/converter/converter.rc @@ -0,0 +1,100 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +PRODUCTVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "InterAccel -> RawAccel settings file converter" + VALUE "FileVersion", RA_VER_STRING + VALUE "OriginalFilename", "converter.exe" + VALUE "ProductName", "Raw Accel" + VALUE "ProductVersion", RA_VER_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/converter/converter.vcxproj b/converter/converter.vcxproj index 2bc5080..049dec7 100644 --- a/converter/converter.vcxproj +++ b/converter/converter.vcxproj @@ -67,6 +67,9 @@ Console DebugFull + + $(SolutionDir)/common; + @@ -89,8 +92,12 @@ copy /Y "$(TargetPath)" "$(SolutionDir)signed\$(TargetFileName)" + + $(SolutionDir)/common; + + @@ -103,6 +110,12 @@ ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + diff --git a/converter/converter.vcxproj.filters b/converter/converter.vcxproj.filters index 954dbfa..f1ad70a 100644 --- a/converter/converter.vcxproj.filters +++ b/converter/converter.vcxproj.filters @@ -18,5 +18,18 @@ Source Files + + Source Files + + + + + Header Files + + + + + Resource Files + \ No newline at end of file diff --git a/converter/resource.h b/converter/resource.h new file mode 100644 index 0000000..a2c7ba8 --- /dev/null +++ b/converter/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by converter.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/driver/driver.cpp b/driver/driver.cpp index a4de824..a99a70b 100644 --- a/driver/driver.cpp +++ b/driver/driver.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "driver.h" @@ -190,6 +191,20 @@ Return Value: global.modifier = { global.args, global.lookups }; } break; + case RA_GET_VERSION: + status = WdfRequestRetrieveOutputBuffer( + Request, + sizeof(ra::version_t), + &buffer, + NULL + ); + if (!NT_SUCCESS(status)) { + DebugPrint(("RetrieveOutputBuffer failed: 0x%x\n", status)); + } + else { + *reinterpret_cast(buffer) = { RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH }; + } + break; default: status = STATUS_INVALID_DEVICE_REQUEST; break; diff --git a/driver/driver.rc b/driver/driver.rc new file mode 100644 index 0000000..e351ac3 --- /dev/null +++ b/driver/driver.rc @@ -0,0 +1,47 @@ +// +// Include the necessary resources +// +#include +#include + +#include + +#ifdef RC_INVOKED + +// +// Set up debug information +// +#if DBG +#define VER_DBG VS_FF_DEBUG +#else +#define VER_DBG 0 +#endif + +// ------- version info ------------------------------------------------------- + +VS_VERSION_INFO VERSIONINFO +FILEVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +PRODUCTVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VER_DBG +FILEOS VOS_NT +FILETYPE VFT_DRV +FILESUBTYPE VFT2_DRV_SYSTEM +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "mouse acceleration filter driver" + VALUE "FileVersion", RA_VER_STRING + VALUE "OriginalFilename", "rawaccel.sys" + VALUE "ProductName", "Raw Accel" + VALUE "ProductVersion", RA_VER_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0409, 1200 + END +END +#endif \ No newline at end of file diff --git a/driver/driver.vcxproj b/driver/driver.vcxproj index f9b7b67..d8cfd40 100644 --- a/driver/driver.vcxproj +++ b/driver/driver.vcxproj @@ -93,6 +93,9 @@ %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfLdr.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfDriverEntry.lib;$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)libcntpr.lib + + $(SolutionDir)/common;$(UM_IncludePath);%(AdditionalIncludeDirectories) + @@ -116,6 +119,9 @@ %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfLdr.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfDriverEntry.lib;$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)libcntpr.lib + + $(SolutionDir)/common;$(UM_IncludePath);%(AdditionalIncludeDirectories) + @@ -130,5 +136,8 @@ + + + \ No newline at end of file diff --git a/grapher/AboutBox.Designer.cs b/grapher/AboutBox.Designer.cs new file mode 100644 index 0000000..d5ab082 --- /dev/null +++ b/grapher/AboutBox.Designer.cs @@ -0,0 +1,138 @@ + +namespace grapher +{ + partial class AboutBox + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutBox)); + this.tableLayoutPanel = new System.Windows.Forms.TableLayoutPanel(); + this.logoPictureBox = new System.Windows.Forms.PictureBox(); + this.labelVersion = new System.Windows.Forms.Label(); + this.okButton = new System.Windows.Forms.Button(); + this.labelDriverVersion = new System.Windows.Forms.Label(); + this.tableLayoutPanel.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).BeginInit(); + this.SuspendLayout(); + // + // tableLayoutPanel + // + this.tableLayoutPanel.ColumnCount = 2; + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F)); + this.tableLayoutPanel.Controls.Add(this.logoPictureBox, 0, 0); + this.tableLayoutPanel.Controls.Add(this.labelVersion, 1, 1); + this.tableLayoutPanel.Controls.Add(this.okButton, 1, 4); + this.tableLayoutPanel.Controls.Add(this.labelDriverVersion, 1, 2); + this.tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.tableLayoutPanel.Location = new System.Drawing.Point(9, 9); + this.tableLayoutPanel.Name = "tableLayoutPanel"; + this.tableLayoutPanel.RowCount = 5; + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F)); + this.tableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20F)); + this.tableLayoutPanel.Size = new System.Drawing.Size(280, 133); + this.tableLayoutPanel.TabIndex = 0; + // + // logoPictureBox + // + this.logoPictureBox.Dock = System.Windows.Forms.DockStyle.Fill; + this.logoPictureBox.Image = ((System.Drawing.Image)(resources.GetObject("logoPictureBox.Image"))); + this.logoPictureBox.Location = new System.Drawing.Point(3, 3); + this.logoPictureBox.Name = "logoPictureBox"; + this.tableLayoutPanel.SetRowSpan(this.logoPictureBox, 5); + this.logoPictureBox.Size = new System.Drawing.Size(134, 127); + this.logoPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; + this.logoPictureBox.TabIndex = 12; + this.logoPictureBox.TabStop = false; + // + // labelVersion + // + this.labelVersion.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelVersion.Location = new System.Drawing.Point(146, 26); + this.labelVersion.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.labelVersion.MaximumSize = new System.Drawing.Size(0, 17); + this.labelVersion.Name = "labelVersion"; + this.labelVersion.Size = new System.Drawing.Size(131, 17); + this.labelVersion.TabIndex = 0; + this.labelVersion.Text = "GUI Version"; + this.labelVersion.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // okButton + // + this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.okButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this.okButton.Location = new System.Drawing.Point(202, 107); + this.okButton.Name = "okButton"; + this.okButton.Size = new System.Drawing.Size(75, 23); + this.okButton.TabIndex = 24; + this.okButton.Text = "&OK"; + // + // labelDriverVersion + // + this.labelDriverVersion.Dock = System.Windows.Forms.DockStyle.Fill; + this.labelDriverVersion.Location = new System.Drawing.Point(146, 52); + this.labelDriverVersion.Margin = new System.Windows.Forms.Padding(6, 0, 3, 0); + this.labelDriverVersion.MaximumSize = new System.Drawing.Size(0, 17); + this.labelDriverVersion.Name = "labelDriverVersion"; + this.labelDriverVersion.Size = new System.Drawing.Size(131, 17); + this.labelDriverVersion.TabIndex = 25; + this.labelDriverVersion.Text = "Driver Version"; + this.labelDriverVersion.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + // + // AboutBox + // + this.AcceptButton = this.okButton; + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(298, 151); + this.Controls.Add(this.tableLayoutPanel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AboutBox"; + this.Padding = new System.Windows.Forms.Padding(9); + this.ShowIcon = false; + this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "AboutBox"; + this.tableLayoutPanel.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.logoPictureBox)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel; + private System.Windows.Forms.PictureBox logoPictureBox; + private System.Windows.Forms.Label labelVersion; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Label labelDriverVersion; + } +} diff --git a/grapher/AboutBox.cs b/grapher/AboutBox.cs new file mode 100644 index 0000000..5547c59 --- /dev/null +++ b/grapher/AboutBox.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace grapher +{ + partial class AboutBox : Form + { + public AboutBox(Version driver) + { + InitializeComponent(); + this.Text = String.Format("About {0}", AssemblyTitle); + this.labelVersion.Text = String.Format("GUI Version {0}", AssemblyVersion); + this.labelDriverVersion.Text = String.Format("Driver Version {0}", driver.ToString()); + } + + #region Assembly Attribute Accessors + + public string AssemblyTitle + { + get + { + object[] attributes = Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyTitleAttribute), false); + if (attributes.Length > 0) + { + AssemblyTitleAttribute titleAttribute = (AssemblyTitleAttribute)attributes[0]; + if (titleAttribute.Title != "") + { + return titleAttribute.Title; + } + } + return System.IO.Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); + } + } + + public string AssemblyVersion + { + get + { + return Assembly.GetExecutingAssembly().GetName().Version.ToString(); + } + } + + #endregion + } +} diff --git a/grapher/AboutBox.resx b/grapher/AboutBox.resx new file mode 100644 index 0000000..e073ca0 --- /dev/null +++ b/grapher/AboutBox.resx @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAFvZJREFUeF7tnQm8VVPfx3k8M+/zJEqpCKWJvA0qyRANPEiDkqkIiYgm0uQ1RmhA + hkemEl5JKIkIoZJ4yRBCiZKEzLP1/L7rrH0697zr3nvuvefce8656/f5/D57n7PXXmuvYa/1X//1X/+9 + VUBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBGYYz58++//z5Dx806viCeJf6XuxyQ71Bl + 91PlJ4PGMFHH3V2wgHyFKno6Nf7LL7+YL7/80nz77bf8tNC1X8U5Yif9/IO7JSCf4N50Ktts3rzZLF26 + 1Dz++OPmrbfeMr/++iuXLHT9HXGQ+A93a0A6oAI9RpwnThPPFpurvP/oLmccSq+6+Lj4OxX922+/mXff + fdfccMMN5oILLjD333+/+fzzz7lkoWBfideJDVwUAaWFCrGJuOU12wLG4LniYMLo99buloxB6ewrPiD+ + xgPoaF599VUzYsQI06lTJ3PRRReZ119/nUsWhBPn6rSDmPHny0uoAAe5wrTd7c8//1yg242g62vEG8TO + +vlnd3tGoDQaiFPFH2OpG/Pmm2+akSNHmlatWpkTTjjBzJkzxz5rBIV9Teyr04w+W95BhTYkVoSxrveH + H36w3e1HH31kuXHjRvPTTz+5EHHQO0wXu+j8ry6qtEPx1xTHk55NVeCZrrjiCtO8eXPTrl07M2XKlOTh + 4WNxqE63c9EEFAUVVjVxRaz4tgCJfNOmTfbNmzdvnnnsscfMSy+9ZD777DMXIg4awx1iR51nRG5Q3P8Q + h4gfxpI0draAjNC6dWvTuHFjM2rUKPPBBx+4q7YhfCaO4l4XTZmgKP+ouPqKU8QJ4gCxqf7P/ZkJmRAb + iv2VqfvEDTqPgyFh7dq1VjK/8sorzbhx48zs2bPNhx/G68NC960XKRyEyLSPyYrzT4r7BPEVm6Dw/fff + m7vuusv2Brvttps544wzzIoVW9qzwm4SL9RpmXoExWFnKsnQ/xtFhOejxL+44LkN5YsG0UIZGi0uFa1Q + BugZEMRuvPFGc/zxx5sBAwaY6dOnm3Xr1rkQMeieN8ThYk0Xbdqg6LdWvJ3EBbHUjJUHmC0ceOCBpnbt + 2uakk04yr7wSbyc8zwZxoE7/5KIpEXTvSuJhONywYYN9Ib766iv+ikNhaGzXi83cbfkBZWhn8UxxoRhv + DChsHn30UdO/f3/bFfft29fcd999di4fQeF/EefotKtYqsIvCoq7pRifOdBbzZo1y/YIO++8s20ISTMH + dAlHu9tThu55mPt5AWgAzz//vLn22mvNJZdcYu69915fb7hY7K3TcptOlwuUqdoi4/H/xbIaw3vvvWfG + jx9v9ttvP7PXXnuZgQMHmqeeesoWWATdwxBxhU7Trt5VvI3FGaKdwpDuPffcY1q2bGkbwllnnVWgkhTu + CbGRu71YKOzh4pZph0Aa9DKXX365Oeyww+yL8OCDDyZrMt8X++g0/7SYytR/K3MoZDbZ3Ap0kUzRjjvu + OFO3bl3TokULK7UnCWjM3+eL3fQzrW+I4muoeOMNgVkNw1WDBg3Mrrvuai699FLz9ddfc4nn+Ekcp9O/ + u9uLhMI2VljkpMtE3vB4b4jAfOedd5qjjjrKHHDAAeaqq64yH3/8sbtq03pV7Oyiyi8of39V5nqLT4pW + mwdWrVplxo4daxo1amTfwmOOOcY89NBDBaaVCr5WHCPu5KJLCxQfCq6HXDJ2qogeAflgn332MTNnziRt + e01H3tL27tYC0P9VFQR5qIuIlnScyBSY4fD/TYnoFRBCmT3RAJhaR1B4cJOILqWKSyK/oMzVU+au1PFT + m2vhu+++M3fffbc55JBDzE477WSHCHoF5vQRFP5HEWm6uYsqLVDU7RTnklgqxqxcudJ0797d1KxZ0x5R + OQOFQVZhmkdDvkakgtfbiylC4W2FI4ckkv+Tof9oGct1vFhMeSjKGShTfxGPExeJtgQ4LFmyxJx66qmm + Vq1atlc4+eSTzXPPPRcvJMIKFP4R+pmWcVPxMGvgWWyL09E88MADtifo2bOn7b4LA2F5q1evXm2HNgS/ + YcOG2Tz07t3bdOnSxaqpmX0geCIMM+yhqEL+QCY6+OCDTdeuXc3w4cPtlBX1dpIWEywR0Vu01F/5JS8o + U03FW8S4dMTbjwS955572l6hffv2Vmj78ce49peCWSGeqNMyzR50/9Yi8opddo6QJKDat5f/6LoZqkaP + Hm2OOOIIK8tUqVIlrUQeofE//PDDVk5JhJ5lnThZpy1cFvIDylRVcZi4JpbV2FRy6tSpVsdPQ9h7773N + xIkTzRdffOFC2AJZrUN/sUR6fsLr3l4iEn+BBQ66Zt5CCh8BFV3GmWeeaZo1a2a23357b6VlijSwU045 + xT4DU8xE6LmxkPqXy1J+QPlCo0fFLI1lM1YhjzzyiJ1O0RB23313K0CuX79lGFb4lBqCwm0rYjcQFzJ0 + bisczSGNC60mS81t2rQp9wovijvssIPtdZCZkJ0i6PlZLq/vspg/UKbairNFKy7raGWCXr16mRo1aphd + dtnFjrlJc/jVIkNDgbFS//1NZBz9MhYytsDFsPLpp59aJRXKKrpfX+FnG+vVq2dljqghKF/fiQN0mn9L + 38pYffFmMS4EoGRBk0dDYPo2dOjQ5Ln1i6I1CtGxs/ieu2QbEqpapnssHyNw+go5F8iwSI8VQXm7R9zW + FlxpoQjQ6LUWs8q8SvmroWe6Wse4Pvm1114zJ554YrxHGDNmTHwJWGFZ9XtUtNMI3nhMys4555yMCG8V + RYYpesJISFZ2XxHruGIrGXTjQDHqcjG6XCnepp8ni3VdsAqFngeB8RI9T7whLF++3PTo0cM2gmnTprl/ + Y6BgWAxiKuYrwHxhhw4d4nKRyucjcS9XZKlDN611EdiIkqH/GWNvF1GKVHO3VQiUvm0IYnxcT5y3o9a9 + 4447zP777+8tsHxkkyZNzBtvvGHzr3JhutTOFVdqsHcKzHmZhmFcgWRMYSbOjYESoIdYJqKxaqW/yl1J + oTS3U9r0UHGgTkZSRtmSTVJ8eRFBFmEZqGwQDru44ioeCrzM3unA9AiBCUkbqx/GUJZQ6WqYniVC97Ki + 92/xX2JGDSCUHGsM55NmLPVYo124cKHVvFWtWtVbOJWFyEQokoDKCFX2qa7oiobC1xDR17OitUX1Jui3 + 7Q3oYoj85ptvtmMrjSJROQMUFvNstnR11fFvLvq0QPHtJ74dSykm3DEDOP300yvlG18Y0Rvcdlusc1R5 + gaGuCFOD7vu7eLBupItH61RgDPjmm2/Myy+/bG6//XY7DcMcbO7cuQWmY0D30RiwDTxMP0u9xMu9igNL + JPscVDzzYIQ+5sW+Qqjs5IXAFgOo3MBgV5wlh+Koogh6iLeKBWoZtSmS+C233GLVptjrM8/2WMNgfjVZ + ZGEjZaWFwjIFjJt5Mc6zWseCjS/jgQV58cUX23JTGWJv0csVa+mhuP5AJYpI4SzMxKcNVA7ywnXXXWe1 + a0OGDLGN4ZNPPnEhYtAtb4oXiLVctF7oeh9xI/fw1iOcohvH0NOX2UA/J02aFJX7t2ITV7zpgSKsJ7KY + gwYu3hjoGZBIL7vsMvu2omPHlDyyvAEKjpCCwqa7fsZ1+TrfXryfMACBlJVCFkd8GQwsmgjG8+fPd6Vp + louZsUlUxHVVmcNcInFgEMq6+eDBg63BBTLDiy++WGA2ofsYIsaLBdS3LNQ8/fTTpmnTpt7MBaZGZKVo + /4bKd6CrssxBiewpIkSusqk6rFmzxkqo6OLR6aOwSRYege6zvcWECRNM9erVvZkKLBkHDbI7/ihbxuSM + 7d4qACWE5Q2re9i9xeeMzN2xBMIABMsYDC0iy1l6BvYU0EB8GQksHXfccUfz/vvv2zJWXfRzVVR+UKIs + zbLm/5gY7/9ZxMEuDzDeY0CJ2ZQvE4Fl4/nnn2/LWeW/xFVLxUAPwMojc/v4GgSLN0888USY22eQrIYi + oKu8EdgrdpFPz8B08hOehYdCj48q0/fgqRDlB5tCWRhBG+YLE1jF7txyL90gVxXlDyV+qPgND4I8gL6A + vQKMU76HLoosB7PjCDs+rGvZo8eaBfoIzMh891RmYhfhGsAcVx3lC1f5cbNXnbuz2Bo+wiHm0lgH+zIQ + ETt+eg3MszHJrlatWoFr/fr1s7qHbt26FbivshPTd6ByZw29fFdylWhz0b756POx5MH696CDDjIdO3a0 + CzusMTBdZM2BDZfMFLCjT+zWGzZsaM3D0AyynyAxg4nknsmTJwfVcQIZKiPtrOqi/HwqKTEcT1ihj8pH + Ii1sFQ/tFY2CfXv0COgE6NpnzJhhzj33XDudYd3Bd28ySYNxb9999y00vcrGyJ5Q9XGsq57MQmmhD5hH + onTzvPlURqrr97zxOHpg3R89ATt7feEK42mnnWZ7kspuLxAR+cjhf1wVZRaqfCt5UHmYNSe+iam+lcgE + vPksMvmuF0V23LA713etMjJBKzjDVVHmoER2E62aD2k/WdJPpQGwMWTZsmUlqkSEQt54jqxFIF/4wlVG + on11DWCxq6bMQYlYGyVMyvbYYw/vAxVHjD9YNyiqsXCNjZlsI8NGgaGGGQJTQ9YbwjLyFiJ4uwbwoaum + zEAJ4CnMVsahhx6acnefSLp8VgSLWhRi+xgrjOgUFixYYC688EK7nYrZA946WGdAxYzOoDTPkG/kZXAN + 4HsdMjMVJGIlYL0y4b6tNFq+tm3bWqcSPvUwFUn3TtzIFsz5qXCfoMfWbLZhM5tYtGiRbYzJYSoTKbdo + OV51VLjtpi7ioXOmwuG4AK8ZOF38WvxcZBMCbk7wKYwnrDMUroVot27rdw8SYLEHFa3vQYoic3i6csar + xP+peCoZLd8zzzxj9wSwSyiVN5upIO7s0DGgb6iswwLlR6/s8E9b2T6oEguYi6cC3UMDoVFYC14Et0Qt + XapET4B/gOSK5eFR/qAkwvawc+fOBa6nQrSDeB5H4XTsscd6w+QzEcRZeXX15d9TqAss39p+AiGKLpYp + 3HnnnWenVbx1qGBR2yJ4YTKOPl732IgBbxo6ft9DFEXGeyqHhR3fdbR7PBN7AXzXUyGbQ6+//nr7jDiN + YqbhC5ePZCsdUF3RDWzjqrwgdIE9+3b6RnfBHgBUiFQMb88LL7xgXaBRGWeffbYdf6lstmRhA4g5F16y + fGNyccR5FJZDvmt43cLMLF3qXQRDbA7JT2kaay6SoRCofte66vZDAUaKW9xaFQGFswXJppHIJp2Ni74H + KI746/Ht7eOtxdsWgl/ytbKQ7dbMIvBtiODpC5NPxJcRUJ0tdFVdOBQO2/yDxDY6xz0axw46Yt3DtwPw + C/iUGN+sCRgOSvP20z3hjyf6nSgDYGXMvoA6derE/0sXkStIl6VknDz5wuQL8cgGVGeTXDWXHYqPaR+r + fQuJ/KabbvImXhwx/2YI4Tyx8plJ4KoevX70X7qJcMR0Eb88hx9+uDdMPpDZk0NPV33pA70BMSMk+hIv + jmj8MFrgPLEBoA1kZTDT1j7Ej/xBIyjtEJbNpPdEplM9sVsorc44o1U/a/2L80ffAxTHZ5991gqVnNMA + GEaYabA5tbRxlpQ0Ahw+MxygNvWFyVXSgzosd9WWPqjycaNqbfxK+6by9rHZFAUQm0loEOwyKmxKmCky + FUWtjMxRv359b5hcJOZzDiNctaUPagB1iBntny/xVHjNNddYnT7zc6RydPlFWf1kkjhdYBGLRlgaZVa2 + EY+l7LNUPbEtr8i9maWC6r5uWRoAZstsYWIamS2KGebMCJ8ojXzXc4nIV0CVP9NVWXqhiPmcnK3EZBVu + KmQnK7uCKuqNL4x9+vSxwxrzZ9/1XCDGoOzgVh2BVq7K0gtFXI0GgBawpA0ALRxuaVA1+65XNG+99VYr + FBZnrZytRLkGVEdzXXWlH4of7yJ2qbGkQiB2aph8ZetmUHqlt99+264m+q5nM4888kgqPhr7S+5GLlWo + 7v9JA2CeWZIegLEfuQHHh77r2UKMTMgbu5t917ORNFyEaaDKn+iqKjNQGtuREEuNJekBMPFGbsi2sd9H + FFKouXPF1WzkQFOVz9bgzH48Uwlso4Tsrp9U3bLSU7A7mC9++q5nG9EJ0FhxdOG7nk1M2AZG17+/q6bM + QglZpxB8KMn3UMlkRxA9Ri559uSrpAis7FfwXc8GYiAbfYtJdTLcVU/mocTsh5n4ZJrvwZLJohHOn33X + spXYNzIMMDPwXa9o8i2m6COWqo+7dCg/d/JKcDQJ4zrO93CJRM+PWRe7dnzXs5l8B5Hpbrb5L2IpG1e/ + QHXBRyXK9/O1SrA9iTNl8j1gIrEiQu2bix5BUA0zbc2mXoAV2ISvirDmW/5fTFei+PPFMNTqnn0PGhFH + hpiYlUZrmA1kzwJrFhW9WMSM6+qrr7Z6fqCyf5B6cFVS/tADWLVTcZIyGz6Ypviu5QKZCvLpGSxsfNfL + g+x5WLx4McVNxQM+ruE39Cwv6CG68EDo9QvT7PE/xp18bdx3PVfI6mVR+cwUWSxDdkIOASpz9mp0d1VQ + sdDz4ODZfhw4Mu5IJvv/Gf+LGyaynQiBTLdwj+u7nm4yXLI4Fbl8AyrruWL6l3fLAj0QX7OyUr5vWxhK + Cnb1lMZwNNuIdzOMR3zX0klU0VgvR1AZ45W7p5h9Xw3TQ/GRRttM8fSRnBnm/1j6JP+fi2SjDMqsTO0r + oLekgSVgs8p2lI4pfeW8wqCHxBG0HaeSu/onn3zSfiE08b9cJU6p2DjD18h910tL7BHZfJMg3fNB7Uli + dVfE2Q897Gwenq4r0TkEY1i6C6wiif8i3OX7rpWUbFTBARYykqt4dPmY8+ziijV3oIdmY4l1VY3zZzJI + Q0BhUVrT8Wwkm0ypsLJYLmNswid5op27Kjfwv2JjV5y5CWXgSHJCV4YE26BBAztm5tM+fQRd9O+YsPuu + F0W2urPBNnKYDVRcONXKn6+FK0N2PxJvPk6KaADYqfkKJFfJx65LMhvAZQ56hKQPaSwSD3DFlj9Q3rAV + sPIAXSUNgH2AvoLJVdKweYuLMxZhmGCDK2rkCCobPtDVSaf59yHoCMogPgfszgQd886DJ8Ib9pB8GSX5 + GgocfBlhnEnjj1ApKj4Ryih+B2xPQGGV1n9QtpKdRJi3R78Z5saOHWt9KkRQ/gHfTWjviqVyQWWAkuha + SoECwSCkTZs2ObsqGJElYha4cGyFPQS+DFwWLXTOtxSniI1cUVRuqCDwHL6OwmHqwzSR7Vi+ws1m0nAx + bcPLSDKUvx9Evo52kli2b/znI1QobCaJfyYOaxZ8EZXXTuCykDe+devWdlxP0NZhCzFV5IsoHcVQ6alA + BcXHp1fYUhSQD1AXs8qWTQ6cML6oXbu23RvAtC8S6PTsfFX9RrG2y1JASaFyxMsIDaHAh6XYnMm6wdFH + H233DqBJLA95gTSocN50DC9YwZw1a5bdxBJBzwmwvWvqshGQDqhAa4ljRPuNgQgsuuCyDnMy/P2xAscb + ydyb2QS9BcYZkIorjFGYKBxH7sW8i/EcQxVkEr66yZ5APYd7ghj0G6eZE8RQ8ZmEynobEadU/xbX29JP + AGMvFjksNuGrEA+gqFXxK8B3CfAbjM8hjtjz03DYfs5GFMzR8CSO08l33nnHqnKVhou5IPQ/Y/sCcaR+ + 4hG1cszbswkqdIaIvcVzxHvFVeKWb9CmAYrvZ/EDcb7I0ms/kQ/tZOabuwFlgypnW7GZKqirjgPFi0S6 + 5tvEaeL0BLJR7k4dqdhxOh8h9td5N7GtznFwYX0dBwQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE + BAQEBAQE5CK22uo/qucXbjOUCEwAAAAASUVORK5CYII= + + + \ No newline at end of file diff --git a/grapher/Form1.cs b/grapher/Form1.cs index d62ed6d..0c12b86 100644 --- a/grapher/Form1.cs +++ b/grapher/Form1.cs @@ -13,6 +13,8 @@ using grapher.Models.Calculations; using grapher.Models.Options; using grapher.Models.Serialized; using grapher.Models; +using System.Reflection; +using System.Diagnostics; namespace grapher { @@ -26,21 +28,30 @@ namespace grapher { InitializeComponent(); - ManagedAccel activeAccel = null; + Version assemVersion = typeof(RawAcceleration).Assembly.GetName().Version; + Version driverVersion = null; try { - activeAccel = ManagedAccel.GetActiveAccel(); + driverVersion = VersionHelper.ValidateAndGetDriverVersion(assemVersion); } - catch (DriverNotInstalledException ex) + catch (VersionException ex) { - MessageBox.Show($"Driver not installed.\n\n {ex.ToString()}"); + MessageBox.Show(ex.Message); throw; } + + ToolStripMenuItem HelpMenuItem = new ToolStripMenuItem("&Help"); + + HelpMenuItem.DropDownItems.AddRange(new ToolStripItem[] { + new ToolStripMenuItem("&About", null, (s, e) => new AboutBox(driverVersion).ShowDialog()) + }); + + menuStrip1.Items.AddRange(new ToolStripItem[] { HelpMenuItem }); AccelGUI = AccelGUIFactory.Construct( this, - activeAccel, + ManagedAccel.GetActiveAccel(), AccelerationChart, AccelerationChartY, VelocityChart, diff --git a/grapher/Models/AccelGUI.cs b/grapher/Models/AccelGUI.cs index c08313b..f9291c1 100644 --- a/grapher/Models/AccelGUI.cs +++ b/grapher/Models/AccelGUI.cs @@ -153,7 +153,7 @@ namespace grapher } else { - throw new Exception($"Bad arguments: \n {SettingsManager.ErrorStringFrom(errors)}"); + throw new Exception($"Bad arguments:\n\n{errors}"); } } diff --git a/grapher/Models/Serialized/SettingsManager.cs b/grapher/Models/Serialized/SettingsManager.cs index f13ba81..f5bb1f1 100644 --- a/grapher/Models/Serialized/SettingsManager.cs +++ b/grapher/Models/Serialized/SettingsManager.cs @@ -47,35 +47,6 @@ namespace grapher.Models.Serialized #endregion Properties #region Methods - - public static string ErrorStringFrom(SettingsErrors errors) - { - StringBuilder builder = new StringBuilder(); - bool yPresent = errors.y?.Count > 0; - - if (yPresent) - { - builder.AppendLine("\nx:"); - } - - foreach (var error in errors.x) - { - builder.AppendLine(error); - } - - if (yPresent) - { - builder.AppendLine("\ny:"); - - foreach (var error in errors.y) - { - builder.AppendLine(error); - } - } - - return builder.ToString(); - } - public SettingsErrors TryUpdateActiveSettings(DriverSettings settings) { var errors = TryUpdateAccel(settings); diff --git a/grapher/Properties/AssemblyInfo.cs b/grapher/Properties/AssemblyInfo.cs index 939f98a..a46d6b3 100644 --- a/grapher/Properties/AssemblyInfo.cs +++ b/grapher/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("grapher")] +[assembly: AssemblyTitle("Raw Accel GUI")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("grapher")] -[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyProduct("Raw Accel")] +[assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion(RawAccelVersion.value)] +[assembly: AssemblyFileVersion(RawAccelVersion.value)] diff --git a/grapher/grapher.csproj b/grapher/grapher.csproj index a3e63ad..f6bdcb9 100644 --- a/grapher/grapher.csproj +++ b/grapher/grapher.csproj @@ -71,6 +71,12 @@ + + Form + + + AboutBox.cs + @@ -122,6 +128,9 @@ + + AboutBox.cs + Form1.cs diff --git a/installer/installer.rc b/installer/installer.rc new file mode 100644 index 0000000..4f56541 --- /dev/null +++ b/installer/installer.rc @@ -0,0 +1,100 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +PRODUCTVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Raw Accel installer (" RA_MIN_OS ")" + VALUE "FileVersion", RA_VER_STRING + VALUE "OriginalFilename", "converter.exe" + VALUE "ProductName", "Raw Accel" + VALUE "ProductVersion", RA_VER_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/installer/installer.vcxproj b/installer/installer.vcxproj index fec4c1e..de4bb0e 100644 --- a/installer/installer.vcxproj +++ b/installer/installer.vcxproj @@ -70,6 +70,9 @@ install.manifest + + $(SolutionDir)/common; + @@ -98,10 +101,19 @@ copy /Y "$(TargetPath)" "$(SolutionDir)signed\$(TargetFileName)" + + $(SolutionDir)/common; + + + + + + + diff --git a/installer/resource.h b/installer/resource.h new file mode 100644 index 0000000..98c3941 --- /dev/null +++ b/installer/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by installer.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/uninstaller/resource.h b/uninstaller/resource.h new file mode 100644 index 0000000..a56edf6 --- /dev/null +++ b/uninstaller/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by uninstaller.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/uninstaller/uninstaller.rc b/uninstaller/uninstaller.rc new file mode 100644 index 0000000..a9e8304 --- /dev/null +++ b/uninstaller/uninstaller.rc @@ -0,0 +1,97 @@ +// Microsoft Visual C++ generated resource script. +// + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE 9, 1 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#include + +VS_VERSION_INFO VERSIONINFO +FILEVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +PRODUCTVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH +FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif +FILEOS 0x40004L +FILETYPE 0x1L +FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Raw Accel uninstaller" + VALUE "FileVersion", RA_VER_STRING + VALUE "OriginalFilename", "uninstaller.exe" + VALUE "ProductName", "Raw Accel" + VALUE "ProductVersion", RA_VER_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/uninstaller/uninstaller.vcxproj b/uninstaller/uninstaller.vcxproj index f094c52..1098ed7 100644 --- a/uninstaller/uninstaller.vcxproj +++ b/uninstaller/uninstaller.vcxproj @@ -63,6 +63,9 @@ true RequireAdministrator + + $(SolutionDir)/common; + @@ -84,10 +87,19 @@ copy /Y "$(TargetPath)" "$(SolutionDir)signed\$(TargetFileName)" + + $(SolutionDir)/common; + + + + + + + diff --git a/wrapper/AssemblyInfo.cpp b/wrapper/AssemblyInfo.cpp new file mode 100644 index 0000000..4854fd4 --- /dev/null +++ b/wrapper/AssemblyInfo.cpp @@ -0,0 +1,12 @@ +#include + +using namespace System; +using namespace System::Reflection; +using namespace System::Runtime::CompilerServices; +using namespace System::Runtime::InteropServices; +using namespace System::Security::Permissions; + +[assembly: AssemblyVersion(RA_VER_STRING)] + +[assembly:ComVisible(false)] ; +[assembly:CLSCompliantAttribute(true)] ; diff --git a/wrapper/resource.h b/wrapper/resource.h new file mode 100644 index 0000000..e41cd50 --- /dev/null +++ b/wrapper/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by wrapper.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index ee88112..acd0dbf 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -3,11 +3,15 @@ #include #include +#include #include "wrapper_io.hpp" using namespace System; using namespace System::Runtime::InteropServices; +using namespace System::Reflection; + +using namespace Windows::Forms; using namespace Newtonsoft::Json; @@ -129,9 +133,11 @@ using error_list_t = Collections::Generic::List; error_list_t^ get_accel_errors(AccelMode mode, AccelArgs^ args) { - accel_mode m = (accel_mode)mode; + accel_mode mode_native = (accel_mode)mode; - auto is_mode = [m](auto... modes) { return ((m == modes) || ...); }; + auto is_mode = [mode_native](auto... modes) { + return ((mode_native == modes) || ...); + }; using am = accel_mode; @@ -142,7 +148,7 @@ error_list_t^ get_accel_errors(AccelMode mode, AccelArgs^ args) else if (args->acceleration == 0 && is_mode(am::naturalgain)) error_list->Add("acceleration must be positive"); else if (args->acceleration < 0) { - bool additive = m < am::power; + bool additive = mode_native < am::power; if (additive) error_list->Add("acceleration can not be negative, use a negative weight to compensate"); else error_list->Add("acceleration can not be negative"); } @@ -161,6 +167,9 @@ error_list_t^ get_accel_errors(AccelMode mode, AccelArgs^ args) if (args->midpoint <= 0) error_list->Add("midpoint must be positive"); + if (args->offset < 0) + error_list->Add("offset can not be negative"); + return error_list; } @@ -172,7 +181,36 @@ public: bool Empty() { - return x->Count == 0 && y->Count == 0; + return (x == nullptr || x->Count == 0) && + (y == nullptr || y->Count == 0); + } + + virtual String^ ToString() override + { + if (x == nullptr) throw; + + Text::StringBuilder^ sb = gcnew Text::StringBuilder(); + + if (y == nullptr) // assume combineMagnitudes + { + for each (String^ str in x) + { + sb->AppendLine(str); + } + } + else + { + for each (String^ str in x) + { + sb->AppendFormat("x: {0}\n", str); + } + for each (String^ str in y) + { + sb->AppendFormat("y: {0}\n", str); + } + } + + return sb->ToString(); } }; @@ -202,8 +240,9 @@ public ref struct DriverInterop errors->x = get_accel_errors(args->modes.x, args->args.x); - if (args->combineMagnitudes) errors->y = gcnew error_list_t(); - else errors->y = get_accel_errors(args->modes.y, args->args.y); + if (!args->combineMagnitudes) { + errors->y = get_accel_errors(args->modes.y, args->args.y); + } return errors; } @@ -274,3 +313,59 @@ public: return active; } }; + +public ref struct RawAccelVersion +{ + literal String^ value = RA_VER_STRING; +}; + +public ref struct VersionException : public Exception +{ +public: + VersionException() {} + VersionException(String^ what) : Exception(what) {} +}; + +Version^ convert(rawaccel::version_t v) +{ + return gcnew Version(v.major, v.minor, v.patch, 0); +} + +public ref struct VersionHelper +{ + + static Version^ ValidateAndGetDriverVersion(Version^ wrapperTarget) + { + Version^ wrapperActual = VersionHelper::typeid->Assembly->GetName()->Version; + + if (wrapperTarget != wrapperActual) { + throw gcnew VersionException("version mismatch, expected wrapper.dll v" + wrapperActual); + } + + version_t drv_ver; + + try { + wrapper_io::getDriverVersion(drv_ver); + } + catch (DriverNotInstalledException^ ex) { + throw gcnew VersionException(ex->Message); + } + catch (DriverIOException^) { + // Assume version ioctl is unimplemented (driver version < v1.3.0) + throw gcnew VersionException("driver version is out of date, run installer.exe to reinstall"); + } + + Version^ drv_ver_managed = convert(drv_ver); + + if (drv_ver_managed < convert(min_driver_version)) { + throw gcnew VersionException("driver version is out of date, run installer.exe to reinstall"); + } + else if (drv_ver_managed > wrapperActual) { + throw gcnew VersionException("newer driver version is installed"); + } + else { + return drv_ver_managed; + } + } + +}; diff --git a/wrapper/wrapper.rc b/wrapper/wrapper.rc new file mode 100644 index 0000000..edb90d2 --- /dev/null +++ b/wrapper/wrapper.rc @@ -0,0 +1,100 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) + +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#include + +VS_VERSION_INFO VERSIONINFO + FILEVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH + PRODUCTVERSION RA_VER_MAJOR, RA_VER_MINOR, RA_VER_PATCH + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "managed interop & I/O" + VALUE "FileVersion", RA_VER_STRING + VALUE "OriginalFilename", "wrapper.dll" + VALUE "ProductName", "Raw Accel" + VALUE "ProductVersion", RA_VER_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/wrapper/wrapper.vcxproj b/wrapper/wrapper.vcxproj index 6c85a57..4f8ed1c 100644 --- a/wrapper/wrapper.vcxproj +++ b/wrapper/wrapper.vcxproj @@ -46,7 +46,12 @@ - + + false + + + false + Level3 @@ -56,6 +61,9 @@ + + $(SolutionDir)/common; + @@ -70,11 +78,16 @@ copy /Y "$(TargetPath)" "$(SolutionDir)signed\$(TargetFileName)" & copy /Y "$(TargetDir)Newtonsoft.Json.dll" "$(SolutionDir)signed\Newtonsoft.Json.dll" + + $(SolutionDir)/common; + + + @@ -82,6 +95,10 @@ copy /Y "$(TargetDir)Newtonsoft.Json.dll" "$(SolutionDir)signed\Newtonsoft.Json. ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + + diff --git a/wrapper/wrapper.vcxproj.filters b/wrapper/wrapper.vcxproj.filters index 88e539f..f265d72 100644 --- a/wrapper/wrapper.vcxproj.filters +++ b/wrapper/wrapper.vcxproj.filters @@ -18,6 +18,9 @@ Header Files + + Header Files + @@ -26,5 +29,13 @@ Source Files + + Source Files + + + + + Resource Files + \ No newline at end of file diff --git a/wrapper/wrapper_io.cpp b/wrapper/wrapper_io.cpp index 0f257bf..4b77174 100644 --- a/wrapper/wrapper_io.cpp +++ b/wrapper/wrapper_io.cpp @@ -3,11 +3,10 @@ #include #include "wrapper_io.hpp" -void wrapper_io::writeToDriver(const settings& args) -{ - try +auto with_managed_ex = [](auto fn) { + try { - write(args); + fn(); } catch (const install_error&) { @@ -17,20 +16,25 @@ void wrapper_io::writeToDriver(const settings& args) { throw gcnew DriverIOException(gcnew String(e.what())); } +}; + +void wrapper_io::writeToDriver(const settings& args) +{ + with_managed_ex([&] { + write(args); + }); } void wrapper_io::readFromDriver(settings& args) { - try - { + with_managed_ex([&] { args = read(); - } - catch (const install_error&) - { - throw gcnew DriverNotInstalledException(); - } - catch (const std::system_error& e) - { - throw gcnew DriverIOException(gcnew String(e.what())); - } + }); +} + +void wrapper_io::getDriverVersion(version_t& ver) +{ + with_managed_ex([&] { + ver = get_version(); + }); } diff --git a/wrapper/wrapper_io.hpp b/wrapper/wrapper_io.hpp index 19f096f..6b94e46 100644 --- a/wrapper/wrapper_io.hpp +++ b/wrapper/wrapper_io.hpp @@ -1,6 +1,8 @@ #pragma once +#include #include +#include using namespace rawaccel; using namespace System; @@ -8,6 +10,7 @@ using namespace System; struct wrapper_io { static void writeToDriver(const settings&); static void readFromDriver(settings&); + static void getDriverVersion(version_t&); }; public ref struct DriverIOException : public IO::IOException { @@ -16,4 +19,7 @@ public: DriverIOException(String^ what) : IO::IOException(what) {} }; -public ref struct DriverNotInstalledException : public DriverIOException {}; +public ref struct DriverNotInstalledException : public DriverIOException { + DriverNotInstalledException() : + DriverIOException(gcnew String(install_error().what())) {} +}; diff --git a/writer/Program.cs b/writer/Program.cs index 2d4128f..ec8706b 100644 --- a/writer/Program.cs +++ b/writer/Program.cs @@ -21,18 +21,21 @@ namespace writer return; } - foreach (var err in errors.x) + Console.Write("Bad settings: \n\n{0}", errors); + } + + static void Main(string[] args) + { + try { - Console.WriteLine(err + (settings.combineMagnitudes ? "" : " (x)")); + VersionHelper.ValidateAndGetDriverVersion(typeof(Program).Assembly.GetName().Version); } - foreach (var err in errors.y) + catch (VersionException e) { - Console.WriteLine(err + " (y)"); + Console.WriteLine(e.Message); + return; } - } - static void Main(string[] args) - { if (args.Length != 1 || args[0].Equals("help")) { Console.WriteLine("USAGE: {0} ", System.AppDomain.CurrentDomain.FriendlyName); @@ -59,15 +62,11 @@ namespace writer } catch (JsonException e) { - Console.WriteLine("Settings invalid:\n{0}", e.Message.ToString()); - } - catch (DriverNotInstalledException) - { - Console.WriteLine("Driver is not installed"); + Console.WriteLine("Settings invalid:\n{0}", e.Message); } catch (Exception e) { - Console.WriteLine("Error: {0}", e.Message.ToString()); + Console.WriteLine("Error: {0}", e); } } } diff --git a/writer/Properties/AssemblyInfo.cs b/writer/Properties/AssemblyInfo.cs index 37e79ad..fc6a1ca 100644 --- a/writer/Properties/AssemblyInfo.cs +++ b/writer/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("writer")] +[assembly: AssemblyTitle("Raw Accel settings writer")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("writer")] -[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyProduct("Raw Accel")] +[assembly: AssemblyCopyright("")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion(RawAccelVersion.value)] +[assembly: AssemblyFileVersion(RawAccelVersion.value)] -- cgit v1.2.3 From 0aca62b28d1b071031460269ccfd7ffdb7b3e6fd Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Wed, 2 Dec 2020 05:47:12 -0500 Subject: support win7 while cross-signing is still available to be dropped by april 2021 --- common/rawaccel-version.h | 2 +- driver/driver.vcxproj | 16 ++++++++++++---- installer/install.manifest | 6 ++++++ installer/installer.cpp | 7 +++---- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/common/rawaccel-version.h b/common/rawaccel-version.h index c9828a0..9470ca0 100644 --- a/common/rawaccel-version.h +++ b/common/rawaccel-version.h @@ -4,7 +4,7 @@ #define RA_VER_MINOR 3 #define RA_VER_PATCH 0 -#define RA_MIN_OS "Win10" +#define RA_MIN_OS "Win7" #define M_STR_HELPER(x) #x #define M_STR(x) M_STR_HELPER(x) diff --git a/driver/driver.vcxproj b/driver/driver.vcxproj index d8cfd40..9034680 100644 --- a/driver/driver.vcxproj +++ b/driver/driver.vcxproj @@ -22,9 +22,9 @@ - Windows10 + Windows7 False - Universal + Desktop KMDF WindowsKernelModeDriver10.0 Driver @@ -32,9 +32,9 @@ Spectre - Windows10 + Windows7 True - Universal + Desktop KMDF WindowsKernelModeDriver10.0 Driver @@ -55,10 +55,12 @@ rawaccel false + http://timestamp.globalsign.com/scripts/timstamp.dll rawaccel false + http://timestamp.globalsign.com/scripts/timstamp.dll @@ -96,6 +98,9 @@ $(SolutionDir)/common;$(UM_IncludePath);%(AdditionalIncludeDirectories) + + sha256 + @@ -119,6 +124,9 @@ %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)wmilib.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfLdr.lib;$(KMDF_LIB_PATH)$(KMDF_VER_PATH)\WdfDriverEntry.lib;$(DDK_LIB_PATH)wdmsec.lib;$(DDK_LIB_PATH)libcntpr.lib + + sha256 + $(SolutionDir)/common;$(UM_IncludePath);%(AdditionalIncludeDirectories) diff --git a/installer/install.manifest b/installer/install.manifest index 0478246..f9b5ea3 100644 --- a/installer/install.manifest +++ b/installer/install.manifest @@ -4,6 +4,12 @@ + + + + + + diff --git a/installer/installer.cpp b/installer/installer.cpp index 06f234e..916209f 100644 --- a/installer/installer.cpp +++ b/installer/installer.cpp @@ -1,9 +1,8 @@ - - #include #include #include + void add_service(const fs::path& target) { SC_HANDLE schSCManager = OpenSCManager( NULL, // local computer @@ -43,8 +42,8 @@ void add_service(const fs::path& target) { int main() { try { - if (!IsWindows10OrGreater()) { - throw std::runtime_error("OS not supported, you need at least Windows 10"); + if (!IsWindows7OrGreater()) { + throw std::runtime_error("OS not supported, you need at least Windows 7"); } fs::path source = fs::path(L"driver") / DRIVER_FILE_NAME; -- cgit v1.2.3 From a5f00995dd48c2a83937e3503257d1b9fba2096f Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Wed, 2 Dec 2020 06:20:43 -0500 Subject: update writer - use messagebox instead of console --- writer/Program.cs | 24 +++++++++++++++--------- writer/writer.csproj | 1 + 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/writer/Program.cs b/writer/Program.cs index ec8706b..37e384c 100644 --- a/writer/Program.cs +++ b/writer/Program.cs @@ -1,15 +1,21 @@ -using Microsoft.CSharp.RuntimeBinder; -using Newtonsoft.Json; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.IO; +using System.Windows.Forms; namespace writer { class Program { + + static void Show(string msg) + { + MessageBox.Show(msg, "Raw Accel writer"); + } + static void Send(JToken settingsToken) { var settings = settingsToken.ToObject(); @@ -21,7 +27,7 @@ namespace writer return; } - Console.Write("Bad settings: \n\n{0}", errors); + Show($"Bad settings:\n\n{errors}"); } static void Main(string[] args) @@ -32,19 +38,19 @@ namespace writer } catch (VersionException e) { - Console.WriteLine(e.Message); + Show(e.Message); return; } - if (args.Length != 1 || args[0].Equals("help")) + if (args.Length != 1) { - Console.WriteLine("USAGE: {0} ", System.AppDomain.CurrentDomain.FriendlyName); + Show($"Usage: {System.AppDomain.CurrentDomain.FriendlyName} "); return; } if (!File.Exists(args[0])) { - Console.WriteLine("Settings file not found at {0}", args[0]); + Show($"Settings file not found at {args[0]}"); return; } @@ -62,11 +68,11 @@ namespace writer } catch (JsonException e) { - Console.WriteLine("Settings invalid:\n{0}", e.Message); + Show($"Settings invalid:\n\n{e.Message}"); } catch (Exception e) { - Console.WriteLine("Error: {0}", e); + Show($"Error:\n\n{e}"); } } } diff --git a/writer/writer.csproj b/writer/writer.csproj index a874b7f..1c4cca5 100644 --- a/writer/writer.csproj +++ b/writer/writer.csproj @@ -44,6 +44,7 @@ + -- cgit v1.2.3 From 5657d5a54c0a8e980c9b0cac39e2d16e452f302e Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Thu, 3 Dec 2020 20:00:31 -0500 Subject: add directional multipliers adds multipliers for movement in negative directions (up & left by default, can be flipped by rot or sens) avoid division by user input in mousewatcher --- common/rawaccel-settings.h | 1 + common/rawaccel.hpp | 13 +++++- grapher/Models/AccelGUI.cs | 3 +- grapher/Models/AccelGUIFactory.cs | 3 +- grapher/Models/Calculations/AccelData.cs | 14 +++---- grapher/Models/Charts/AccelCharts.cs | 4 +- grapher/Models/Charts/ChartState/ChartState.cs | 2 +- grapher/Models/Charts/ChartState/CombinedState.cs | 4 +- .../Models/Charts/ChartState/XYOneGraphState.cs | 4 +- .../Models/Charts/ChartState/XYTwoGraphState.cs | 4 +- grapher/Models/Mouse/MouseWatcher.cs | 46 ++++++++++++++-------- grapher/Models/Serialized/RawAccelSettings.cs | 2 + grapher/Models/Serialized/SettingsManager.cs | 4 +- wrapper/wrapper.cpp | 5 ++- 14 files changed, 71 insertions(+), 38 deletions(-) diff --git a/common/rawaccel-settings.h b/common/rawaccel-settings.h index e9e158c..0f52807 100644 --- a/common/rawaccel-settings.h +++ b/common/rawaccel-settings.h @@ -21,6 +21,7 @@ namespace rawaccel { vec2 modes = { accel_mode::noaccel, accel_mode::noaccel }; vec2 argsv; vec2d sens = { 1, 1 }; + vec2d neg_multipliers = {}; milliseconds time_min = DEFAULT_TIME_MIN; }; diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index b160a42..a7b9144 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -233,6 +233,7 @@ namespace rawaccel { rotator rotate; vec2 accels; vec2d sensitivity = { 1, 1 }; + vec2d negative_multipliers = {}; mouse_modifier(const settings& args, vec2 luts = {}) : combine_magnitudes(args.combine_mags) @@ -245,6 +246,9 @@ namespace rawaccel { if (args.sens.x != 0) sensitivity.x = args.sens.x; if (args.sens.y != 0) sensitivity.y = args.sens.y; + negative_multipliers.x = fabs(args.neg_multipliers.x); + negative_multipliers.y = fabs(args.neg_multipliers.y); + if ((combine_magnitudes && args.modes.x == accel_mode::noaccel) || (args.modes.x == accel_mode::noaccel && args.modes.y == accel_mode::noaccel)) { @@ -285,9 +289,16 @@ namespace rawaccel { } } - inline void apply_sensitivity(vec2d& movement) { + inline void apply_sensitivity(vec2d& movement) { movement.x *= sensitivity.x; movement.y *= sensitivity.y; + + if (negative_multipliers.x > 0 && movement.x < 0) { + movement.x *= negative_multipliers.x; + } + if (negative_multipliers.y > 0 && movement.y < 0) { + movement.y *= negative_multipliers.y; + } } mouse_modifier() = default; diff --git a/grapher/Models/AccelGUI.cs b/grapher/Models/AccelGUI.cs index f9291c1..e1cf462 100644 --- a/grapher/Models/AccelGUI.cs +++ b/grapher/Models/AccelGUI.cs @@ -140,7 +140,8 @@ namespace grapher combineMagnitudes = ApplyOptions.IsWhole, modes = ApplyOptions.GetModes(), args = newArgs, - minimumTime = driverSettings.minimumTime + minimumTime = driverSettings.minimumTime, + negativeMultipliers = driverSettings.negativeMultipliers }; ButtonDelay(WriteButton); diff --git a/grapher/Models/AccelGUIFactory.cs b/grapher/Models/AccelGUIFactory.cs index 3dc2a74..d789593 100644 --- a/grapher/Models/AccelGUIFactory.cs +++ b/grapher/Models/AccelGUIFactory.cs @@ -2,6 +2,7 @@ using grapher.Models.Mouse; using grapher.Models.Options; using grapher.Models.Serialized; +using System; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; @@ -331,7 +332,7 @@ namespace grapher.Models showLastMouseMoveMenuItem, showVelocityGainToolStripMenuItem); - var mouseWatcher = new MouseWatcher(form, mouseLabel, accelCharts, accelCalculator.PollRate); + var mouseWatcher = new MouseWatcher(form, mouseLabel, accelCharts, settings); return new AccelGUI( form, diff --git a/grapher/Models/Calculations/AccelData.cs b/grapher/Models/Calculations/AccelData.cs index 67d4f3f..778c5a2 100644 --- a/grapher/Models/Calculations/AccelData.cs +++ b/grapher/Models/Calculations/AccelData.cs @@ -56,9 +56,9 @@ namespace grapher.Models.Calculations OutVelocityToPoints.Clear(); } - public void CalculateDots(int x, int y, double timeInMs) + public void CalculateDots(double x, double y, double timeInMsRecip) { - var outVelocity = AccelCalculator.Velocity(x, y, timeInMs); + var outVelocity = AccelCalculator.Magnitude(x, y) * timeInMsRecip; (var inCombVel, var combSens, var combGain) = Combined.FindPointValuesFromOut(outVelocity); Estimated.Velocity.Set(inCombVel, outVelocity); @@ -66,10 +66,10 @@ namespace grapher.Models.Calculations Estimated.Gain.Set(inCombVel, combGain); } - public void CalculateDotsXY(int x, int y, double timeInMs) + public void CalculateDotsXY(double x, double y, double timeInMsRecip) { - var outX = Math.Abs(x) / timeInMs; - var outY = Math.Abs(y) / timeInMs; + var outX = Math.Abs(x) * timeInMsRecip; + var outY = Math.Abs(y) * timeInMsRecip; (var inXVelocity, var xSensitivity, var xGain) = X.FindPointValuesFromOut(outX); EstimatedX.Velocity.Set(inXVelocity, outX); @@ -82,10 +82,10 @@ namespace grapher.Models.Calculations EstimatedY.Gain.Set(inYVelocity, yGain); } - public void CalculateDotsCombinedDiffSens(int x, int y, double timeInMs, DriverSettings settings) + public void CalculateDotsCombinedDiffSens(double x, double y, double timeInMsRecip, DriverSettings settings) { (var xStripped, var yStripped) = AccelCalculator.StripSens(x, y, settings.sensitivity.x, settings.sensitivity.y); - var outVelocity = AccelCalculator.Velocity(xStripped, yStripped, timeInMs); + var outVelocity = AccelCalculator.Magnitude(xStripped, yStripped) * timeInMsRecip; if (OutVelocityToPoints.TryGetValue(outVelocity, out var points)) { diff --git a/grapher/Models/Charts/AccelCharts.cs b/grapher/Models/Charts/AccelCharts.cs index 6247811..188db10 100644 --- a/grapher/Models/Charts/AccelCharts.cs +++ b/grapher/Models/Charts/AccelCharts.cs @@ -99,9 +99,9 @@ namespace grapher #region Methods - public void MakeDots(int x, int y, double timeInMs) + public void MakeDots(double x, double y, double timeInMsRecip) { - ChartState.MakeDots(x, y, timeInMs); + ChartState.MakeDots(x, y, timeInMsRecip); } public void DrawLastMovement() diff --git a/grapher/Models/Charts/ChartState/ChartState.cs b/grapher/Models/Charts/ChartState/ChartState.cs index 270e212..c1b8e9b 100644 --- a/grapher/Models/Charts/ChartState/ChartState.cs +++ b/grapher/Models/Charts/ChartState/ChartState.cs @@ -40,7 +40,7 @@ namespace grapher.Models.Charts.ChartState internal bool TwoDotsPerGraph { get; set; } - public abstract void MakeDots(int x, int y, double timeInMs); + public abstract void MakeDots(double x, double y, double timeInMsRecip); public abstract void Bind(); diff --git a/grapher/Models/Charts/ChartState/CombinedState.cs b/grapher/Models/Charts/ChartState/CombinedState.cs index aab8a38..9fcdc11 100644 --- a/grapher/Models/Charts/ChartState/CombinedState.cs +++ b/grapher/Models/Charts/ChartState/CombinedState.cs @@ -30,9 +30,9 @@ namespace grapher.Models.Charts.ChartState GainChart.ClearSecondDots(); } - public override void MakeDots(int x, int y, double timeInMs) + public override void MakeDots(double x, double y, double timeInMsRecip) { - Data.CalculateDots(x, y, timeInMs); + Data.CalculateDots(x, y, timeInMsRecip); } public override void Bind() diff --git a/grapher/Models/Charts/ChartState/XYOneGraphState.cs b/grapher/Models/Charts/ChartState/XYOneGraphState.cs index 92c799f..95c4b20 100644 --- a/grapher/Models/Charts/ChartState/XYOneGraphState.cs +++ b/grapher/Models/Charts/ChartState/XYOneGraphState.cs @@ -28,9 +28,9 @@ namespace grapher.Models.Charts.ChartState GainChart.SetCombined(); } - public override void MakeDots(int x, int y, double timeInMs) + public override void MakeDots(double x, double y, double timeInMsRecip) { - Data.CalculateDotsCombinedDiffSens(x, y, timeInMs, Settings); + Data.CalculateDotsCombinedDiffSens(x, y, timeInMsRecip, Settings); } public override void Bind() diff --git a/grapher/Models/Charts/ChartState/XYTwoGraphState.cs b/grapher/Models/Charts/ChartState/XYTwoGraphState.cs index d107f87..075cc0f 100644 --- a/grapher/Models/Charts/ChartState/XYTwoGraphState.cs +++ b/grapher/Models/Charts/ChartState/XYTwoGraphState.cs @@ -33,9 +33,9 @@ namespace grapher.Models.Charts.ChartState GainChart.ClearSecondDots(); } - public override void MakeDots(int x, int y, double timeInMs) + public override void MakeDots(double x, double y, double timeInMsRecip) { - Data.CalculateDotsXY(x, y, timeInMs); + Data.CalculateDotsXY(x, y, timeInMsRecip); } public override void Bind() diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs index 6209445..66e72bb 100644 --- a/grapher/Models/Mouse/MouseWatcher.cs +++ b/grapher/Models/Mouse/MouseWatcher.cs @@ -1,4 +1,5 @@ -using System; +using grapher.Models.Serialized; +using System; using System.Runtime.InteropServices; using System.Windows.Forms; @@ -677,12 +678,12 @@ namespace grapher.Models.Mouse #region Constructors - public MouseWatcher(Form containingForm, Label display, AccelCharts accelCharts, Field pollRate) + public MouseWatcher(Form containingForm, Label display, AccelCharts accelCharts, SettingsManager setMngr) { ContainingForm = containingForm; Display = display; AccelCharts = accelCharts; - PollRateField = pollRate; + SettingsManager = setMngr; MouseData = new MouseData(); RAWINPUTDEVICE device = new RAWINPUTDEVICE(); @@ -694,7 +695,7 @@ namespace grapher.Models.Mouse RAWINPUTDEVICE[] devices = new RAWINPUTDEVICE[1]; devices[0] = device; RegisterRawInputDevices(devices, 1, Marshal.SizeOf(typeof(RAWINPUTDEVICE))); - PollTime = 1; + PollTimeRecip = 1; PollRate = 1000; } @@ -708,24 +709,18 @@ namespace grapher.Models.Mouse private AccelCharts AccelCharts { get; } - private Field PollRateField { get; set; } + private SettingsManager SettingsManager { get; } private MouseData MouseData { get; } private double PollRate { get; set; } - private double PollTime { get; set; } + private double PollTimeRecip { get; set; } #endregion Properties #region Methods - public void OnMouseMove(int x, int y, double timeInMs) - { - MouseData.Set(x,y); - AccelCharts.MakeDots(x, y, timeInMs); - } - public void UpdateLastMove() { MouseData.Get(out var x, out var y); @@ -740,15 +735,34 @@ namespace grapher.Models.Mouse outSize = GetRawInputData((IntPtr)message.LParam, RawInputCommand.Input, out rawInput, ref size, Marshal.SizeOf(typeof(RAWINPUTHEADER))); - if (PollRateField.Data != PollRate) + if (SettingsManager.PollRateField.Data != PollRate) { - PollRate = PollRateField.Data; - PollTime = 1000 / PollRate; + PollRate = SettingsManager.PollRateField.Data; + PollTimeRecip = Math.Abs(SettingsManager.PollRateField.Data) / 1000; } if (rawInput.Data.Mouse.LastX != 0 || rawInput.Data.Mouse.LastY != 0) { - OnMouseMove(rawInput.Data.Mouse.LastX, rawInput.Data.Mouse.LastY, PollTime); + double x = rawInput.Data.Mouse.LastX; + double y = rawInput.Data.Mouse.LastY; + + // strip negative multipliers, charts calculated from positive input + + Vec2 negMults = SettingsManager.RawAccelSettings + .AccelerationSettings.negativeMultipliers; + + if (negMults.x > 0 && x < 0) + { + x /= negMults.x; + } + + if (negMults.y > 0 && y < 0) + { + y /= negMults.y; + } + + MouseData.Set(rawInput.Data.Mouse.LastX, rawInput.Data.Mouse.LastY); + AccelCharts.MakeDots(x, y, PollTimeRecip); } } diff --git a/grapher/Models/Serialized/RawAccelSettings.cs b/grapher/Models/Serialized/RawAccelSettings.cs index af87a65..67953ac 100644 --- a/grapher/Models/Serialized/RawAccelSettings.cs +++ b/grapher/Models/Serialized/RawAccelSettings.cs @@ -127,6 +127,8 @@ namespace grapher.Models.Serialized return accelSettings.sensitivity.x == 1 && accelSettings.sensitivity.y == 1 && + accelSettings.negativeMultipliers.x <= 0 && + accelSettings.negativeMultipliers.y <= 0 && accelSettings.rotation == 0 && accelSettings.modes.x == AccelMode.noaccel && wholeOrNoY; diff --git a/grapher/Models/Serialized/SettingsManager.cs b/grapher/Models/Serialized/SettingsManager.cs index f5bb1f1..41ebcb5 100644 --- a/grapher/Models/Serialized/SettingsManager.cs +++ b/grapher/Models/Serialized/SettingsManager.cs @@ -34,9 +34,9 @@ namespace grapher.Models.Serialized public RawAccelSettings RawAccelSettings { get; private set; } - private Field DpiField { get; set; } + public Field DpiField { get; private set; } - private Field PollRateField { get; set; } + public Field PollRateField { get; private set; } private ToolStripMenuItem AutoWriteMenuItem { get; set; } diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index acd0dbf..54bfb91 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -71,7 +71,10 @@ public ref struct DriverSettings [JsonProperty("Sensitivity multipliers")] Vec2 sensitivity; - + + [JsonProperty("Negative directional multipliers", Required = Required::Default)] + Vec2 negativeMultipliers; + [JsonProperty(Required = Required::Default)] double minimumTime; -- cgit v1.2.3 From c5c3c2a8a841d664a422380ab31497b3f741f07f Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Thu, 3 Dec 2020 20:01:49 -0500 Subject: filter out abs move raw input --- grapher/Models/Calculations/AccelChartData.cs | 3 +++ grapher/Models/Mouse/MouseWatcher.cs | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/grapher/Models/Calculations/AccelChartData.cs b/grapher/Models/Calculations/AccelChartData.cs index 60d4c89..48374c1 100644 --- a/grapher/Models/Calculations/AccelChartData.cs +++ b/grapher/Models/Calculations/AccelChartData.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace grapher.Models.Calculations @@ -80,6 +81,8 @@ namespace grapher.Models.Calculations public int GetVelocityIndex(double outVelocityValue) { + Debug.Assert(outVelocityValue >= 0); + var log = Math.Log10(outVelocityValue); if (log < -2) { diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs index 66e72bb..68b56e5 100644 --- a/grapher/Models/Mouse/MouseWatcher.cs +++ b/grapher/Models/Mouse/MouseWatcher.cs @@ -741,7 +741,9 @@ namespace grapher.Models.Mouse PollTimeRecip = Math.Abs(SettingsManager.PollRateField.Data) / 1000; } - if (rawInput.Data.Mouse.LastX != 0 || rawInput.Data.Mouse.LastY != 0) + bool relative = !rawInput.Data.Mouse.Flags.HasFlag(RawMouseFlags.MoveAbsolute); + + if (relative && (rawInput.Data.Mouse.LastX != 0 || rawInput.Data.Mouse.LastY != 0)) { double x = rawInput.Data.Mouse.LastX; double y = rawInput.Data.Mouse.LastY; -- cgit v1.2.3 From 95755e723bab4942dffe5e45c9f8007ce0e008c7 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Thu, 3 Dec 2020 20:03:47 -0500 Subject: fix chart range not updating on disable --- grapher/Models/Charts/ChartXY.cs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/grapher/Models/Charts/ChartXY.cs b/grapher/Models/Charts/ChartXY.cs index 98ba059..953065e 100644 --- a/grapher/Models/Charts/ChartXY.cs +++ b/grapher/Models/Charts/ChartXY.cs @@ -1,5 +1,6 @@ using grapher.Models.Mouse; using System.Collections; +using System.Diagnostics; using System.Windows.Forms.DataVisualization.Charting; namespace grapher @@ -29,6 +30,8 @@ namespace grapher #endregion Constructors + private const double VerticalMargin = 0.1; + #region Properties public Chart ChartX { get; } @@ -221,26 +224,20 @@ namespace grapher public void SetMinMax(double min, double max) { - if (min < max) - { - ChartX.ChartAreas[0].AxisY.Minimum = min * 0.95; - ChartX.ChartAreas[0].AxisY.Maximum = max * 1.05; - } + Debug.Assert(min <= max); + ChartX.ChartAreas[0].AxisY.Minimum = min * (1 - VerticalMargin); + ChartX.ChartAreas[0].AxisY.Maximum = max * (1 + VerticalMargin); } public void SetMinMaxXY(double minX, double maxX, double minY, double maxY) { - if (minX < maxX) - { - ChartX.ChartAreas[0].AxisY.Minimum = minX * 0.95; - ChartX.ChartAreas[0].AxisY.Maximum = maxX * 1.05; - } + Debug.Assert(minX <= maxY); + ChartX.ChartAreas[0].AxisY.Minimum = minX * (1 - VerticalMargin); + ChartX.ChartAreas[0].AxisY.Maximum = maxX * (1 + VerticalMargin); - if (minY < maxY) - { - ChartY.ChartAreas[0].AxisY.Minimum = minY * 0.95; - ChartY.ChartAreas[0].AxisY.Maximum = maxY * 1.05; - } + Debug.Assert(minX <= maxY); + ChartX.ChartAreas[0].AxisY.Minimum = minY * (1 - VerticalMargin); + ChartX.ChartAreas[0].AxisY.Maximum = maxY * (1 + VerticalMargin); } public void SetCombined() -- cgit v1.2.3 From c221eb3d62ccbd7f2a5203abd3e0eae8e9bf31e8 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Thu, 3 Dec 2020 22:42:29 -0500 Subject: add changes from review rename some vars prefer exceptions over assert refactor poll rate field usage in MouseWatcher --- common/rawaccel-settings.h | 2 +- common/rawaccel.hpp | 14 ++++++------ grapher/Models/AccelGUI.cs | 2 +- grapher/Models/Calculations/AccelChartData.cs | 6 ++++-- grapher/Models/Charts/ChartXY.cs | 16 ++++++++++---- grapher/Models/Mouse/MouseWatcher.cs | 31 +++++++++++---------------- grapher/Models/Serialized/RawAccelSettings.cs | 4 ++-- wrapper/wrapper.cpp | 2 +- 8 files changed, 40 insertions(+), 37 deletions(-) diff --git a/common/rawaccel-settings.h b/common/rawaccel-settings.h index 0f52807..649c936 100644 --- a/common/rawaccel-settings.h +++ b/common/rawaccel-settings.h @@ -21,7 +21,7 @@ namespace rawaccel { vec2 modes = { accel_mode::noaccel, accel_mode::noaccel }; vec2 argsv; vec2d sens = { 1, 1 }; - vec2d neg_multipliers = {}; + vec2d dir_multipliers = {}; milliseconds time_min = DEFAULT_TIME_MIN; }; diff --git a/common/rawaccel.hpp b/common/rawaccel.hpp index a7b9144..3e049cb 100644 --- a/common/rawaccel.hpp +++ b/common/rawaccel.hpp @@ -233,7 +233,7 @@ namespace rawaccel { rotator rotate; vec2 accels; vec2d sensitivity = { 1, 1 }; - vec2d negative_multipliers = {}; + vec2d directional_multipliers = {}; mouse_modifier(const settings& args, vec2 luts = {}) : combine_magnitudes(args.combine_mags) @@ -246,8 +246,8 @@ namespace rawaccel { if (args.sens.x != 0) sensitivity.x = args.sens.x; if (args.sens.y != 0) sensitivity.y = args.sens.y; - negative_multipliers.x = fabs(args.neg_multipliers.x); - negative_multipliers.y = fabs(args.neg_multipliers.y); + directional_multipliers.x = fabs(args.dir_multipliers.x); + directional_multipliers.y = fabs(args.dir_multipliers.y); if ((combine_magnitudes && args.modes.x == accel_mode::noaccel) || (args.modes.x == accel_mode::noaccel && @@ -293,11 +293,11 @@ namespace rawaccel { movement.x *= sensitivity.x; movement.y *= sensitivity.y; - if (negative_multipliers.x > 0 && movement.x < 0) { - movement.x *= negative_multipliers.x; + if (directional_multipliers.x > 0 && movement.x < 0) { + movement.x *= directional_multipliers.x; } - if (negative_multipliers.y > 0 && movement.y < 0) { - movement.y *= negative_multipliers.y; + if (directional_multipliers.y > 0 && movement.y < 0) { + movement.y *= directional_multipliers.y; } } diff --git a/grapher/Models/AccelGUI.cs b/grapher/Models/AccelGUI.cs index e1cf462..c6a2dcc 100644 --- a/grapher/Models/AccelGUI.cs +++ b/grapher/Models/AccelGUI.cs @@ -141,7 +141,7 @@ namespace grapher modes = ApplyOptions.GetModes(), args = newArgs, minimumTime = driverSettings.minimumTime, - negativeMultipliers = driverSettings.negativeMultipliers + directionalMultipliers = driverSettings.directionalMultipliers }; ButtonDelay(WriteButton); diff --git a/grapher/Models/Calculations/AccelChartData.cs b/grapher/Models/Calculations/AccelChartData.cs index 48374c1..71ee927 100644 --- a/grapher/Models/Calculations/AccelChartData.cs +++ b/grapher/Models/Calculations/AccelChartData.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; namespace grapher.Models.Calculations @@ -81,7 +80,10 @@ namespace grapher.Models.Calculations public int GetVelocityIndex(double outVelocityValue) { - Debug.Assert(outVelocityValue >= 0); + if (outVelocityValue < 0) + { + throw new ArgumentException($"invalid velocity: {outVelocityValue}"); + } var log = Math.Log10(outVelocityValue); if (log < -2) diff --git a/grapher/Models/Charts/ChartXY.cs b/grapher/Models/Charts/ChartXY.cs index 953065e..553ab3e 100644 --- a/grapher/Models/Charts/ChartXY.cs +++ b/grapher/Models/Charts/ChartXY.cs @@ -1,6 +1,6 @@ using grapher.Models.Mouse; +using System; using System.Collections; -using System.Diagnostics; using System.Windows.Forms.DataVisualization.Charting; namespace grapher @@ -222,20 +222,28 @@ namespace grapher ChartX.Series[2].IsVisibleInLegend = true; } + private void VerifyRange(double min, double max) + { + if (min > max) + { + throw new ArgumentException($"invalid chart range: ({min}, {max})"); + } + } + public void SetMinMax(double min, double max) { - Debug.Assert(min <= max); + VerifyRange(min, max); ChartX.ChartAreas[0].AxisY.Minimum = min * (1 - VerticalMargin); ChartX.ChartAreas[0].AxisY.Maximum = max * (1 + VerticalMargin); } public void SetMinMaxXY(double minX, double maxX, double minY, double maxY) { - Debug.Assert(minX <= maxY); + VerifyRange(minX, maxX); ChartX.ChartAreas[0].AxisY.Minimum = minX * (1 - VerticalMargin); ChartX.ChartAreas[0].AxisY.Maximum = maxX * (1 + VerticalMargin); - Debug.Assert(minX <= maxY); + VerifyRange(minY, maxY); ChartX.ChartAreas[0].AxisY.Minimum = minY * (1 - VerticalMargin); ChartX.ChartAreas[0].AxisY.Maximum = maxY * (1 + VerticalMargin); } diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs index 68b56e5..ae48a76 100644 --- a/grapher/Models/Mouse/MouseWatcher.cs +++ b/grapher/Models/Mouse/MouseWatcher.cs @@ -695,8 +695,6 @@ namespace grapher.Models.Mouse RAWINPUTDEVICE[] devices = new RAWINPUTDEVICE[1]; devices[0] = device; RegisterRawInputDevices(devices, 1, Marshal.SizeOf(typeof(RAWINPUTDEVICE))); - PollTimeRecip = 1; - PollRate = 1000; } #endregion Constructors @@ -713,9 +711,10 @@ namespace grapher.Models.Mouse private MouseData MouseData { get; } - private double PollRate { get; set; } - - private double PollTimeRecip { get; set; } + private double PollTimeReciprocal + { + get => Math.Abs(SettingsManager.PollRateField.Data) * 0.001; + } #endregion Properties @@ -735,12 +734,6 @@ namespace grapher.Models.Mouse outSize = GetRawInputData((IntPtr)message.LParam, RawInputCommand.Input, out rawInput, ref size, Marshal.SizeOf(typeof(RAWINPUTHEADER))); - if (SettingsManager.PollRateField.Data != PollRate) - { - PollRate = SettingsManager.PollRateField.Data; - PollTimeRecip = Math.Abs(SettingsManager.PollRateField.Data) / 1000; - } - bool relative = !rawInput.Data.Mouse.Flags.HasFlag(RawMouseFlags.MoveAbsolute); if (relative && (rawInput.Data.Mouse.LastX != 0 || rawInput.Data.Mouse.LastY != 0)) @@ -748,23 +741,23 @@ namespace grapher.Models.Mouse double x = rawInput.Data.Mouse.LastX; double y = rawInput.Data.Mouse.LastY; - // strip negative multipliers, charts calculated from positive input + // strip negative directional multipliers, charts calculated from positive input - Vec2 negMults = SettingsManager.RawAccelSettings - .AccelerationSettings.negativeMultipliers; + Vec2 dirMults = SettingsManager.RawAccelSettings + .AccelerationSettings.directionalMultipliers; - if (negMults.x > 0 && x < 0) + if (dirMults.x > 0 && x < 0) { - x /= negMults.x; + x /= dirMults.x; } - if (negMults.y > 0 && y < 0) + if (dirMults.y > 0 && y < 0) { - y /= negMults.y; + y /= dirMults.y; } MouseData.Set(rawInput.Data.Mouse.LastX, rawInput.Data.Mouse.LastY); - AccelCharts.MakeDots(x, y, PollTimeRecip); + AccelCharts.MakeDots(x, y, PollTimeReciprocal); } } diff --git a/grapher/Models/Serialized/RawAccelSettings.cs b/grapher/Models/Serialized/RawAccelSettings.cs index 67953ac..dcaf864 100644 --- a/grapher/Models/Serialized/RawAccelSettings.cs +++ b/grapher/Models/Serialized/RawAccelSettings.cs @@ -127,8 +127,8 @@ namespace grapher.Models.Serialized return accelSettings.sensitivity.x == 1 && accelSettings.sensitivity.y == 1 && - accelSettings.negativeMultipliers.x <= 0 && - accelSettings.negativeMultipliers.y <= 0 && + accelSettings.directionalMultipliers.x <= 0 && + accelSettings.directionalMultipliers.y <= 0 && accelSettings.rotation == 0 && accelSettings.modes.x == AccelMode.noaccel && wholeOrNoY; diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index 54bfb91..c59dc00 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -73,7 +73,7 @@ public ref struct DriverSettings Vec2 sensitivity; [JsonProperty("Negative directional multipliers", Required = Required::Default)] - Vec2 negativeMultipliers; + Vec2 directionalMultipliers; [JsonProperty(Required = Required::Default)] double minimumTime; -- cgit v1.2.3 From 2c7c24ee1513616dc6260849bf97340d8484b6b4 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Sat, 5 Dec 2020 01:21:03 -0500 Subject: add more changes from review improve version error messages revert poll time changes add range validation after text parse --- grapher/Models/AccelGUIFactory.cs | 4 ++-- grapher/Models/Calculations/AccelData.cs | 14 +++++++------- grapher/Models/Charts/AccelCharts.cs | 4 ++-- grapher/Models/Charts/ChartState/ChartState.cs | 2 +- grapher/Models/Charts/ChartState/CombinedState.cs | 4 ++-- grapher/Models/Charts/ChartState/XYOneGraphState.cs | 4 ++-- grapher/Models/Charts/ChartState/XYTwoGraphState.cs | 4 ++-- grapher/Models/Fields/Field.cs | 20 +++++++++++++------- grapher/Models/Mouse/MouseWatcher.cs | 6 +++--- wrapper/wrapper.cpp | 10 +++++++--- 10 files changed, 41 insertions(+), 31 deletions(-) diff --git a/grapher/Models/AccelGUIFactory.cs b/grapher/Models/AccelGUIFactory.cs index d789593..901a1b5 100644 --- a/grapher/Models/AccelGUIFactory.cs +++ b/grapher/Models/AccelGUIFactory.cs @@ -105,8 +105,8 @@ namespace grapher.Models Label mouseLabel) { var accelCalculator = new AccelCalculator( - new Field(dpiTextBox.TextBox, form, Constants.DefaultDPI), - new Field(pollRateTextBox.TextBox, form, Constants.DefaultPollRate)); + new Field(dpiTextBox.TextBox, form, Constants.DefaultDPI, 1), + new Field(pollRateTextBox.TextBox, form, Constants.DefaultPollRate, 1)); var accelCharts = new AccelCharts( form, diff --git a/grapher/Models/Calculations/AccelData.cs b/grapher/Models/Calculations/AccelData.cs index 778c5a2..d35217f 100644 --- a/grapher/Models/Calculations/AccelData.cs +++ b/grapher/Models/Calculations/AccelData.cs @@ -56,9 +56,9 @@ namespace grapher.Models.Calculations OutVelocityToPoints.Clear(); } - public void CalculateDots(double x, double y, double timeInMsRecip) + public void CalculateDots(double x, double y, double timeInMs) { - var outVelocity = AccelCalculator.Magnitude(x, y) * timeInMsRecip; + var outVelocity = AccelCalculator.Velocity(x, y, timeInMs); (var inCombVel, var combSens, var combGain) = Combined.FindPointValuesFromOut(outVelocity); Estimated.Velocity.Set(inCombVel, outVelocity); @@ -66,10 +66,10 @@ namespace grapher.Models.Calculations Estimated.Gain.Set(inCombVel, combGain); } - public void CalculateDotsXY(double x, double y, double timeInMsRecip) + public void CalculateDotsXY(double x, double y, double timeInMs) { - var outX = Math.Abs(x) * timeInMsRecip; - var outY = Math.Abs(y) * timeInMsRecip; + var outX = Math.Abs(x) / timeInMs; + var outY = Math.Abs(y) / timeInMs; (var inXVelocity, var xSensitivity, var xGain) = X.FindPointValuesFromOut(outX); EstimatedX.Velocity.Set(inXVelocity, outX); @@ -82,10 +82,10 @@ namespace grapher.Models.Calculations EstimatedY.Gain.Set(inYVelocity, yGain); } - public void CalculateDotsCombinedDiffSens(double x, double y, double timeInMsRecip, DriverSettings settings) + public void CalculateDotsCombinedDiffSens(double x, double y, double timeInMs, DriverSettings settings) { (var xStripped, var yStripped) = AccelCalculator.StripSens(x, y, settings.sensitivity.x, settings.sensitivity.y); - var outVelocity = AccelCalculator.Magnitude(xStripped, yStripped) * timeInMsRecip; + var outVelocity = AccelCalculator.Velocity(xStripped, yStripped, timeInMs); if (OutVelocityToPoints.TryGetValue(outVelocity, out var points)) { diff --git a/grapher/Models/Charts/AccelCharts.cs b/grapher/Models/Charts/AccelCharts.cs index 188db10..b7abb35 100644 --- a/grapher/Models/Charts/AccelCharts.cs +++ b/grapher/Models/Charts/AccelCharts.cs @@ -99,9 +99,9 @@ namespace grapher #region Methods - public void MakeDots(double x, double y, double timeInMsRecip) + public void MakeDots(double x, double y, double timeInMs) { - ChartState.MakeDots(x, y, timeInMsRecip); + ChartState.MakeDots(x, y, timeInMs); } public void DrawLastMovement() diff --git a/grapher/Models/Charts/ChartState/ChartState.cs b/grapher/Models/Charts/ChartState/ChartState.cs index c1b8e9b..0bb141e 100644 --- a/grapher/Models/Charts/ChartState/ChartState.cs +++ b/grapher/Models/Charts/ChartState/ChartState.cs @@ -40,7 +40,7 @@ namespace grapher.Models.Charts.ChartState internal bool TwoDotsPerGraph { get; set; } - public abstract void MakeDots(double x, double y, double timeInMsRecip); + public abstract void MakeDots(double x, double y, double timeInMs); public abstract void Bind(); diff --git a/grapher/Models/Charts/ChartState/CombinedState.cs b/grapher/Models/Charts/ChartState/CombinedState.cs index 9fcdc11..9eadb87 100644 --- a/grapher/Models/Charts/ChartState/CombinedState.cs +++ b/grapher/Models/Charts/ChartState/CombinedState.cs @@ -30,9 +30,9 @@ namespace grapher.Models.Charts.ChartState GainChart.ClearSecondDots(); } - public override void MakeDots(double x, double y, double timeInMsRecip) + public override void MakeDots(double x, double y, double timeInMs) { - Data.CalculateDots(x, y, timeInMsRecip); + Data.CalculateDots(x, y, timeInMs); } public override void Bind() diff --git a/grapher/Models/Charts/ChartState/XYOneGraphState.cs b/grapher/Models/Charts/ChartState/XYOneGraphState.cs index 95c4b20..2b3cd9c 100644 --- a/grapher/Models/Charts/ChartState/XYOneGraphState.cs +++ b/grapher/Models/Charts/ChartState/XYOneGraphState.cs @@ -28,9 +28,9 @@ namespace grapher.Models.Charts.ChartState GainChart.SetCombined(); } - public override void MakeDots(double x, double y, double timeInMsRecip) + public override void MakeDots(double x, double y, double timeInMs) { - Data.CalculateDotsCombinedDiffSens(x, y, timeInMsRecip, Settings); + Data.CalculateDotsCombinedDiffSens(x, y, timeInMs, Settings); } public override void Bind() diff --git a/grapher/Models/Charts/ChartState/XYTwoGraphState.cs b/grapher/Models/Charts/ChartState/XYTwoGraphState.cs index 075cc0f..732ea3c 100644 --- a/grapher/Models/Charts/ChartState/XYTwoGraphState.cs +++ b/grapher/Models/Charts/ChartState/XYTwoGraphState.cs @@ -33,9 +33,9 @@ namespace grapher.Models.Charts.ChartState GainChart.ClearSecondDots(); } - public override void MakeDots(double x, double y, double timeInMsRecip) + public override void MakeDots(double x, double y, double timeInMs) { - Data.CalculateDotsXY(x, y, timeInMsRecip); + Data.CalculateDotsXY(x, y, timeInMs); } public override void Bind() diff --git a/grapher/Models/Fields/Field.cs b/grapher/Models/Fields/Field.cs index 541bbe2..345f814 100644 --- a/grapher/Models/Fields/Field.cs +++ b/grapher/Models/Fields/Field.cs @@ -27,12 +27,16 @@ namespace grapher #region Constructors - public Field(TextBox box, Form containingForm, double defaultData) + public Field(TextBox box, Form containingForm, double defaultData, + double minData = double.MinValue, + double maxData = double.MaxValue) { DefaultText = DecimalString(defaultData); Box = box; _data = defaultData; DefaultData = defaultData; + MinData = minData; + MaxData = maxData; State = FieldState.Undefined; ContainingForm = containingForm; FormatString = Constants.DefaultFieldFormatString; @@ -69,7 +73,7 @@ namespace grapher { return DefaultData; } - } + } } public int Top @@ -122,6 +126,10 @@ namespace grapher private double DefaultData { get; set; } + private double MinData { get; } + + private double MaxData { get; } + #endregion Properties #region Methods @@ -268,12 +276,10 @@ namespace grapher private void TextToData() { - try - { - _data = Convert.ToDouble(Box.Text); - } - catch + if (double.TryParse(Box.Text, out double value) && + value <= MaxData && value >= MinData) { + _data = value; } Box.Text = DecimalString(Data); diff --git a/grapher/Models/Mouse/MouseWatcher.cs b/grapher/Models/Mouse/MouseWatcher.cs index ae48a76..cbfc119 100644 --- a/grapher/Models/Mouse/MouseWatcher.cs +++ b/grapher/Models/Mouse/MouseWatcher.cs @@ -711,9 +711,9 @@ namespace grapher.Models.Mouse private MouseData MouseData { get; } - private double PollTimeReciprocal + private double PollTime { - get => Math.Abs(SettingsManager.PollRateField.Data) * 0.001; + get => 1000 / SettingsManager.PollRateField.Data; } #endregion Properties @@ -757,7 +757,7 @@ namespace grapher.Models.Mouse } MouseData.Set(rawInput.Data.Mouse.LastX, rawInput.Data.Mouse.LastY); - AccelCharts.MakeDots(x, y, PollTimeReciprocal); + AccelCharts.MakeDots(x, y, PollTime); } } diff --git a/wrapper/wrapper.cpp b/wrapper/wrapper.cpp index c59dc00..fcbf2e8 100644 --- a/wrapper/wrapper.cpp +++ b/wrapper/wrapper.cpp @@ -355,16 +355,20 @@ public ref struct VersionHelper } catch (DriverIOException^) { // Assume version ioctl is unimplemented (driver version < v1.3.0) - throw gcnew VersionException("driver version is out of date, run installer.exe to reinstall"); + throw gcnew VersionException("driver version is out of date\n\nrun installer.exe to reinstall"); } Version^ drv_ver_managed = convert(drv_ver); if (drv_ver_managed < convert(min_driver_version)) { - throw gcnew VersionException("driver version is out of date, run installer.exe to reinstall"); + throw gcnew VersionException( + String::Format("driver version is out of date (v{0})\n\nrun installer.exe to reinstall", + drv_ver_managed)); } else if (drv_ver_managed > wrapperActual) { - throw gcnew VersionException("newer driver version is installed"); + throw gcnew VersionException( + String::Format("newer driver version is installed (v{0})", + drv_ver_managed)); } else { return drv_ver_managed; -- cgit v1.2.3 From f87fa2c92e246fe0464eacfc2c338e34061e7fd4 Mon Sep 17 00:00:00 2001 From: a1xd <68629610+a1xd@users.noreply.github.com> Date: Sat, 5 Dec 2020 21:26:29 -0500 Subject: update signed, add installers --- .gitignore | 1 + installer/installer.rc | 2 +- installer/installer.vcxproj | 3 ++- signed/driver/rawaccel.sys | Bin 52560 -> 61096 bytes signed/installer.exe | Bin 0 -> 59784 bytes signed/uninstaller.exe | Bin 0 -> 55176 bytes uninstaller/uninstaller.vcxproj | 3 ++- 7 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 signed/installer.exe create mode 100644 signed/uninstaller.exe diff --git a/.gitignore b/.gitignore index 35e04b4..71d9e60 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ bld/ [Ll]og/ signed/* !signed/driver/ +!signed/*install*.exe # Visual Studio 2015/2017 cache/options directory .vs/ diff --git a/installer/installer.rc b/installer/installer.rc index 4f56541..43672d7 100644 --- a/installer/installer.rc +++ b/installer/installer.rc @@ -72,7 +72,7 @@ BEGIN BEGIN VALUE "FileDescription", "Raw Accel installer (" RA_MIN_OS ")" VALUE "FileVersion", RA_VER_STRING - VALUE "OriginalFilename", "converter.exe" + VALUE "OriginalFilename", "installer.exe" VALUE "ProductName", "Raw Accel" VALUE "ProductVersion", RA_VER_STRING END diff --git a/installer/installer.vcxproj b/installer/installer.vcxproj index de4bb0e..fa18344 100644 --- a/installer/installer.vcxproj +++ b/installer/installer.vcxproj @@ -99,7 +99,8 @@ install.manifest - copy /Y "$(TargetPath)" "$(SolutionDir)signed\$(TargetFileName)" + + $(SolutionDir)/common; diff --git a/signed/driver/rawaccel.sys b/signed/driver/rawaccel.sys index a390fc4..3762899 100644 Binary files a/signed/driver/rawaccel.sys and b/signed/driver/rawaccel.sys differ diff --git a/signed/installer.exe b/signed/installer.exe new file mode 100644 index 0000000..38f9201 Binary files /dev/null and b/signed/installer.exe differ diff --git a/signed/uninstaller.exe b/signed/uninstaller.exe new file mode 100644 index 0000000..aa59df9 Binary files /dev/null and b/signed/uninstaller.exe differ diff --git a/uninstaller/uninstaller.vcxproj b/uninstaller/uninstaller.vcxproj index 1098ed7..52e5583 100644 --- a/uninstaller/uninstaller.vcxproj +++ b/uninstaller/uninstaller.vcxproj @@ -85,7 +85,8 @@ RequireAdministrator - copy /Y "$(TargetPath)" "$(SolutionDir)signed\$(TargetFileName)" + + $(SolutionDir)/common; -- cgit v1.2.3