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