Repository: mfakane/rawinput-sharp Branch: master Commit: a6ed7b2ce410 Files: 86 Total size: 131.6 KB Directory structure: gitextract_swu12hhg/ ├── .gitattributes ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── LICENSE.txt ├── README.md ├── RawInput.Sharp/ │ ├── EnvironmentEx.cs │ ├── HidButton.cs │ ├── HidButtonSet.cs │ ├── HidButtonSetState.cs │ ├── HidButtonState.cs │ ├── HidPreparsedByteArrayData.cs │ ├── HidReader.cs │ ├── HidUsageAndPage.cs │ ├── HidValue.cs │ ├── HidValueSet.cs │ ├── HidValueSetState.cs │ ├── HidValueState.cs │ ├── IHidPreparsedData.cs │ ├── MarshalEx.cs │ ├── Native/ │ │ ├── CfgMgr32.cs │ │ ├── ConfigReturnValue.cs │ │ ├── DeviceInstanceHandle.cs │ │ ├── DevicePropertyKey.cs │ │ ├── HidD.cs │ │ ├── HidDeviceHandle.cs │ │ ├── HidP.cs │ │ ├── HidPButtonCaps.cs │ │ ├── HidPCaps.cs │ │ ├── HidPCapsNotRange.cs │ │ ├── HidPCapsRange.cs │ │ ├── HidPReportType.cs │ │ ├── HidPValueCaps.cs │ │ ├── HidPreparsedData.cs │ │ ├── Kernel32.cs │ │ ├── NtStatus.cs │ │ ├── RawHid.cs │ │ ├── RawInputDeviceHandle.cs │ │ ├── RawInputDeviceInfo.cs │ │ ├── RawInputDeviceInfoBehavior.cs │ │ ├── RawInputDeviceListItem.cs │ │ ├── RawInputHandle.cs │ │ ├── RawInputHeader.cs │ │ ├── RawInputHidInfo.cs │ │ ├── RawInputKeyboardInfo.cs │ │ ├── RawInputMouseInfo.cs │ │ ├── RawKeyboard.cs │ │ ├── RawMouse.cs │ │ ├── User32.cs │ │ └── Win32ErrorException.cs │ ├── RawInput.Sharp.csproj │ ├── RawInputData.cs │ ├── RawInputDevice.cs │ ├── RawInputDeviceFlags.cs │ ├── RawInputDeviceRegistration.cs │ ├── RawInputDeviceType.cs │ ├── RawInputDigitizer.cs │ ├── RawInputDigitizerButton.cs │ ├── RawInputDigitizerContact.cs │ ├── RawInputDigitizerContactKind.cs │ ├── RawInputDigitizerData.cs │ ├── RawInputHid.cs │ ├── RawInputHidData.cs │ ├── RawInputKeyboard.cs │ ├── RawInputKeyboardData.cs │ ├── RawInputMouse.cs │ └── RawInputMouseData.cs ├── RawInput.Sharp.DigitizerExample/ │ ├── MainForm.Designer.cs │ ├── MainForm.cs │ ├── MainForm.resx │ ├── Program.cs │ └── RawInput.Sharp.DigitizerExample.csproj ├── RawInput.Sharp.SimpleExample/ │ ├── Program.cs │ ├── RawInput.Sharp.SimpleExample.csproj │ ├── RawInputEventArgs.cs │ ├── RawInputReceiverWindow.cs │ └── app.config ├── RawInput.Sharp.SimpleExample.WPF/ │ ├── App.xaml │ ├── App.xaml.cs │ ├── AssemblyInfo.cs │ ├── MainWindow.xaml │ ├── MainWindow.xaml.cs │ └── RawInput.Sharp.SimpleExample.WPF.csproj ├── RawInput.Sharp.SimpleExample.Win32/ │ ├── Program.cs │ └── RawInput.Sharp.SimpleExample.Win32.csproj ├── RawInput.Sharp.sln └── renovate.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .github/FUNDING.yml ================================================ github: [mfakane] ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json project.fragment.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted #*.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc ================================================ FILE: LICENSE.txt ================================================ The zlib License Copyright (c) 2019 mfakane This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ================================================ FILE: README.md ================================================ # RawInput.Sharp A simple wrapper library for Windows [Raw Input](https://learn.microsoft.com/en-us/windows/win32/inputdev/raw-input). Available on .NET Standard 1.1 and .NET Framework 4.6.1. ## NuGet https://www.nuget.org/packages/RawInput.Sharp/ ## Usage ### Acquiring connected devices ```cs // using Linearstar.Windows.RawInput; // Get the devices that can be handled with Raw Input. var devices = RawInputDevice.GetDevices(); // Keyboards will be returned as RawInputKeyboard. var keyboards = devices.OfType(); // Mice will be RawInputMouse. var mice = devices.OfType(); ``` ### Reading raw inputs ```cs protected override void WndProc(ref Message m) { const int WM_INPUT = 0x00FF; // You can read inputs by processing the WM_INPUT message. if (m.Msg == WM_INPUT) { // Create an RawInputData from the handle stored in lParam. var data = RawInputData.FromHandle(m.LParam); // You can identify the source device using Header.DeviceHandle or just Device. var sourceDeviceHandle = data.Header.DeviceHandle; var sourceDevice = data.Device; // The data will be an instance of either RawInputMouseData, RawInputKeyboardData, or RawInputHidData. // They contain the raw input data in their properties. switch (data) { case RawInputMouseData mouse: Console.WriteLine(mouse.Mouse); break; case RawInputKeyboardData keyboard: Console.WriteLine(keyboard.Keyboard); break; case RawInputHidData hid: Console.WriteLine(hid.Hid); break; } } base.WndProc(ref m); } ``` ### Further examples - [RawInput.Sharp.SimpleExample](RawInput.Sharp.SimpleExample) - A simple example that shows keyboard input events. - [RawInput.Sharp.SimpleExample.WPF](RawInput.Sharp.SimpleExample.WPF) - WPF version of the prior one. - [RawInput.Sharp.DigitizerExample](RawInput.Sharp.DigitizerExample) - An example handling digitizers, such as pen tablets, touch screens, and touch pads. ## License [zlib License](LICENSE.txt) ================================================ FILE: RawInput.Sharp/EnvironmentEx.cs ================================================ using System; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; static class EnvironmentEx { public static bool Is64BitOperatingSystem { get { var isWow64ProcessProc = Kernel32.GetProcAddress(Kernel32.GetModuleHandle("kernel32"), "IsWow64Process"); return isWow64ProcessProc != IntPtr.Zero && Kernel32.IsWow64Process(Kernel32.GetCurrentProcess()); } } public static bool Is64BitProcess => IntPtr.Size == 8; } ================================================ FILE: RawInput.Sharp/HidButton.cs ================================================ using System; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidButton { internal readonly HidReader reader; internal readonly HidPButtonCaps buttonCaps; public int ReportId => buttonCaps.ReportID; public HidUsageAndPage UsageAndPage { get; } public HidUsageAndPage LinkUsageAndPage => new(buttonCaps.LinkUsagePage, buttonCaps.LinkUsage); public int LinkCollection => buttonCaps.LinkCollection; internal HidButton(HidReader reader, HidPButtonCaps buttonCaps, ushort usage) { this.reader = reader; this.buttonCaps = buttonCaps; UsageAndPage = new HidUsageAndPage(buttonCaps.UsagePage, usage); } public HidButtonState GetState(ArraySegment report) => GetState(report.ToArray(), report.Count); public HidButtonState GetState(byte[] report, int reportLength) => new(this, report, reportLength); public override string ToString() => $"{ReportId}, {LinkCollection}, Link: {{{LinkUsageAndPage}}}, Usage: {{{UsageAndPage}}}"; } ================================================ FILE: RawInput.Sharp/HidButtonSet.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidButtonSet : IEnumerable { internal readonly HidReader reader; internal readonly HidPButtonCaps buttonCaps; public int ReportId => buttonCaps.ReportID; public int ButtonCount => buttonCaps.Range.UsageMax - buttonCaps.Range.UsageMin + 1; public ushort UsagePage => buttonCaps.UsagePage; public ushort UsageMin => buttonCaps.Range.UsageMin; public ushort UsageMax => buttonCaps.Range.UsageMax; public HidUsageAndPage LinkUsageAndPage => new(buttonCaps.LinkUsagePage, buttonCaps.LinkUsage); public int LinkCollection => buttonCaps.LinkCollection; internal HidButtonSet(HidReader reader, HidPButtonCaps buttonCaps) { this.reader = reader; this.buttonCaps = buttonCaps; } public HidButtonSetState GetStates(ArraySegment report) => GetStates(report.ToArray(), report.Count); public HidButtonSetState GetStates(byte[] report, int reportLength) => new(this, report, reportLength); public override string ToString() => $"{ReportId}, {LinkCollection}, Link: {{{LinkUsageAndPage}}}, UsagePage: {{{UsagePage}}}, Count: {ButtonCount}"; public IEnumerator GetEnumerator() { for (var usage = buttonCaps.Range.UsageMin; usage <= buttonCaps.Range.UsageMax; usage++) yield return new HidButton(reader, buttonCaps, usage); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } ================================================ FILE: RawInput.Sharp/HidButtonSetState.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidButtonSetState : IEnumerable { readonly byte[] report; readonly int reportLength; public HidButtonSet ButtonSet { get; } public unsafe ushort[] ActiveUsages { get { fixed (void* preparsedData = ButtonSet.reader.PreparsedData) return HidP.GetUsages((IntPtr)preparsedData, HidPReportType.Input, ButtonSet.buttonCaps, report, reportLength); } } internal HidButtonSetState(HidButtonSet buttonSet, byte[] report, int reportLength) { ButtonSet = buttonSet; this.report = report; this.reportLength = reportLength; } public override string ToString() => $"ButtonSet: {{{ButtonSet}}}, Active: [{string.Join(", ", ActiveUsages)}]"; public IEnumerator GetEnumerator() { for (var usage = ButtonSet.UsageMin; usage <= ButtonSet.UsageMax; usage++) yield return new HidButtonState(new HidButton(ButtonSet.reader, ButtonSet.buttonCaps, usage), report, reportLength); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } ================================================ FILE: RawInput.Sharp/HidButtonState.cs ================================================ using System; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidButtonState { readonly byte[] report; readonly int reportLength; public HidButton Button { get; } public unsafe bool IsActive { get { fixed (void* preparsedData = Button.reader.PreparsedData) { var activeUsages = HidP.GetUsages((IntPtr)preparsedData, HidPReportType.Input, Button.buttonCaps, report, reportLength); return Array.IndexOf(activeUsages, Button.UsageAndPage.Usage) != -1; } } } internal HidButtonState(HidButton button, byte[] report, int reportLength) { Button = button; this.report = report; this.reportLength = reportLength; } public override string ToString() => $"Button: {{{Button}}}, IsActive: {IsActive}"; } ================================================ FILE: RawInput.Sharp/HidPreparsedByteArrayData.cs ================================================ namespace Linearstar.Windows.RawInput; public class HidPreparsedByteArrayData : IHidPreparsedData { readonly byte[] preparsedData; public HidPreparsedByteArrayData(byte[] preparsedData) => this.preparsedData = preparsedData; public ref byte GetPinnableReference() => ref preparsedData[0]; } ================================================ FILE: RawInput.Sharp/HidReader.cs ================================================ using System; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidReader { readonly HidPCaps capabilities; public IHidPreparsedData PreparsedData { get; } public int ValueCount => capabilities.NumberInputValueCaps; public HidButtonSet[] ButtonSets { get; } public HidValueSet[] ValueSets { get; } public unsafe HidReader(IHidPreparsedData preparsedData) { fixed (void* preparsedDataPtr = PreparsedData = preparsedData) { capabilities = HidP.GetCaps((IntPtr)preparsedDataPtr); var buttonCaps = HidP.GetButtonCaps((IntPtr)preparsedDataPtr, HidPReportType.Input); ButtonSets = buttonCaps.Select(i => new HidButtonSet(this, i)).ToArray(); var valueCaps = HidP.GetValueCaps((IntPtr)preparsedDataPtr, HidPReportType.Input); ValueSets = valueCaps.Select(i => new HidValueSet(this, i)).ToArray(); } } } ================================================ FILE: RawInput.Sharp/HidUsageAndPage.cs ================================================ using System; namespace Linearstar.Windows.RawInput; public readonly struct HidUsageAndPage : IEquatable { public static readonly HidUsageAndPage Mouse = new(0x01, 0x02); public static readonly HidUsageAndPage Joystick = new(0x01, 0x04); public static readonly HidUsageAndPage GamePad = new(0x01, 0x05); public static readonly HidUsageAndPage Keyboard = new(0x01, 0x06); public static readonly HidUsageAndPage Pen = new(0x0D, 0x02); public static readonly HidUsageAndPage TouchScreen = new(0x0D, 0x04); public static readonly HidUsageAndPage TouchPad = new(0x0D, 0x05); public HidUsageAndPage(ushort usagePage, ushort usage) { UsagePage = usagePage; Usage = usage; } public ushort Usage { get; } public ushort UsagePage { get; } public static bool operator ==(HidUsageAndPage a, HidUsageAndPage b) => a.UsagePage == b.UsagePage && a.Usage == b.Usage; public static bool operator !=(HidUsageAndPage a, HidUsageAndPage b) => a.UsagePage != b.UsagePage || a.Usage != b.Usage; public bool Equals(HidUsageAndPage other) => GetHashCode() == other.GetHashCode(); public override bool Equals(object? obj) => obj is HidUsageAndPage huap ? Equals(huap) : base.Equals(obj); public override int GetHashCode() => UsagePage << 16 | Usage; public override string ToString() => $"{UsagePage:X2}:{Usage:X2}"; } ================================================ FILE: RawInput.Sharp/HidValue.cs ================================================ using System; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidValue { internal readonly HidReader reader; internal readonly HidPValueCaps valueCaps; public int ReportId => valueCaps.ReportID; public int ReportCount => valueCaps.ReportCount; public HidUsageAndPage UsageAndPage { get; } public HidUsageAndPage LinkUsageAndPage => new(valueCaps.LinkUsagePage, valueCaps.LinkUsage); public int LinkCollection => valueCaps.LinkCollection; public int MinValue => valueCaps.LogicalMin; public int MaxValue => valueCaps.LogicalMax; public int MinPhysicalValue => valueCaps.PhysicalMin; public int MaxPhysicalValue => valueCaps.PhysicalMax; public bool CanBeNull => valueCaps.HasNull; internal HidValue(HidReader reader, HidPValueCaps valueCaps, ushort usage) { this.reader = reader; this.valueCaps = valueCaps; UsageAndPage = new HidUsageAndPage(valueCaps.UsagePage, usage); } public HidValueState GetValue(ArraySegment report) => GetValue(report.ToArray(), report.Count); public HidValueState GetValue(byte[] report, int reportLength) => new(this, report, reportLength); public override string ToString() => $"{ReportId}, {LinkCollection}, Link: {{{LinkUsageAndPage}}}, Usage: {{{UsageAndPage}}}"; } ================================================ FILE: RawInput.Sharp/HidValueSet.cs ================================================ using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidValueSet : IEnumerable { internal readonly HidReader reader; internal readonly HidPValueCaps valueCaps; public int ReportId => valueCaps.ReportID; public int ReportCount => valueCaps.ReportCount; public int ValueCount => valueCaps.Range.UsageMax - valueCaps.Range.UsageMin + 1; public ushort UsagePage => valueCaps.UsagePage; public ushort UsageMin => valueCaps.Range.UsageMin; public ushort UsageMax => valueCaps.Range.UsageMax; public HidUsageAndPage LinkUsageAndPage => new(valueCaps.LinkUsagePage, valueCaps.LinkUsage); public int LinkCollection => valueCaps.LinkCollection; internal HidValueSet(HidReader reader, HidPValueCaps valueCaps) { this.reader = reader; this.valueCaps = valueCaps; } public HidValueSetState GetStates(ArraySegment report) => GetStates(report.ToArray(), report.Count); public HidValueSetState GetStates(byte[] report, int reportLength) => new(this, report, reportLength); public override string ToString() => $"{ReportId}, {LinkCollection}, Link: {{{LinkUsageAndPage}}}, UsagePage: {{{UsagePage}}}, Count: {ValueCount}"; public IEnumerator GetEnumerator() { for (var usage = valueCaps.Range.UsageMin; usage <= valueCaps.Range.UsageMax; usage++) yield return new HidValue(reader, valueCaps, usage); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } ================================================ FILE: RawInput.Sharp/HidValueSetState.cs ================================================ using System.Collections; using System.Collections.Generic; using System.Linq; namespace Linearstar.Windows.RawInput; public class HidValueSetState : IEnumerable { readonly byte[] report; readonly int reportLength; public HidValueSet ValueSet { get; } public int[] CurrentValues => this.Select(x => x.CurrentValue).ToArray(); public int?[] ScaledValues => this.Select(x => x.ScaledValue).ToArray(); internal HidValueSetState(HidValueSet valueSet, byte[] report, int reportLength) { ValueSet = valueSet; this.report = report; this.reportLength = reportLength; } public override string ToString() => $"ValueSet: {{{ValueSet}}}, CurrentValues: [{string.Join(", ", CurrentValues)}]"; public IEnumerator GetEnumerator() { for (var usage = ValueSet.UsageMin; usage <= ValueSet.UsageMax; usage++) yield return new HidValueState(new HidValue(ValueSet.reader, ValueSet.valueCaps, usage), report, reportLength); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } ================================================ FILE: RawInput.Sharp/HidValueState.cs ================================================ using System; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class HidValueState { readonly byte[] report; readonly int reportLength; public HidValue Value { get; } public unsafe int CurrentValue { get { fixed (void* preparsedData = Value.reader.PreparsedData) return HidP.GetUsageValue((IntPtr)preparsedData, HidPReportType.Input, Value.valueCaps, Value.UsageAndPage.Usage, report, reportLength); } } public unsafe int? ScaledValue { get { fixed (void* preparsedData = Value.reader.PreparsedData) return HidP.TryGetScaledUsageValue((IntPtr)preparsedData, HidPReportType.Input, Value.valueCaps, Value.UsageAndPage.Usage, report, reportLength, out var value) == NtStatus.Success ? value : null; } } public bool HasValue { get { if (!Value.CanBeNull) return true; var currentValue = CurrentValue; return currentValue >= Value.MinValue && currentValue <= Value.MaxValue; } } internal HidValueState(HidValue value, byte[] report, int reportLength) { Value = value; this.report = report; this.reportLength = reportLength; } public override string ToString() => $"Value: {{{Value}}}, CurrentValue: {CurrentValue}"; } ================================================ FILE: RawInput.Sharp/IHidPreparsedData.cs ================================================ namespace Linearstar.Windows.RawInput; public interface IHidPreparsedData { ref byte GetPinnableReference(); } ================================================ FILE: RawInput.Sharp/MarshalEx.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput; static class MarshalEx { #if NET7_0_OR_GREATER public static int SizeOf() => Marshal.SizeOf(); #else public static int SizeOf() => Marshal.SizeOf(typeof(T)); #endif } ================================================ FILE: RawInput.Sharp/Native/CfgMgr32.cs ================================================ using System; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; public static class CfgMgr32 { [DllImport("cfgmgr32", CharSet = CharSet.Unicode)] static extern ConfigReturnValue CM_Locate_DevNode(out IntPtr pdnDevInst, string pDeviceID, LocateDevNodeFlags ulFlags); [DllImport("cfgmgr32", CharSet = CharSet.Unicode)] static extern ConfigReturnValue CM_Get_DevNode_Property(IntPtr dnDevInst, in DevicePropertyKey propertyKey, out uint propertyType, IntPtr propertyBuffer, ref uint propertyBufferSize, uint ulFlags); /// /// CM_LOCATE_DEVNODE_* /// [Flags] public enum LocateDevNodeFlags : uint { /// /// CM_LOCATE_DEVNODE_NORMAL /// Normal = 0x0, /// /// CM_LOCATE_DEVNODE_PHANTOM /// Phantom = 0x1, /// /// CM_LOCATE_DEVNODE_CANCELREMOVE /// CancelRemove = 0x2, /// /// CM_LOCATE_DEVNODE_NOVALIDATION /// NoValidation = 0x4, } public static DeviceInstanceHandle LocateDevNode(string devicePath, LocateDevNodeFlags flags) { TryLocateDevNode(devicePath, flags, out var device).EnsureSuccess(); return device; } public static ConfigReturnValue TryLocateDevNode(string devicePath, LocateDevNodeFlags flags, out DeviceInstanceHandle device) { var result = CM_Locate_DevNode(out var devInst, devicePath, flags); device = result == ConfigReturnValue.Success ? (DeviceInstanceHandle)devInst : DeviceInstanceHandle.Zero; return result; } public static string? GetDevNodePropertyString(DeviceInstanceHandle device, in DevicePropertyKey propertyKey) { TryGetDevNodePropertyString(device, in propertyKey, out var value); return value; } public static ConfigReturnValue TryGetDevNodePropertyString(DeviceInstanceHandle device, in DevicePropertyKey propertyKey, out string? value) { var devInst = DeviceInstanceHandle.GetRawValue(device); uint size = 0; var result = CM_Get_DevNode_Property(devInst, in propertyKey, out _, IntPtr.Zero, ref size, 0); if (result != ConfigReturnValue.Success && result != ConfigReturnValue.BufferSmall) { value = null; return result; } var buffer = Marshal.AllocHGlobal((int)size); try { result = CM_Get_DevNode_Property(devInst, in propertyKey, out _, buffer, ref size, 0); if (result != ConfigReturnValue.Success) { value = null; return result; } value = Marshal.PtrToStringUni(buffer); return ConfigReturnValue.Success; } finally { Marshal.FreeHGlobal(buffer); } } static void EnsureSuccess(this ConfigReturnValue result) { if (result != ConfigReturnValue.Success) throw new InvalidOperationException(result.ToString()); } } ================================================ FILE: RawInput.Sharp/Native/ConfigReturnValue.cs ================================================ namespace Linearstar.Windows.RawInput.Native; /// /// CONFIGRET /// public enum ConfigReturnValue { Success = 0x0, Default = 0x1, OutOfMemory = 0x2, InvalidPointer = 0x3, InvalidFlag = 0x4, InvalidDevNode = 0x5, InvalidDevInst = 0x5, InvalidResDes = 0x6, InvalidLogConf = 0x7, InvalidArbitrator = 0x8, InvalidNodeList = 0x9, DevNodeHasReqs = 0xA, DevInstHasReqs = 0xA, InvalidResourceId = 0xB, DlvxdNotFound = 0xC, NoSuchDevNode = 0xD, NoSuchDevInst = 0xD, NoMoreLogConf = 0xE, NoMoreResDes = 0xF, AlreadySuchDevNode = 0x10, AlreadySuchDevInst = 0x10, InvalidRangeList = 0x11, InvalidRange = 0x12, Failure = 0x13, NoSuchLogicalDev = 0x14, CreateBlocked = 0x15, NotSystemVm = 0x16, RemoveVetoed = 0x17, ApmVetoed = 0x18, InvalidLoadType = 0x19, BufferSmall = 0x1A, NoArbitrator = 0x1B, NoRegistryHandle = 0x1C, RegistryError = 0x1D, InvalidDeviceId = 0x1E, InvalidData = 0x1F, InvalidApi = 0x20, DevloaderNotReady = 0x21, NeedRestart = 0x22, NoMoreHwProfiles = 0x23, DeviceNotThere = 0x24, NoSuchValue = 0x25, WrongType = 0x26, InvalidPriority = 0x27, NotDisableable = 0x28, FreeResources = 0x29, QueryVetoed = 0x2A, CantShareIrq = 0x2B, NoDependent = 0x2C, SameResourecs = 0x2D, NoSuchRegistryKey = 0x2E, InvalidMachineName = 0x2F, RemoteCommFailure = 0x30, MachineUnavailable = 0x31, NoCmServices = 0x32, AccessDenied = 0x33, CallNotImplemented = 0x34, InvalidProperty = 0x35, DeviceInterfaceActive = 0x36, NoSuchDeviceInterface = 0x37, InvalidReferenceString = 0x38, InvalidConflictList = 0x39, InvalidIndex = 0x3A, InvalidStructureSize = 0x3B, } ================================================ FILE: RawInput.Sharp/Native/DeviceInstanceHandle.cs ================================================ using System; namespace Linearstar.Windows.RawInput.Native; /// /// DEVINST /// public readonly struct DeviceInstanceHandle : IEquatable { readonly IntPtr value; public static DeviceInstanceHandle Zero => (DeviceInstanceHandle)IntPtr.Zero; DeviceInstanceHandle(IntPtr value) => this.value = value; public static IntPtr GetRawValue(DeviceInstanceHandle handle) => handle.value; public static explicit operator DeviceInstanceHandle(IntPtr value) => new(value); public static bool operator ==(DeviceInstanceHandle a, DeviceInstanceHandle b) => a.Equals(b); public static bool operator !=(DeviceInstanceHandle a, DeviceInstanceHandle b) => !a.Equals(b); public bool Equals(DeviceInstanceHandle other) => value.Equals(other.value); public override bool Equals(object? obj) => obj is DeviceInstanceHandle other && Equals(other); public override int GetHashCode() => value.GetHashCode(); public override string ToString() => value.ToString(); } ================================================ FILE: RawInput.Sharp/Native/DevicePropertyKey.cs ================================================ using System; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// DEVPROPKEY /// [StructLayout(LayoutKind.Sequential)] public readonly struct DevicePropertyKey { /// /// DEVPKEY_NAME /// public static readonly DevicePropertyKey Name = new(0xb725f130, 0x47ef, 0x101a, 0xa5, 0xf1, 0x02, 0x60, 0x8c, 0x9e, 0xeb, 0xac, 10); /// /// DEVPKEY_Device_Manufacturer /// public static readonly DevicePropertyKey DeviceManufacturer = new(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 13); /// /// DEVPKEY_Device_FriendlyName /// public static readonly DevicePropertyKey DeviceFriendlyName = new(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); readonly Guid fmtid; readonly int pid; public DevicePropertyKey(uint l, ushort w1, ushort w2, byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7, byte b8, int pid) { fmtid = new Guid((int)l, (short)w1, (short)w2, b1, b2, b3, b4, b5, b6, b7, b8); this.pid = pid; } } ================================================ FILE: RawInput.Sharp/Native/HidD.cs ================================================ using System; using System.Runtime.InteropServices; using System.Text; namespace Linearstar.Windows.RawInput.Native; public static class HidD { [DllImport("hid", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.U1)] static extern bool HidD_GetManufacturerString(IntPtr HidDeviceObject, [Out] byte[] Buffer, uint BufferLength); [DllImport("hid", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.U1)] static extern bool HidD_GetProductString(IntPtr HidDeviceObject, [Out] byte[] Buffer, uint BufferLength); [DllImport("hid", CharSet = CharSet.Unicode)] [return: MarshalAs(UnmanagedType.U1)] static extern bool HidD_GetSerialNumberString(IntPtr HidDeviceObject, [Out] byte[] Buffer, uint BufferLength); [DllImport("hid")] [return: MarshalAs(UnmanagedType.U1)] static extern bool HidD_GetPreparsedData(IntPtr HidDeviceObject, out IntPtr PreparsedData); [DllImport("hid")] [return: MarshalAs(UnmanagedType.U1)] static extern bool HidD_FreePreparsedData(IntPtr PreparsedData); public static HidDeviceHandle OpenDevice(string devicePath) { var deviceHandle = Kernel32.CreateFile(devicePath, Kernel32.ShareMode.Read | Kernel32.ShareMode.Write, Kernel32.CreateDisposition.OpenExisting); return (HidDeviceHandle)deviceHandle; } public static bool TryOpenDevice(string devicePath, out HidDeviceHandle device) { if (!Kernel32.TryCreateFile( devicePath, Kernel32.ShareMode.Read | Kernel32.ShareMode.Write, Kernel32.CreateDisposition.OpenExisting, out var deviceHandle)) { device = HidDeviceHandle.Zero; return false; } device = (HidDeviceHandle)deviceHandle; return true; } public static void CloseDevice(HidDeviceHandle device) { var deviceHandle = HidDeviceHandle.GetRawValue(device); Kernel32.CloseHandle(deviceHandle); } public static string? GetManufacturerString(HidDeviceHandle device) { var deviceHandle = HidDeviceHandle.GetRawValue(device); return GetString(deviceHandle, HidD_GetManufacturerString); } public static string? GetProductString(HidDeviceHandle device) { var deviceHandle = HidDeviceHandle.GetRawValue(device); return GetString(deviceHandle, HidD_GetProductString); } public static string? GetSerialNumberString(HidDeviceHandle device) { var deviceHandle = HidDeviceHandle.GetRawValue(device); return GetString(deviceHandle, HidD_GetSerialNumberString); } public static HidPreparsedData GetPreparsedData(HidDeviceHandle device) { var deviceHandle = HidDeviceHandle.GetRawValue(device); HidD_GetPreparsedData(deviceHandle, out var preparsedData); return (HidPreparsedData)preparsedData; } public static void FreePreparsedData(HidPreparsedData preparsedData) { HidD_FreePreparsedData((IntPtr)preparsedData); } static string? GetString(IntPtr handle, Func proc) { var buf = new byte[256]; if (!proc(handle, buf, (uint)buf.Length)) return null; var str = Encoding.Unicode.GetString(buf, 0, buf.Length); return str.Contains("\0") ? str.Substring(0, str.IndexOf('\0')) : str; } } ================================================ FILE: RawInput.Sharp/Native/HidDeviceHandle.cs ================================================ using System; namespace Linearstar.Windows.RawInput.Native; public readonly struct HidDeviceHandle : IEquatable { readonly IntPtr value; public static HidDeviceHandle Zero => (HidDeviceHandle)IntPtr.Zero; HidDeviceHandle(IntPtr value) => this.value = value; public static IntPtr GetRawValue(HidDeviceHandle handle) => handle.value; public static explicit operator HidDeviceHandle(IntPtr value) => new(value); public static bool operator ==(HidDeviceHandle a, HidDeviceHandle b) => a.Equals(b); public static bool operator !=(HidDeviceHandle a, HidDeviceHandle b) => !a.Equals(b); public bool Equals(HidDeviceHandle other) => value.Equals(other.value); public override bool Equals(object? obj) => obj is HidDeviceHandle other && Equals(other); public override int GetHashCode() => value.GetHashCode(); public override string ToString() => value.ToString(); } ================================================ FILE: RawInput.Sharp/Native/HidP.cs ================================================ using System; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; public static class HidP { [DllImport("hid")] static extern NtStatus HidP_GetCaps(IntPtr preparsedData, out HidPCaps capabilities); [DllImport("hid")] static extern NtStatus HidP_GetButtonCaps(HidPReportType reportType, [Out] HidPButtonCaps[] buttonCaps, ref ushort buttonCapsLength, IntPtr preparsedData); [DllImport("hid")] static extern NtStatus HidP_GetValueCaps(HidPReportType reportType, [Out] HidPValueCaps[] valueCaps, ref ushort valueCapsLength, IntPtr preparsedData); [DllImport("hid")] static extern NtStatus HidP_GetUsages(HidPReportType reportType, ushort usagePage, ushort linkCollection, [Out] ushort[]? usageList, ref uint usageLength, IntPtr preparsedData, byte[] report, uint reportLength); [DllImport("hid")] static extern NtStatus HidP_GetUsageValue(HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, out int usageValue, IntPtr preparsedData, byte[] report, uint reportLength); [DllImport("hid")] static extern NtStatus HidP_GetScaledUsageValue(HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, out int usageValue, IntPtr preparsedData, byte[] report, uint reportLength); [DllImport("hid")] static extern NtStatus HidP_GetUsageValueArray(HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, [Out] byte[] usageValue, ushort usageValueByteLength, IntPtr preparsedData, byte[] report, uint reportLength); public static NtStatus TryGetCaps(IntPtr preparsedData, out HidPCaps capabilities) => HidP_GetCaps(preparsedData, out capabilities); public static NtStatus TryGetCaps(HidPreparsedData preparsedData, out HidPCaps capabilities) => TryGetCaps((IntPtr)preparsedData, out capabilities); public static HidPCaps GetCaps(IntPtr preparsedData) { TryGetCaps(preparsedData, out var capabilities).EnsureSuccess(); return capabilities; } public static HidPCaps GetCaps(HidPreparsedData preparsedData) => GetCaps((IntPtr)preparsedData); public static NtStatus TryGetButtonCaps(IntPtr preparsedData, HidPReportType reportType, out HidPButtonCaps[] buttonCaps) { var caps = GetCaps(preparsedData); var capsCount = reportType switch { HidPReportType.Input => caps.NumberInputButtonCaps, HidPReportType.Output => caps.NumberOutputButtonCaps, HidPReportType.Feature => caps.NumberFeatureButtonCaps, _ => throw new ArgumentException($"Invalid HidPReportType: {reportType}", nameof(reportType)), }; buttonCaps = new HidPButtonCaps[capsCount]; return HidP_GetButtonCaps(reportType, buttonCaps, ref capsCount, preparsedData); } public static NtStatus TryGetButtonCaps(HidPreparsedData preparsedData, HidPReportType reportType, out HidPButtonCaps[] buttonCaps) => TryGetButtonCaps((IntPtr)preparsedData, reportType, out buttonCaps); public static HidPButtonCaps[] GetButtonCaps(IntPtr preparsedData, HidPReportType reportType) { TryGetButtonCaps(preparsedData, reportType, out var buttonCaps).EnsureSuccess(); return buttonCaps; } public static HidPButtonCaps[] GetButtonCaps(HidPreparsedData preparsedData, HidPReportType reportType) => GetButtonCaps((IntPtr)preparsedData, reportType); public static NtStatus TryGetValueCaps(IntPtr preparsedData, HidPReportType reportType, out HidPValueCaps[] valueCaps) { var caps = GetCaps(preparsedData); var capsCount = reportType switch { HidPReportType.Input => caps.NumberInputValueCaps, HidPReportType.Output => caps.NumberOutputValueCaps, HidPReportType.Feature => caps.NumberFeatureValueCaps, _ => throw new ArgumentException($"Invalid HidPReportType: {reportType}", nameof(reportType)), }; valueCaps = new HidPValueCaps[capsCount]; return HidP_GetValueCaps(reportType, valueCaps, ref capsCount, preparsedData); } public static NtStatus TryGetValueCaps(HidPreparsedData preparsedData, HidPReportType reportType, out HidPValueCaps[] valueCaps) => TryGetValueCaps((IntPtr)preparsedData, reportType, out valueCaps); public static HidPValueCaps[] GetValueCaps(IntPtr preparsedData, HidPReportType reportType) { TryGetValueCaps(preparsedData, reportType, out var valueCaps).EnsureSuccess(); return valueCaps; } public static HidPValueCaps[] GetValueCaps(HidPreparsedData preparsedData, HidPReportType reportType) => GetValueCaps((IntPtr)preparsedData, reportType); public static NtStatus TryGetUsages(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, byte[] report, int reportLength, out ushort[] usageList) { uint usageCount = 0; HidP_GetUsages(reportType, usagePage, linkCollection, null, ref usageCount, preparsedData, report, (uint)reportLength); usageList = new ushort[usageCount]; return HidP_GetUsages(reportType, usagePage, linkCollection, usageList, ref usageCount, preparsedData, report, (uint)reportLength); } public static NtStatus TryGetUsages(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, byte[] report, int reportLength, out ushort[] usageList) => TryGetUsages((IntPtr)preparsedData, reportType, usagePage, linkCollection, report, reportLength, out usageList); public static NtStatus TryGetUsages(IntPtr preparsedData, HidPReportType reportType, HidPButtonCaps buttonCaps, byte[] report, int reportLength, out ushort[] usageList) => TryGetUsages(preparsedData, reportType, buttonCaps.UsagePage, buttonCaps.LinkCollection, report, reportLength, out usageList); public static NtStatus TryGetUsages(HidPreparsedData preparsedData, HidPReportType reportType, HidPButtonCaps buttonCaps, byte[] report, int reportLength, out ushort[] usageList) => TryGetUsages(preparsedData, reportType, buttonCaps.UsagePage, buttonCaps.LinkCollection, report, reportLength, out usageList); public static ushort[] GetUsages(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, byte[] report, int reportLength) { TryGetUsages(preparsedData, reportType, usagePage, linkCollection, report, reportLength, out var usageList).EnsureSuccess(); return usageList; } public static ushort[] GetUsages(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, byte[] report, int reportLength) => GetUsages((IntPtr)preparsedData, reportType, usagePage, linkCollection, report, reportLength); public static ushort[] GetUsages(IntPtr preparsedData, HidPReportType reportType, HidPButtonCaps buttonCaps, byte[] report, int reportLength) => GetUsages(preparsedData, reportType, buttonCaps.UsagePage, buttonCaps.LinkCollection, report, reportLength); public static ushort[] GetUsages(HidPreparsedData preparsedData, HidPReportType reportType, HidPButtonCaps buttonCaps, byte[] report, int reportLength) => GetUsages(preparsedData, reportType, buttonCaps.UsagePage, buttonCaps.LinkCollection, report, reportLength); public static NtStatus TryGetUsageValue(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength, out int usageValue) => HidP_GetUsageValue(reportType, usagePage, linkCollection, usage, out usageValue, preparsedData, report, (uint)reportLength); public static NtStatus TryGetUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength, out int usageValue) => TryGetUsageValue((IntPtr)preparsedData, reportType, usagePage, linkCollection, usage, report, reportLength, out usageValue); public static NtStatus TryGetUsageValue(IntPtr preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength, out int usageValue) => TryGetUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength, out usageValue); public static NtStatus TryGetUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength, out int usageValue) => TryGetUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength, out usageValue); public static int GetUsageValue(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength) { TryGetUsageValue(preparsedData, reportType, usagePage, linkCollection, usage, report, reportLength, out var usageValue).EnsureSuccess(); return usageValue; } public static int GetUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength) => GetUsageValue((IntPtr)preparsedData, reportType, usagePage, linkCollection, usage, report, reportLength); public static int GetUsageValue(IntPtr preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength) => GetUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength); public static int GetUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength) => GetUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength); public static NtStatus TryGetScaledUsageValue(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength, out int usageValue) => HidP_GetScaledUsageValue(reportType, usagePage, linkCollection, usage, out usageValue, preparsedData, report, (uint)reportLength); public static NtStatus TryGetScaledUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength, out int usageValue) => TryGetScaledUsageValue((IntPtr)preparsedData, reportType, usagePage, linkCollection, usage, report, reportLength, out usageValue); public static NtStatus TryGetScaledUsageValue(IntPtr preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength, out int usageValue) => TryGetScaledUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength, out usageValue); public static NtStatus TryGetScaledUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength, out int usageValue) => TryGetScaledUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength, out usageValue); public static int GetScaledUsageValue(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength) { TryGetScaledUsageValue(preparsedData, reportType, usagePage, linkCollection, usage, report, reportLength, out var usageValue).EnsureSuccess(); return usageValue; } public static int GetScaledUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, byte[] report, int reportLength) => GetScaledUsageValue((IntPtr)preparsedData, reportType, usagePage, linkCollection, usage, report, reportLength); public static int GetScaledUsageValue(IntPtr preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength) => GetScaledUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength); public static int GetScaledUsageValue(HidPreparsedData preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength) => GetScaledUsageValue(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, report, reportLength); public static NtStatus TryGetUsageValueArray(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, ushort usageValueByteLength, byte[] report, int reportLength, out byte[] usageValue) { usageValue = new byte[usageValueByteLength]; return HidP_GetUsageValueArray(reportType, usagePage, linkCollection, usage, usageValue, usageValueByteLength, preparsedData, report, (uint)reportLength); } public static NtStatus TryGetUsageValueArray(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, ushort usageValueByteLength, byte[] report, int reportLength, out byte[] usageValue) => TryGetUsageValueArray((IntPtr)preparsedData, reportType, usagePage, linkCollection, usage, usageValueByteLength, report, reportLength, out usageValue); public static NtStatus TryGetUsageValueArray(IntPtr preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength, out byte[] usageValue) => TryGetUsageValueArray(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, (ushort)(valueCaps.BitSize * valueCaps.ReportCount), report, reportLength, out usageValue); public static NtStatus TryGetUsageValueArray(HidPreparsedData preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength, out byte[] usageValue) => TryGetUsageValueArray(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, (ushort)(valueCaps.BitSize * valueCaps.ReportCount), report, reportLength, out usageValue); public static byte[] GetUsageValueArray(IntPtr preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, ushort usageValueByteLength, byte[] report, int reportLength) { TryGetUsageValueArray(preparsedData, reportType, usagePage, linkCollection, usage, usageValueByteLength, report, reportLength, out var usageValue).EnsureSuccess(); return usageValue; } public static byte[] GetUsageValueArray(HidPreparsedData preparsedData, HidPReportType reportType, ushort usagePage, ushort linkCollection, ushort usage, ushort usageValueByteLength, byte[] report, int reportLength) => GetUsageValueArray((IntPtr)preparsedData, reportType, usagePage, linkCollection, usage, usageValueByteLength, report, reportLength); public static byte[] GetUsageValueArray(IntPtr preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength) => GetUsageValueArray(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, (ushort)(valueCaps.BitSize * valueCaps.ReportCount), report, reportLength); public static byte[] GetUsageValueArray(HidPreparsedData preparsedData, HidPReportType reportType, HidPValueCaps valueCaps, ushort usage, byte[] report, int reportLength) => GetUsageValueArray(preparsedData, reportType, valueCaps.UsagePage, valueCaps.LinkCollection, usage, (ushort)(valueCaps.BitSize * valueCaps.ReportCount), report, reportLength); public static void EnsureSuccess(this NtStatus result) { if (result != NtStatus.Success) throw new InvalidOperationException(result.ToString()); } } ================================================ FILE: RawInput.Sharp/Native/HidPButtonCaps.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// HIDP_BUTTON_CAPS /// [StructLayout(LayoutKind.Explicit)] public struct HidPButtonCaps { [FieldOffset(0)] public ushort UsagePage; [FieldOffset(2)] public byte ReportID; [FieldOffset(3), MarshalAs(UnmanagedType.U1)] public bool IsAlias; [FieldOffset(4)] public ushort BitField; [FieldOffset(6)] public ushort LinkCollection; [FieldOffset(8)] public ushort LinkUsage; [FieldOffset(10)] public ushort LinkUsagePage; [FieldOffset(12), MarshalAs(UnmanagedType.U1)] public bool IsRange; [FieldOffset(13), MarshalAs(UnmanagedType.U1)] public bool IsStringRange; [FieldOffset(14), MarshalAs(UnmanagedType.U1)] public bool IsDesignatorRange; [FieldOffset(15), MarshalAs(UnmanagedType.U1)] public bool IsAbsolute; [FieldOffset(16), MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] readonly int[] Reserved; [FieldOffset(56)] public HidPCapsRange Range; [FieldOffset(56)] public HidPCapsNotRange NotRange; } ================================================ FILE: RawInput.Sharp/Native/HidPCaps.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// HIDP_CAPS /// [StructLayout(LayoutKind.Sequential)] public struct HidPCaps { readonly ushort Usage; readonly ushort UsagePage; public ushort InputReportByteLength; public ushort OutputReportByteLength; public ushort FeatureReportByteLength; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] readonly ushort[] reserved; public ushort NumberLinkCollectionNodes; public ushort NumberInputButtonCaps; public ushort NumberInputValueCaps; public ushort NumberInputDataIndices; public ushort NumberOutputButtonCaps; public ushort NumberOutputValueCaps; public ushort NumberOutputDataIndices; public ushort NumberFeatureButtonCaps; public ushort NumberFeatureValueCaps; public ushort NumberFeatureDateIndices; public HidUsageAndPage UsageAndPage => new(UsagePage, Usage); } ================================================ FILE: RawInput.Sharp/Native/HidPCapsNotRange.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; [StructLayout(LayoutKind.Sequential)] public struct HidPCapsNotRange { public ushort Usage; readonly ushort Reserved1; public ushort StringIndex; readonly ushort Reserved2; public ushort DesignatorIndex; readonly ushort Reserved3; public ushort DataIndex; readonly ushort Reserved4; } ================================================ FILE: RawInput.Sharp/Native/HidPCapsRange.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; [StructLayout(LayoutKind.Sequential)] public struct HidPCapsRange { public ushort UsageMin; public ushort UsageMax; public ushort StringMin; public ushort StringMax; public ushort DesignatorMin; public ushort DesignatorMax; public ushort DataIndexMin; public ushort DataIndexMax; } ================================================ FILE: RawInput.Sharp/Native/HidPReportType.cs ================================================ namespace Linearstar.Windows.RawInput.Native; /// /// HIDP_REPORT_TYPE /// public enum HidPReportType { Input = 0, Output = 1, Feature = 2, } ================================================ FILE: RawInput.Sharp/Native/HidPValueCaps.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// HIDP_VALUE_CAPS /// [StructLayout(LayoutKind.Explicit)] public struct HidPValueCaps { [FieldOffset(0)] public ushort UsagePage; [FieldOffset(2)] public byte ReportID; [FieldOffset(3), MarshalAs(UnmanagedType.U1)] public bool IsAlias; [FieldOffset(4)] public ushort BitField; [FieldOffset(6)] public ushort LinkCollection; [FieldOffset(8)] public ushort LinkUsage; [FieldOffset(10)] public ushort LinkUsagePage; [FieldOffset(12), MarshalAs(UnmanagedType.U1)] public bool IsRange; [FieldOffset(13), MarshalAs(UnmanagedType.U1)] public bool IsStringRange; [FieldOffset(14), MarshalAs(UnmanagedType.U1)] public bool IsDesignatorRange; [FieldOffset(15), MarshalAs(UnmanagedType.U1)] public bool IsAbsolute; [FieldOffset(16), MarshalAs(UnmanagedType.U1)] public bool HasNull; [FieldOffset(18)] public ushort BitSize; [FieldOffset(20)] public ushort ReportCount; [FieldOffset(32)] public uint UnitsExp; [FieldOffset(36)] public uint Units; [FieldOffset(40)] public int LogicalMin; [FieldOffset(44)] public int LogicalMax; [FieldOffset(48)] public int PhysicalMin; [FieldOffset(52)] public int PhysicalMax; [FieldOffset(56)] public HidPCapsRange Range; [FieldOffset(56)] public HidPCapsNotRange NotRange; } ================================================ FILE: RawInput.Sharp/Native/HidPreparsedData.cs ================================================ using System; namespace Linearstar.Windows.RawInput.Native; /// /// HIDP_PREPARSED_DATA /// public readonly struct HidPreparsedData : IHidPreparsedData, IEquatable { readonly IntPtr value; public static HidPreparsedData Zero => (HidPreparsedData)IntPtr.Zero; HidPreparsedData(IntPtr value) { this.value = value; } public static explicit operator HidPreparsedData(IntPtr value) => new(value); public static explicit operator IntPtr(HidPreparsedData preparsedData) => preparsedData.value; public static bool operator ==(HidPreparsedData a, HidPreparsedData b) => a.Equals(b); public static bool operator !=(HidPreparsedData a, HidPreparsedData b) => !a.Equals(b); public bool Equals(HidPreparsedData other) => value.Equals(other.value); public override bool Equals(object? obj) => obj is HidPreparsedData other && Equals(other); public override int GetHashCode() => value.GetHashCode(); public override string ToString() => value.ToString(); public unsafe ref byte GetPinnableReference() => ref *(byte*)value.ToPointer(); } ================================================ FILE: RawInput.Sharp/Native/Kernel32.cs ================================================ using System; using System.Runtime.InteropServices; using System.Text; namespace Linearstar.Windows.RawInput.Native; static class Kernel32 { [DllImport("kernel32", EntryPoint = "CreateFile", SetLastError = true, CharSet = CharSet.Unicode)] static extern IntPtr CreateFileCore(string lpFileName, DesiredAccess dwDesiredAccess, ShareMode dwShareMode, IntPtr lpSecurityAttributes, CreateDisposition dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile); [DllImport("kernel32", SetLastError = true)] public static extern bool CloseHandle(IntPtr hObject); [DllImport("kernel32", EntryPoint = "GetModuleHandle", SetLastError = true)] static extern IntPtr GetModuleHandleCore(string lpModuleName); [DllImport("kernel32", EntryPoint = "GetProcAddress", SetLastError = true)] static extern IntPtr GetProcAddressCore(IntPtr hModule, string procName); [DllImport("kernel32", EntryPoint = "IsWow64Process", SetLastError = true)] static extern bool IsWow64ProcessCore(IntPtr hProcess, out bool lpSystemInfo); [DllImport("kernel32")] public static extern IntPtr GetCurrentProcess(); [DllImport("kernel32", SetLastError = true)] static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, uint dwMessageId, uint dwLanguageId, StringBuilder lpBuffer, int nSize, IntPtr Arguments); const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; [Flags] public enum DesiredAccess : uint { None, Write = 0x40000000, Read = 0x80000000 } [Flags] public enum ShareMode : uint { None, Read = 0x00000001, Write = 0x00000002, Delete = 0x00000004 } public enum CreateDisposition : uint { CreateNew = 1, CreateAlways, OpenExisting, OpenAlways, TruncateExisting } public static IntPtr GetModuleHandle(string moduleName) { var hModule = GetModuleHandleCore(moduleName); if (hModule == IntPtr.Zero) throw new Win32ErrorException(); return hModule; } public static IntPtr GetProcAddress(IntPtr hModule, string procName) { var farProc = GetProcAddressCore(hModule, procName); if (farProc == IntPtr.Zero) throw new Win32ErrorException(); return farProc; } public static bool IsWow64Process(IntPtr hProcess) { if (!IsWow64ProcessCore(hProcess, out var result)) throw new Win32ErrorException(); return result; } public static IntPtr CreateFile( string fileName, ShareMode shareMode, CreateDisposition creationDisposition, DesiredAccess desiredAccess = DesiredAccess.None, IntPtr securityAttributes = default, uint flagsAndAttributes = 0, IntPtr templateFile = default) { var handle = CreateFileCore(fileName, desiredAccess, shareMode, securityAttributes, creationDisposition, flagsAndAttributes, templateFile); if (handle == new IntPtr(-1)) throw new Win32ErrorException(); return handle; } public static bool TryCreateFile( string fileName, ShareMode shareMode, CreateDisposition creationDisposition, out IntPtr handle, DesiredAccess desiredAccess = DesiredAccess.None, IntPtr securityAttributes = default, uint flagsAndAttributes = 0, IntPtr templateFile = default) { handle = CreateFileCore(fileName, desiredAccess, shareMode, securityAttributes, creationDisposition, flagsAndAttributes, templateFile); return handle != new IntPtr(-1); } public static string FormatMessage(int errorCode) { var message = new StringBuilder(255); var charsWritten = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, IntPtr.Zero, (uint)errorCode, 0, message, message.Capacity, IntPtr.Zero); if (charsWritten == 0) throw new Win32ErrorException(); return message.ToString(); } } ================================================ FILE: RawInput.Sharp/Native/NtStatus.cs ================================================ namespace Linearstar.Windows.RawInput.Native; /// /// NTSTATUS /// public enum NtStatus : uint { Success = 0x00110000, Null = 0x80110001, InvalidPreparsedData = 0xC0110001, InvalidReportType = 0xC0110002, InvalidReportLength = 0xC0110003, UsageNotFound = 0xC0110004, ValueOutOfRange = 0xC0110005, BadLogPhyValues = 0xC0110006, BufferTooSmall = 0xC0110007, InternalError = 0xC0110008, I8042TransUnknown = 0xC0110009, IncompatibleReportId = 0xC011000A, NotValueArray = 0xC011000B, IsValueArray = 0xC011000C, DataIndexNotFound = 0xC011000D, DataIndexOutOfRange = 0xC011000E, ButtonNotPressed = 0xC011000F, ReportDoesNotExist = 0xC0110010, NotImplemented = 0xC0110020, } ================================================ FILE: RawInput.Sharp/Native/RawHid.cs ================================================ using System; using System.Linq; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RAWHID /// public struct RawHid { int dwSizeHid; int dwCount; byte[] rawData; public int ElementSize => dwSizeHid; public int Count => dwCount; public unsafe byte[] RawData => rawData; public static unsafe RawHid FromPointer(void* ptr) { var result = new RawHid(); var intPtr = (int*)ptr; result.dwSizeHid = intPtr[0]; result.dwCount = intPtr[1]; result.rawData = new byte[result.ElementSize * result.Count]; Marshal.Copy(new IntPtr(&intPtr[2]), result.rawData, 0, result.rawData.Length); return result; } public ArraySegment[] ToHidReports() { var elementSize = ElementSize; var rawDataArray = RawData; return Enumerable.Range(0, Count) .Select(x => new ArraySegment(rawDataArray, elementSize * x, elementSize)) .ToArray(); } public unsafe byte[] ToStructure() { var result = new byte[dwSizeHid * dwCount + sizeof(int) * 2]; fixed (byte* resultPtr = result) { var intPtr = (int*)resultPtr; intPtr[0] = dwSizeHid; intPtr[1] = dwCount; } rawData.CopyTo(result, sizeof(int) * 2); return result; } public override string ToString() => $"{{Count: {Count}, Size: {ElementSize}, Content: {BitConverter.ToString(RawData).Replace("-", " ")}}}"; } ================================================ FILE: RawInput.Sharp/Native/RawInputDeviceHandle.cs ================================================ using System; namespace Linearstar.Windows.RawInput.Native; /// /// HANDLE /// public readonly struct RawInputDeviceHandle : IEquatable { readonly IntPtr value; public static RawInputDeviceHandle Zero => (RawInputDeviceHandle)IntPtr.Zero; RawInputDeviceHandle(IntPtr value) => this.value = value; public static IntPtr GetRawValue(RawInputDeviceHandle handle) => handle.value; public static explicit operator RawInputDeviceHandle(IntPtr value) => new(value); public static bool operator ==(RawInputDeviceHandle a, RawInputDeviceHandle b) => a.Equals(b); public static bool operator !=(RawInputDeviceHandle a, RawInputDeviceHandle b) => !a.Equals(b); public bool Equals(RawInputDeviceHandle other) => value.Equals(other.value); public override bool Equals(object? obj) => obj is RawInputDeviceHandle other && Equals(other); public override int GetHashCode() => value.GetHashCode(); public override string ToString() => value.ToString(); } ================================================ FILE: RawInput.Sharp/Native/RawInputDeviceInfo.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RID_DEVICE_INFO /// [StructLayout(LayoutKind.Explicit)] public readonly struct RawInputDeviceInfo { [FieldOffset(0)] readonly int sbSize; [FieldOffset(4)] readonly RawInputDeviceType dwType; [FieldOffset(8)] readonly RawInputMouseInfo mouse; [FieldOffset(8)] readonly RawInputKeyboardInfo keyboard; [FieldOffset(8)] readonly RawInputHidInfo hid; /// /// dwType /// public RawInputDeviceType Type => dwType; /// /// mouse /// public RawInputMouseInfo Mouse => mouse; /// /// keyboard /// public RawInputKeyboardInfo Keyboard => keyboard; /// /// hid /// public RawInputHidInfo Hid => hid; } ================================================ FILE: RawInput.Sharp/Native/RawInputDeviceInfoBehavior.cs ================================================ namespace Linearstar.Windows.RawInput.Native; /// /// RIDI_* /// public enum RawInputDeviceInfoBehavior : uint { /// /// RIDI_PREPARSEDDATA /// PreparsedData = 0x20000005, /// /// RIDI_DEVICENAME /// DeviceName = 0x20000007, /// /// RIDI_DEVICEINFO /// DeviceInfo = 0x2000000b, } ================================================ FILE: RawInput.Sharp/Native/RawInputDeviceListItem.cs ================================================ namespace Linearstar.Windows.RawInput.Native; /// /// RAWINPUTDEVICELIST /// public struct RawInputDeviceListItem { /// /// hDevice /// public RawInputDeviceHandle Device { get; set; } /// /// dwType /// public RawInputDeviceType Type { get; set; } } ================================================ FILE: RawInput.Sharp/Native/RawInputHandle.cs ================================================ using System; namespace Linearstar.Windows.RawInput.Native; /// /// HRAWINPUT /// public readonly struct RawInputHandle : IEquatable { readonly IntPtr value; public static RawInputHandle Zero => (RawInputHandle)IntPtr.Zero; RawInputHandle(IntPtr value) => this.value = value; public static IntPtr GetRawValue(RawInputHandle handle) => handle.value; public static explicit operator RawInputHandle(IntPtr value) => new(value); public static bool operator ==(RawInputHandle a, RawInputHandle b) => a.Equals(b); public static bool operator !=(RawInputHandle a, RawInputHandle b) => !a.Equals(b); public bool Equals(RawInputHandle other) => value.Equals(other.value); public override bool Equals(object? obj) => obj is RawInputHandle other && Equals(other); public override int GetHashCode() => value.GetHashCode(); public override string ToString() => value.ToString(); } ================================================ FILE: RawInput.Sharp/Native/RawInputHeader.cs ================================================ using System; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RAWINPUTHEADER /// [StructLayout(LayoutKind.Sequential)] public readonly struct RawInputHeader { readonly RawInputDeviceType dwType; readonly int dwSize; readonly RawInputDeviceHandle hDevice; readonly IntPtr wParam; public RawInputDeviceType Type => dwType; public int Size => dwSize; public RawInputDeviceHandle DeviceHandle => hDevice; public IntPtr WParam => wParam; public override string ToString() => $"{{{Type}: {DeviceHandle}, WParam: {WParam}}}"; } ================================================ FILE: RawInput.Sharp/Native/RawInputHidInfo.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RID_DEVICE_INFO_HID /// [StructLayout(LayoutKind.Sequential)] public readonly struct RawInputHidInfo { readonly int dwVendorId; readonly int dwProductId; readonly int dwVersionNumber; readonly ushort usUsagePage; readonly ushort usUsage; /// /// dwVendorId /// public int VendorId => dwVendorId; /// /// dwProductId /// public int ProductId => dwProductId; /// /// dwVersionNumber /// public int VersionNumber => dwVersionNumber; /// /// usUsagePage, usUsage /// public HidUsageAndPage UsageAndPage => new(usUsagePage, usUsage); } ================================================ FILE: RawInput.Sharp/Native/RawInputKeyboardInfo.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RID_DEVICE_INFO_KEYBOARD /// [StructLayout(LayoutKind.Sequential)] public readonly struct RawInputKeyboardInfo { readonly int dwType; readonly int dwSubType; readonly int dwKeyboardMode; readonly int dwNumberOfFunctionKeys; readonly int dwNumberOfIndicators; readonly int dwNumberOfKeysTotal; /// /// dwType /// public int KeyboardType => dwType; /// /// dwSubType /// public int KeyboardSubType => dwSubType; /// /// dwKeyboardMode /// public int KeyboardMode => dwKeyboardMode; /// /// dwNumberOfFunctionKeys /// public int FunctionKeyCount => dwNumberOfFunctionKeys; /// /// dwNumberOfIndicators /// public int IndicatorCount => dwNumberOfIndicators; /// /// dwNumberOfKeysTotal /// public int TotalKeyCount => dwNumberOfKeysTotal; } ================================================ FILE: RawInput.Sharp/Native/RawInputMouseInfo.cs ================================================ using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RID_DEVICE_INFO_MOUSE /// [StructLayout(LayoutKind.Sequential)] public readonly struct RawInputMouseInfo { readonly int dwId; readonly int dwNumberOfButtons; readonly int dwSampleRate; [MarshalAs(UnmanagedType.Bool)] readonly bool fHasHorizontalWheel; /// /// dwId /// public int Id => dwId; /// /// dwNumberOfButtons /// public int ButtonCount => dwNumberOfButtons; /// /// dwSampleRate /// public int SampleRate => dwSampleRate; /// /// fHasHorizontalWheel /// public bool HasHorizontalWheel => fHasHorizontalWheel; } ================================================ FILE: RawInput.Sharp/Native/RawKeyboard.cs ================================================ using System; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RAWKEYBOARD /// [StructLayout(LayoutKind.Sequential)] public readonly struct RawKeyboard { readonly ushort usMakeCode; readonly RawKeyboardFlags usFlags; readonly ushort usReserverd; readonly ushort usVKey; readonly uint ulMessage; readonly uint ulExtraInformation; public int ScanCode => usMakeCode; public RawKeyboardFlags Flags => usFlags; public int VirutalKey => usVKey; public uint WindowMessage => ulMessage; public uint ExtraInformation => ulExtraInformation; public override string ToString() => $"{{Key: {VirutalKey}, ScanCode: {ScanCode}, Flags: {Flags}}}"; } /// /// RI_KEY_* /// [Flags] public enum RawKeyboardFlags : ushort { /// /// RI_KEY_MAKE /// None = 0, /// /// RI_KEY_BREAK /// Up = 1, /// /// RI_KEY_E0 /// KeyE0 = 2, /// /// RI_KEY_E1 /// KeyE1 = 4, } ================================================ FILE: RawInput.Sharp/Native/RawMouse.cs ================================================ using System; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; /// /// RAWMOUSE /// [StructLayout(LayoutKind.Sequential)] public readonly struct RawMouse { readonly RawMouseFlags usFlags; readonly ushort usReserved; readonly RawMouseButtonFlags usButtonFlags; readonly short usButtonData; readonly uint ulRawButtons; readonly int lLastX; readonly int lLastY; readonly uint ulExtraInformation; public RawMouseFlags Flags => usFlags; public RawMouseButtonFlags Buttons => usButtonFlags; public int ButtonData => usButtonData; public uint RawButtons => ulRawButtons; public int LastX => lLastX; public int LastY => lLastY; public uint ExtraInformation => ulExtraInformation; public override string ToString() => $"{{X: {LastX}, Y: {LastY}, Flags: {Flags}, Buttons: {Buttons}, Data: {ButtonData}}}"; } /// /// MOUSE_* /// [Flags] public enum RawMouseFlags : ushort { /// /// MOUSE_MOVE_RELATIVE /// None = 0, /// /// MOUSE_MOVE_ABSOLUTE /// MoveAbsolute = 1, /// /// MOUSE_VIRTUAL_DESKTOP /// VirtualDesktop = 2, /// /// MOUSE_ATTRIBUTES_CHANGED /// AttributesChanged = 4, } /// /// RI_MOUSE_* /// [Flags] public enum RawMouseButtonFlags : ushort { None, /// /// RI_MOUSE_LEFT_BUTTON_DOWN /// LeftButtonDown = 0x0001, /// /// RI_MOUSE_LEFT_BUTTON_UP /// LeftButtonUp = 0x0002, /// /// RI_MOUSE_RIGHT_BUTTON_DOWN /// RightButtonDown = 0x0004, /// /// RI_MOUSE_RIGHT_BUTTON_UP /// RightButtonUp = 0x0008, /// /// RI_MOUSE_MIDDLE_BUTTON_DOWN /// MiddleButtonDown = 0x0010, /// /// RI_MOUSE_MIDDLE_BUTTON_UP /// MiddleButtonUp = 0x0020, /// /// RI_MOUSE_BUTTON_4_DOWN /// Button4Down = 0x0040, /// /// RI_MOUSE_BUTTON_4_UP /// Button4Up = 0x0080, /// /// RI_MOUSE_BUTTON_5_DOWN /// Button5Down = 0x0100, /// /// RI_MOUSE_BUTTON_5_UP /// Button5Up = 0x0200, /// /// RI_MOUSE_WHEEL /// MouseWheel = 0x0400, /// /// RI_MOUSE_HWHEEL /// MouseHorizontalWheel = 0x0800, } ================================================ FILE: RawInput.Sharp/Native/User32.cs ================================================ using System; using System.Runtime.InteropServices; using System.Text; namespace Linearstar.Windows.RawInput.Native; public static class User32 { [DllImport("user32", SetLastError = true)] static extern uint GetRawInputDeviceList([Out] RawInputDeviceListItem[]? pRawInputDeviceList, ref uint puiNumDevices, uint cbSize); [DllImport("user32", SetLastError = true)] static extern uint GetRawInputDeviceInfo(IntPtr hDevice, RawInputDeviceInfoBehavior uiBehavior, IntPtr pData, out uint pcbSize); [DllImport("user32", SetLastError = true, CharSet = CharSet.Unicode)] static extern uint GetRawInputDeviceInfo(IntPtr hDevice, RawInputDeviceInfoBehavior uiBehavior, StringBuilder pData, in uint pcbSize); [DllImport("user32", SetLastError = true)] static extern uint GetRawInputDeviceInfo(IntPtr hDevice, RawInputDeviceInfoBehavior uiBehavior, out RawInputDeviceInfo pData, in uint pcbSize); [DllImport("user32", SetLastError = true)] static extern uint GetRawInputDeviceInfo(IntPtr hDevice, RawInputDeviceInfoBehavior uiBehavior, [Out] byte[] pData, in uint pcbSize); [DllImport("user32", SetLastError = true)] static extern bool RegisterRawInputDevices(RawInputDeviceRegistration[] pRawInputDevices, uint uiNumDevices, uint cbSize); [DllImport("user32", SetLastError = true)] static extern uint GetRegisteredRawInputDevices([Out] RawInputDeviceRegistration[]? pRawInputDevices, ref uint puiNumDevices, uint cbSize); [DllImport("user32", SetLastError = true)] static extern uint GetRawInputData(IntPtr hRawInput, RawInputGetBehavior uiBehavior, IntPtr pData, ref uint pcbSize, uint cbSizeHeader); [DllImport("user32", SetLastError = true)] static extern uint GetRawInputData(IntPtr hRawInput, RawInputGetBehavior uiBehavior, out RawInputHeader pData, ref uint pcbSize, uint cbSizeHeader); [DllImport("user32", SetLastError = true)] static extern uint GetRawInputBuffer(IntPtr pData, ref uint pcbSize, uint cbSizeHeader); [DllImport("user32", SetLastError = true)] static extern IntPtr DefRawInputProc(byte[] paRawInput, int nInput, uint cbSizeHeader); public enum RawInputGetBehavior : uint { Input = 0x10000003, Header = 0x10000005, } public static RawInputDeviceListItem[] GetRawInputDeviceList() { var size = (uint)MarshalEx.SizeOf(); // Get device count by passing null for pRawInputDeviceList. uint deviceCount = 0; GetRawInputDeviceList(null, ref deviceCount, size); // Now, fill the buffer using the device count. var devices = new RawInputDeviceListItem[deviceCount]; GetRawInputDeviceList(devices, ref deviceCount, size).EnsureSuccess(); return devices; } public static string? GetRawInputDeviceName(RawInputDeviceHandle device) { var deviceHandle = RawInputDeviceHandle.GetRawValue(device); // Get the length of the device name first. // For RIDI_DEVICENAME, the value in the pcbSize is the character count instead of the byte count. GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoBehavior.DeviceName, IntPtr.Zero, out var size); if (size <= 2) return null; var sb = new StringBuilder((int)size); GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoBehavior.DeviceName, sb, in size).EnsureSuccess(); return sb.ToString(); } public static RawInputDeviceInfo GetRawInputDeviceInfo(RawInputDeviceHandle device) { var deviceHandle = RawInputDeviceHandle.GetRawValue(device); var size = (uint)MarshalEx.SizeOf(); GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoBehavior.DeviceInfo, out var deviceInfo, in size).EnsureSuccess(); return deviceInfo; } public static byte[] GetRawInputDevicePreparsedData(RawInputDeviceHandle device) { var deviceHandle = RawInputDeviceHandle.GetRawValue(device); GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoBehavior.PreparsedData, IntPtr.Zero, out var size); if (size == 0) return new byte[0]; var rt = new byte[size]; GetRawInputDeviceInfo(deviceHandle, RawInputDeviceInfoBehavior.PreparsedData, rt, in size).EnsureSuccess(); return rt; } public static void RegisterRawInputDevices(params RawInputDeviceRegistration[] devices) { RegisterRawInputDevices(devices, (uint)devices.Length, (uint)MarshalEx.SizeOf()).EnsureSuccess(); } public static RawInputDeviceRegistration[] GetRegisteredRawInputDevices() { var size = (uint)MarshalEx.SizeOf(); uint count = 0; GetRegisteredRawInputDevices(null, ref count, size); var rt = new RawInputDeviceRegistration[count]; GetRegisteredRawInputDevices(rt, ref count, size).EnsureSuccess(); return rt; } public static RawInputHeader GetRawInputDataHeader(RawInputHandle rawInput) { var hRawInput = RawInputHandle.GetRawValue(rawInput); var headerSize = (uint)MarshalEx.SizeOf(); var size = headerSize; GetRawInputData(hRawInput, RawInputGetBehavior.Header, out var header, ref size, headerSize).EnsureSuccess(); return header; } public static uint GetRawInputDataSize(RawInputHandle rawInput) { var hRawInput = RawInputHandle.GetRawValue(rawInput); var headerSize = (uint)MarshalEx.SizeOf(); uint size = 0; GetRawInputData(hRawInput, RawInputGetBehavior.Input, IntPtr.Zero, ref size, headerSize); return size; } public static void GetRawInputData(RawInputHandle rawInput, IntPtr ptr, uint size) { var hRawInput = RawInputHandle.GetRawValue(rawInput); var headerSize = (uint)MarshalEx.SizeOf(); GetRawInputData(hRawInput, RawInputGetBehavior.Input, ptr, ref size, headerSize).EnsureSuccess(); } public static unsafe RawMouse GetRawInputMouseData(RawInputHandle rawInput, out RawInputHeader header) { var size = GetRawInputDataSize(rawInput); var headerSize = (uint)MarshalEx.SizeOf(); var bytes = new byte[size]; fixed (byte* bytesPtr = bytes) { GetRawInputData(rawInput, (IntPtr)bytesPtr, size); header = *(RawInputHeader*)bytesPtr; return *(RawMouse*)(bytesPtr + headerSize); } } public static unsafe RawKeyboard GetRawInputKeyboardData(RawInputHandle rawInput, out RawInputHeader header) { var size = GetRawInputDataSize(rawInput); var headerSize = (uint)MarshalEx.SizeOf(); var bytes = new byte[size]; fixed (byte* bytesPtr = bytes) { GetRawInputData(rawInput, (IntPtr)bytesPtr, size); header = *(RawInputHeader*)bytesPtr; return *(RawKeyboard*)(bytesPtr + headerSize); } } public static unsafe RawHid GetRawInputHidData(RawInputHandle rawInput, out RawInputHeader header) { var size = GetRawInputDataSize(rawInput); var headerSize = (uint)MarshalEx.SizeOf(); var bytes = new byte[size]; fixed (byte* bytesPtr = bytes) { GetRawInputData(rawInput, (IntPtr)bytesPtr, size); header = *(RawInputHeader*)bytesPtr; return RawHid.FromPointer(bytesPtr + headerSize); } } public static uint GetRawInputBufferSize() { var headerSize = (uint)MarshalEx.SizeOf(); uint size = 0; GetRawInputBuffer(IntPtr.Zero, ref size, headerSize); return size; } public static uint GetRawInputBuffer(IntPtr ptr, uint size) { var headerSize = (uint)MarshalEx.SizeOf(); return GetRawInputBuffer(ptr, ref size, headerSize).EnsureSuccess(); } public static void DefRawInputProc(byte[] paRawInput) { var headerSize = (uint)MarshalEx.SizeOf(); DefRawInputProc(paRawInput, paRawInput.Length, headerSize); } public static bool EnsureSuccess(this bool result) { if (!result) throw new Win32ErrorException(); return result; } public static uint EnsureSuccess(this uint result) { if (result == unchecked((uint)-1)) throw new Win32ErrorException(); return result; } } ================================================ FILE: RawInput.Sharp/Native/Win32ErrorException.cs ================================================ using System; using System.Runtime.InteropServices; namespace Linearstar.Windows.RawInput.Native; public class Win32ErrorException : Exception { public Win32ErrorException() : this(Marshal.GetLastWin32Error()) { } public Win32ErrorException(int win32ErrorCode) : base(Kernel32.FormatMessage(win32ErrorCode)) { } } ================================================ FILE: RawInput.Sharp/RawInput.Sharp.csproj ================================================  10 Linearstar.Windows.RawInput true Linearstar true A simple wrapper library for Raw Input. Copyright (c) 2019 mfakane mfakane Zlib 0.1.3 0.0.0.0 0.0.0.0 Windows RawInput https://github.com/mfakane/rawinput-sharp enable netstandard1.1;net461;net462;net47;net471;net472;net48;net7.0 ================================================ FILE: RawInput.Sharp/RawInputData.cs ================================================ using System; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public abstract class RawInputData { RawInputDevice? device; public RawInputHeader Header { get; } public RawInputDevice? Device => device ??= Header.DeviceHandle != RawInputDeviceHandle.Zero ? RawInputDevice.FromHandle(Header.DeviceHandle) : null; protected RawInputData(RawInputHeader header) { Header = header; } public static RawInputData FromHandle(IntPtr lParam) => FromHandle((RawInputHandle)lParam); public static RawInputData FromHandle(RawInputHandle rawInput) { var header = User32.GetRawInputDataHeader(rawInput); switch (header.Type) { case RawInputDeviceType.Mouse: return new RawInputMouseData(header, User32.GetRawInputMouseData(rawInput, out _)); case RawInputDeviceType.Keyboard: return new RawInputKeyboardData(header, User32.GetRawInputKeyboardData(rawInput, out _)); case RawInputDeviceType.Hid: return RawInputHidData.Create(header, User32.GetRawInputHidData(rawInput, out _)); default: throw new ArgumentException(); } } static unsafe RawInputData ParseRawInputBufferItem(byte* ptr) { var header = *(RawInputHeader*)ptr; var headerSize = MarshalEx.SizeOf(); var dataPtr = ptr + headerSize; // RAWINPUT structure must be aligned by 8 bytes on WOW64 // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getrawinputbuffer#remarks if (EnvironmentEx.Is64BitProcess && EnvironmentEx.Is64BitOperatingSystem) dataPtr += 8; switch (header.Type) { case RawInputDeviceType.Mouse: return new RawInputMouseData(header, *(RawMouse*)dataPtr); case RawInputDeviceType.Keyboard: return new RawInputKeyboardData(header, *(RawKeyboard*)dataPtr); case RawInputDeviceType.Hid: return RawInputHidData.Create(header, RawHid.FromPointer(dataPtr)); default: throw new ArgumentException(); } } public static unsafe RawInputData[] GetBufferedData(int bufferSize = 8) { var itemSize = User32.GetRawInputBufferSize(); if (itemSize == 0) return new RawInputData[0]; var bytes = new byte[itemSize * bufferSize]; fixed (byte* bytesPtr = bytes) { var count = User32.GetRawInputBuffer((IntPtr)bytesPtr, (uint)bytes.Length); if (count == 0) return new RawInputData[0]; var result = new RawInputData[count]; for (int i = 0, offset = 0; i < result.Length; i++) { var data = ParseRawInputBufferItem(bytesPtr + offset); result[i] = data; offset = Align(offset + data.Header.Size); } return result; } } protected static int Align(int x) => (x + IntPtr.Size - 1) & ~(IntPtr.Size - 1); public static void DefRawInputProc(RawInputData[] data) => User32.DefRawInputProc(data.SelectMany(i => i.ToStructure()).ToArray()); public abstract byte[] ToStructure(); } ================================================ FILE: RawInput.Sharp/RawInputDevice.cs ================================================ using System; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public abstract class RawInputDevice { bool gotAttributes; string? productName; string? manufacturerName; string? serialNumber; protected RawInputDeviceInfo DeviceInfo { get; } public RawInputDeviceHandle Handle { get; } public RawInputDeviceType DeviceType => DeviceInfo.Type; public string? DevicePath { get; } public string? ManufacturerName { get { if (manufacturerName == null) GetAttributesOnce(); return manufacturerName; } } public string? ProductName { get { if (productName == null) GetAttributesOnce(); return productName; } } public string? SerialNumber { get { if (serialNumber == null) GetAttributesOnce(); return serialNumber; } } public bool IsConnected => DevicePath != null && CfgMgr32.TryLocateDevNode(DevicePath, CfgMgr32.LocateDevNodeFlags.Normal, out _) == ConfigReturnValue.Success; public abstract HidUsageAndPage UsageAndPage { get; } public abstract int VendorId { get; } public abstract int ProductId { get; } void GetAttributesOnce() { if (gotAttributes) return; gotAttributes = true; if (DevicePath == null) return; GetAttributesFromHidD(); if (manufacturerName == null || productName == null) GetAttributesFromCfgMgr(); } void GetAttributesFromHidD() { if (DevicePath == null || !HidD.TryOpenDevice(DevicePath, out var device)) return; try { manufacturerName ??= HidD.GetManufacturerString(device); productName ??= HidD.GetProductString(device); serialNumber ??= HidD.GetSerialNumberString(device); } finally { HidD.CloseDevice(device); } } void GetAttributesFromCfgMgr() { if (DevicePath == null) return; var path = DevicePath.Substring(4).Replace('#', '\\'); if (path.Contains("{")) path = path.Substring(0, path.IndexOf('{') - 1); var device = CfgMgr32.LocateDevNode(path, CfgMgr32.LocateDevNodeFlags.Phantom); manufacturerName ??= CfgMgr32.GetDevNodePropertyString(device, in DevicePropertyKey.DeviceManufacturer); productName ??= CfgMgr32.GetDevNodePropertyString(device, in DevicePropertyKey.DeviceFriendlyName); productName ??= CfgMgr32.GetDevNodePropertyString(device, in DevicePropertyKey.Name); } protected RawInputDevice(RawInputDeviceHandle device, RawInputDeviceInfo deviceInfo) { Handle = device; DevicePath = User32.GetRawInputDeviceName(device); DeviceInfo = deviceInfo; } public static RawInputDevice FromHandle(RawInputDeviceHandle device) { var deviceInfo = User32.GetRawInputDeviceInfo(device); switch (deviceInfo.Type) { case RawInputDeviceType.Mouse: return new RawInputMouse(device, deviceInfo); case RawInputDeviceType.Keyboard: return new RawInputKeyboard(device, deviceInfo); case RawInputDeviceType.Hid: return RawInputDigitizer.IsSupported(deviceInfo.Hid.UsageAndPage) ? new RawInputDigitizer(device, deviceInfo) : new RawInputHid(device, deviceInfo); default: throw new ArgumentException(); } } /// /// Gets available devices that can be handled with Raw Input. /// /// Array of , which contains mouse as a , keyboard as a , and any other HIDs as a . public static RawInputDevice[] GetDevices() { var devices = User32.GetRawInputDeviceList(); return devices.Select(i => FromHandle(i.Device)).ToArray(); } public byte[] GetPreparsedData() => User32.GetRawInputDevicePreparsedData(Handle); public static void RegisterDevice(HidUsageAndPage usageAndPage, RawInputDeviceFlags flags, IntPtr hWndTarget) => RegisterDevice(new RawInputDeviceRegistration(usageAndPage, flags, hWndTarget)); public static void RegisterDevice(params RawInputDeviceRegistration[] devices) => User32.RegisterRawInputDevices(devices); public static void UnregisterDevice(HidUsageAndPage usageAndPage) => RegisterDevice(usageAndPage, RawInputDeviceFlags.Remove, IntPtr.Zero); public static RawInputDeviceRegistration[] GetRegisteredDevices() => User32.GetRegisteredRawInputDevices(); } ================================================ FILE: RawInput.Sharp/RawInputDeviceFlags.cs ================================================ using System; namespace Linearstar.Windows.RawInput; /// /// RIDEV_* /// [Flags] public enum RawInputDeviceFlags { None, /// /// RIDEV_REMOVE. Removes the top level collection from the inclusion list. /// Remove = 0x1, /// /// RIDEV_EXCLUDE. Specifies the top level collections to exclude when reading a complete usage page. /// Exclude = 0x10, /// /// RIDEV_PAGEONLY. Specifies all devices whose top level collection is from the specified . /// PageOnly = 0x20, /// /// RIDEV_NOLEGACY. Prevents any devices specified by or from generating legacy messages. /// NoLegacy = 0x30, /// /// RIDEV_INPUTSINK. Enables the caller to receive the input even when the caller is not in the foreground. Note that must be specified. /// InputSink = 0x100, /// /// RIDEV_CAPTUREMOUSE. The mouse button click does not activate the other window. /// CaptureMouse = 0x200, /// /// RIDEV_NOHOTKEYS. The application-defined keyboard device hotkeys are not handled. This can be specified even if is not specified and is . /// NoHotKeys = 0x200, /// /// RIDEV_APPKEYS. The application command keys are handled. This can be specified only if is specified for a keyboard device. /// AppKeys = 0x400, /// /// RIDEV_EXINPUTSINK. Enables the caller to receive input in the background only if the foreground application does not process it. /// ExInputSink = 0x1000, /// /// RIDEV_DEVNOTIFY. Enables the caller to receive WM_INPUT_DEVICE_CHANGE notifications for device arrival and device removal. /// DevNotify = 0x2000, } ================================================ FILE: RawInput.Sharp/RawInputDeviceRegistration.cs ================================================ using System; namespace Linearstar.Windows.RawInput; public readonly struct RawInputDeviceRegistration { readonly ushort usUsagePage; readonly ushort usUsage; readonly RawInputDeviceFlags dwFlags; readonly IntPtr hwndTarget; public ushort UsagePage => usUsagePage; public ushort Usage => usUsage; public RawInputDeviceFlags Flags => dwFlags; public IntPtr HwndTarget => hwndTarget; public RawInputDeviceRegistration(HidUsageAndPage usageAndPage, RawInputDeviceFlags flags, IntPtr hWndTarget) : this(usageAndPage.UsagePage, usageAndPage.Usage, flags, hWndTarget) { } public RawInputDeviceRegistration(ushort usagePage, ushort usage, RawInputDeviceFlags flags, IntPtr hWndTarget) { usUsagePage = usagePage; usUsage = usage; dwFlags = flags; hwndTarget = hWndTarget; } } ================================================ FILE: RawInput.Sharp/RawInputDeviceType.cs ================================================ namespace Linearstar.Windows.RawInput; public enum RawInputDeviceType { Mouse, Keyboard, Hid, } ================================================ FILE: RawInput.Sharp/RawInputDigitizer.cs ================================================ using System; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputDigitizer : RawInputHid { public static readonly HidUsageAndPage UsageContactCount = new(0x0D, 0x54); public int MaxContactCount => Reader.ValueSets.SelectMany(x => x).FirstOrDefault(x => x.LinkUsageAndPage == UsageAndPage && x.UsageAndPage == UsageContactCount)?.MaxValue ?? 1; internal RawInputDigitizer(RawInputDeviceHandle device, RawInputDeviceInfo deviceInfo) : base(device, deviceInfo) { if (!IsSupported(deviceInfo.Hid.UsageAndPage)) throw new ArgumentException($"UsagePage and Usage {deviceInfo.Hid.UsageAndPage} is not supported as a digitizer.", nameof(deviceInfo)); } public static bool IsSupported(HidUsageAndPage usageAndPage) => usageAndPage.UsagePage == 0x0D; } ================================================ FILE: RawInput.Sharp/RawInputDigitizerButton.cs ================================================ using System.Collections.Generic; using System.Linq; namespace Linearstar.Windows.RawInput; class RawInputDigitizerButton { public static readonly HidUsageAndPage UsageInRange = new(0x0D, 0x32); public static readonly HidUsageAndPage UsageInvert = new(0x0D, 0x3c); public static readonly HidUsageAndPage UsageTipSwitch = new(0x0D, 0x42); public static readonly HidUsageAndPage UsageBarrel = new(0x0D, 0x44); public static readonly HidUsageAndPage UsageEraser = new(0x0D, 0x45); public static readonly HidUsageAndPage UsageConfidence = new(0x0D, 0x47); public bool? InRange { get; } public bool? IsInverted { get; } public bool IsDown { get; } public bool? IsBarrel { get; } public bool? IsEraser { get; } public bool? HasConfidence { get; } RawInputDigitizerButton(bool? inRange, bool? isInverted, bool isDown, bool? isBarrel, bool? isEraser, bool? hasConfidence) { InRange = inRange; IsInverted = isInverted; IsDown = isDown; IsBarrel = isBarrel; IsEraser = isEraser; HasConfidence = hasConfidence; } internal static IEnumerable Read( ILookup buttonStatesByLinkCollection) { var buttonStates = buttonStatesByLinkCollection.ToDictionary(x => x.Key, x => x.ToList()); while (buttonStates[UsageTipSwitch].Any()) { var inRange = PopState(UsageInRange); var isInverted = PopState(UsageInvert); var isDown = PopState(UsageTipSwitch) ?? false; var isBarrel = PopState(UsageBarrel); var isEraser = PopState(UsageEraser); var hasConfidence = PopState(UsageConfidence); yield return new RawInputDigitizerButton(inRange, isInverted, isDown, isBarrel, isEraser, hasConfidence); } bool? PopState(HidUsageAndPage usageAndPage) { if (buttonStates?.TryGetValue(usageAndPage, out var usageStates) != true) return null; if (usageStates?.Any() != true) return null; var isActive = usageStates[0].IsActive; usageStates.RemoveAt(0); return isActive; } } } ================================================ FILE: RawInput.Sharp/RawInputDigitizerContact.cs ================================================ using System.Collections.Generic; using System.Linq; namespace Linearstar.Windows.RawInput; public class RawInputDigitizerContact { public static readonly HidUsageAndPage UsageX = new(0x01, 0x30); public static readonly HidUsageAndPage UsageY = new(0x01, 0x31); public static readonly HidUsageAndPage UsagePressure = new(0x0D, 0x30); public static readonly HidUsageAndPage UsageWidth = new(0x0D, 0x48); public static readonly HidUsageAndPage UsageHeight = new(0x0D, 0x49); public static readonly HidUsageAndPage UsageIdentifier = new(0x0D, 0x51); readonly RawInputDigitizerButton button; public RawInputDigitizerContactKind Kind { get { if (button.IsEraser == true) return RawInputDigitizerContactKind.Eraser; if (button.IsDown) return button.HasConfidence == true ? RawInputDigitizerContactKind.Finger : RawInputDigitizerContactKind.Pen; return button.InRange == true ? RawInputDigitizerContactKind.Hover : RawInputDigitizerContactKind.None; } } public int X { get; } public int Y { get; } public int MinX { get; } public int MinY { get; } public int MaxX { get; } public int MaxY { get; } public int? Pressure { get; } public int? MaxPressure { get; } public bool? IsInverted => button.IsInverted; public bool? IsButtonDown => button.IsDown; public int? Width { get; } public int? Height { get; } public int? Identifier { get; } RawInputDigitizerContact( RawInputDigitizerButton button, HidValueState x, HidValueState y, HidValueState? pressure, HidValueState? width, HidValueState? height, HidValueState? identifier) { this.button = button; X = x.CurrentValue; Y = y.CurrentValue; MinX = x.Value.MinValue; MinY = y.Value.MinValue; MaxX = x.Value.MaxValue; MaxY = y.Value.MaxValue; Pressure = pressure?.CurrentValue; MaxPressure = pressure?.Value.MaxValue; Width = width?.CurrentValue; Height = height?.CurrentValue; Identifier = identifier?.CurrentValue; } internal static IEnumerable Read( RawInputDigitizerButton button, IEnumerable valueStatesPerLinkCollection) { var valueStates = valueStatesPerLinkCollection .ToLookup(x => x.Value.UsageAndPage) .ToDictionary(x => x.Key, x => x.ToList()); while (valueStates[UsageX].Any()) { var x = PopState(UsageX); var y = PopState(UsageY); if (x == null || y == null) break; var pressure = PopState(UsagePressure); var width = PopState(UsageWidth); var height = PopState(UsageHeight); var identifier = PopState(UsageIdentifier); yield return new RawInputDigitizerContact( button, x, y, pressure, width, height, identifier ); } HidValueState? PopState(HidUsageAndPage usageAndPage) { if (valueStates?.TryGetValue(usageAndPage, out var usageStates) != true) return null; if (usageStates?.Any() != true) return null; var state = usageStates[0]; usageStates.RemoveAt(0); return state; } } public override string ToString() => "{" + string.Join(", ", new[] { $"X: {X}/{MaxX}", $"Y: {Y}/{MaxY}", $"Kind: {Kind}", Pressure.HasValue ? $"Pressure: {Pressure}/{MaxPressure}" : null, IsInverted.HasValue ? $"Inverted: {IsInverted}" : null, IsButtonDown.HasValue ? $"Button: {IsButtonDown}" : null, Width.HasValue ? $"Width: {Width}" : null, Height.HasValue ? $"Height: {Height}" : null, Identifier.HasValue ? $"Identifier: {Identifier}" : null, }.Where(i => i != null)) + "}"; } ================================================ FILE: RawInput.Sharp/RawInputDigitizerContactKind.cs ================================================ namespace Linearstar.Windows.RawInput; public enum RawInputDigitizerContactKind { None, Finger, Hover, Pen, Eraser, } ================================================ FILE: RawInput.Sharp/RawInputDigitizerData.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputDigitizerData : RawInputHidData { public int ContactsCount { get; } public int MaxContactsCount { get; } public RawInputDigitizerContact[] Contacts { get; } public RawInputDigitizerData(RawInputHeader header, RawHid hid) : base(header, hid) { if (Device is not RawInputDigitizer digitizer) throw new ArgumentException($"Device specified in the {nameof(header)} was not a valid digitizer.", nameof(header)); var contactButtonStates = ButtonSetStates .SelectMany(x => x) .Where(x => x.Button.LinkUsageAndPage != digitizer.UsageAndPage) .ToLookup(x => x.Button.LinkCollection); var contactValueStates = ValueSetStates .SelectMany(x => x) .Where(x => x.Value.LinkUsageAndPage != digitizer.UsageAndPage) .ToLookup(x => x.Value.LinkCollection); var contactsCountUsages = ValueSetStates .SelectMany(x => x) .Where(x => x.Value.LinkUsageAndPage == digitizer.UsageAndPage && x.Value.UsageAndPage == RawInputDigitizer.UsageContactCount) .ToArray(); var contactsCount = contactsCountUsages.Select(x => x.CurrentValue).DefaultIfEmpty(1).Max(); Contacts = EnumerateContacts().ToArray(); this.ContactsCount = contactsCountUsages.Any() ? contactsCount : Math.Min(Contacts.Length, 1); this.MaxContactsCount = contactsCountUsages.Select(x => x.Value.MaxValue).DefaultIfEmpty(1).Max(); IEnumerable EnumerateContacts() { foreach (var buttonStates in contactButtonStates) foreach (var button in RawInputDigitizerButton.Read(buttonStates.ToLookup(x => x.Button.UsageAndPage))) foreach (var contact in RawInputDigitizerContact.Read(button, contactValueStates[buttonStates.Key])) if (buttonStates.Key <= contactsCount) yield return contact; } } } ================================================ FILE: RawInput.Sharp/RawInputHid.cs ================================================ using System; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputHid : RawInputDevice { readonly Lazy hidReader; public override HidUsageAndPage UsageAndPage => DeviceInfo.Hid.UsageAndPage; public override int VendorId => DeviceInfo.Hid.VendorId; public override int ProductId => DeviceInfo.Hid.ProductId; public int Version => DeviceInfo.Hid.VersionNumber; public HidReader Reader => hidReader.Value; internal RawInputHid(RawInputDeviceHandle device, RawInputDeviceInfo deviceInfo) : base(device, deviceInfo) { if (deviceInfo.Type != RawInputDeviceType.Hid) throw new ArgumentException($"Device type must be {RawInputDeviceType.Hid}.", nameof(deviceInfo)); hidReader = new Lazy(() => new HidReader(new HidPreparsedByteArrayData(GetPreparsedData()))); } } ================================================ FILE: RawInput.Sharp/RawInputHidData.cs ================================================ using System.Linq; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputHidData : RawInputData { public new RawInputHid? Device => (RawInputHid?)base.Device; public RawHid Hid { get; } public HidButtonSetState[] ButtonSetStates => Device != null ? Hid.ToHidReports().SelectMany(report => Device.Reader.ButtonSets.Select(x => x.GetStates(report))).ToArray() : new HidButtonSetState[0]; public HidValueSetState[] ValueSetStates => Device != null ? Hid.ToHidReports().SelectMany(report => Device.Reader.ValueSets.Select(x => x.GetStates(report))).ToArray() : new HidValueSetState[0]; protected RawInputHidData(RawInputHeader header, RawHid hid) : base(header) => Hid = hid; public static RawInputHidData Create(RawInputHeader header, RawHid hid) { var device = header.DeviceHandle != RawInputDeviceHandle.Zero ? RawInputDevice.FromHandle(header.DeviceHandle) : null; if (device != null && RawInputDigitizer.IsSupported(device.UsageAndPage)) return new RawInputDigitizerData(header, hid); return new RawInputHidData(header, hid); } public override unsafe byte[] ToStructure() { var headerSize = MarshalEx.SizeOf(); var hid = Hid.ToStructure(); var bytes = new byte[Align(headerSize + hid.Length)]; fixed (byte* bytesPtr = bytes) *(RawInputHeader*) bytesPtr = Header; hid.CopyTo(bytes, headerSize); return bytes; } public override string ToString() => $"{{{Header}, {Hid}}}"; } ================================================ FILE: RawInput.Sharp/RawInputKeyboard.cs ================================================ using System; using System.Globalization; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputKeyboard : RawInputDevice { public override HidUsageAndPage UsageAndPage => HidUsageAndPage.Keyboard; public override int VendorId => DevicePath?.Contains("VID_") == true ? int.Parse(DevicePath.Substring(DevicePath.IndexOf("VID_", StringComparison.Ordinal) + 4, 4), NumberStyles.HexNumber) : 0; public override int ProductId => DevicePath?.Contains("PID_") == true ? int.Parse(DevicePath.Substring(DevicePath.IndexOf("PID_", StringComparison.Ordinal) + 4, 4), NumberStyles.HexNumber) : 0; public int KeyboardType => DeviceInfo.Keyboard.KeyboardType; public int KeyboardSubType => DeviceInfo.Keyboard.KeyboardSubType; public int KeyboardMode => DeviceInfo.Keyboard.KeyboardMode; public int FunctionKeyCount => DeviceInfo.Keyboard.FunctionKeyCount; public int IndicatorCount => DeviceInfo.Keyboard.IndicatorCount; public int TotalKeyCount => DeviceInfo.Keyboard.TotalKeyCount; internal RawInputKeyboard(RawInputDeviceHandle device, RawInputDeviceInfo deviceInfo) : base(device, deviceInfo) { if (deviceInfo.Type != RawInputDeviceType.Keyboard) throw new ArgumentException($"Device type must be {RawInputDeviceType.Keyboard}", nameof(deviceInfo)); } } ================================================ FILE: RawInput.Sharp/RawInputKeyboardData.cs ================================================ using System.Runtime.InteropServices; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputKeyboardData : RawInputData { public RawKeyboard Keyboard { get; } public RawInputKeyboardData(RawInputHeader header, RawKeyboard keyboard) : base(header) => Keyboard = keyboard; public override unsafe byte[] ToStructure() { var headerSize = MarshalEx.SizeOf(); var mouseSize = MarshalEx.SizeOf(); var bytes = new byte[headerSize + mouseSize]; fixed (byte* bytesPtr = bytes) { *(RawInputHeader*)bytesPtr = Header; *(RawKeyboard*)(bytesPtr + headerSize) = Keyboard; } return bytes; } public override string ToString() => $"{{{Header}, {Keyboard}}}"; } ================================================ FILE: RawInput.Sharp/RawInputMouse.cs ================================================ using System; using System.Globalization; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputMouse : RawInputDevice { public override HidUsageAndPage UsageAndPage => HidUsageAndPage.Mouse; public override int VendorId => DevicePath?.Contains("VID_") == true ? int.Parse(DevicePath.Substring(DevicePath.IndexOf("VID_", StringComparison.Ordinal) + 4, 4), NumberStyles.HexNumber) : 0; public override int ProductId => DevicePath?.Contains("PID_") == true ? int.Parse(DevicePath.Substring(DevicePath.IndexOf("PID_", StringComparison.Ordinal) + 4, 4), NumberStyles.HexNumber) : 0; public int Id => DeviceInfo.Mouse.Id; public int ButtonCount => DeviceInfo.Mouse.ButtonCount; public int SampleRate => DeviceInfo.Mouse.SampleRate; public bool HasHorizontalWheel => DeviceInfo.Mouse.HasHorizontalWheel; internal RawInputMouse(RawInputDeviceHandle device, RawInputDeviceInfo deviceInfo) : base(device, deviceInfo) { if (deviceInfo.Type != RawInputDeviceType.Mouse) throw new ArgumentException($"Device type must be {RawInputDeviceType.Mouse}.", nameof(deviceInfo)); } } ================================================ FILE: RawInput.Sharp/RawInputMouseData.cs ================================================ using System.Runtime.InteropServices; using Linearstar.Windows.RawInput.Native; namespace Linearstar.Windows.RawInput; public class RawInputMouseData : RawInputData { public RawMouse Mouse { get; } public RawInputMouseData(RawInputHeader header, RawMouse mouse) : base(header) => Mouse = mouse; public override unsafe byte[] ToStructure() { var headerSize = MarshalEx.SizeOf(); var mouseSize = MarshalEx.SizeOf(); var bytes = new byte[headerSize + mouseSize]; fixed (byte* bytesPtr = bytes) { *(RawInputHeader*)bytesPtr = Header; *(RawMouse*)(bytesPtr + headerSize) = Mouse; } return bytes; } public override string ToString() => $"{{{Header}, {Mouse}}}"; } ================================================ FILE: RawInput.Sharp.DigitizerExample/MainForm.Designer.cs ================================================ namespace RawInput.Sharp.DigitizerExample; partial class MainForm { /// /// Required designer variable. /// private System.ComponentModel.IContainer components = null; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. 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() { this.textLabel = new System.Windows.Forms.Label(); this.SuspendLayout(); // // textLabel // this.textLabel.AutoSize = true; this.textLabel.Dock = System.Windows.Forms.DockStyle.Fill; this.textLabel.Location = new System.Drawing.Point(0, 0); this.textLabel.Name = "textLabel"; this.textLabel.Size = new System.Drawing.Size(38, 15); this.textLabel.TabIndex = 0; this.textLabel.Text = "Touch anywhere in this window."; // // MainForm // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(800, 450); this.Controls.Add(this.textLabel); this.Name = "MainForm"; this.Text = "Digitizer Example"; this.ResumeLayout(false); this.PerformLayout(); } #endregion private Label textLabel; } ================================================ FILE: RawInput.Sharp.DigitizerExample/MainForm.cs ================================================ using Linearstar.Windows.RawInput; namespace RawInput.Sharp.DigitizerExample; public partial class MainForm : Form { public MainForm() { InitializeComponent(); } protected override void OnShown(EventArgs e) { base.OnShown(e); RawInputDevice.RegisterDevice(HidUsageAndPage.Pen, RawInputDeviceFlags.None, this.Handle); RawInputDevice.RegisterDevice(HidUsageAndPage.TouchScreen, RawInputDeviceFlags.None, this.Handle); RawInputDevice.RegisterDevice(HidUsageAndPage.TouchPad, RawInputDeviceFlags.None, this.Handle); } protected override void WndProc(ref Message m) { const int WM_INPUT = 0x00FF; if (m.Msg == WM_INPUT) { var data = RawInputData.FromHandle(m.LParam); if (data is RawInputDigitizerData digitizerData) { var contacts = digitizerData.Contacts; this.textLabel.Text = $"Touch anywhere in this window.\r\n\r\nContacts: {digitizerData.ContactsCount}\r\n" + string.Join("\r\n", contacts.Select(x => x.ToString())); } } base.WndProc(ref m); } } ================================================ FILE: RawInput.Sharp.DigitizerExample/MainForm.resx ================================================  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 ================================================ FILE: RawInput.Sharp.DigitizerExample/Program.cs ================================================ namespace RawInput.Sharp.DigitizerExample; static class Program { /// /// The main entry point for the application. /// [STAThread] static void Main() { // To customize application configuration such as set high DPI settings or default font, // see https://aka.ms/applicationconfiguration. ApplicationConfiguration.Initialize(); Application.Run(new MainForm()); } } ================================================ FILE: RawInput.Sharp.DigitizerExample/RawInput.Sharp.DigitizerExample.csproj ================================================  WinExe net6.0-windows enable true enable ================================================ FILE: RawInput.Sharp.SimpleExample/Program.cs ================================================ using System; using System.Linq; using System.Windows.Forms; using Linearstar.Windows.RawInput; using RawInput.Sharp.SimpleExample; // Get the devices that can be handled with Raw Input. var devices = RawInputDevice.GetDevices(); // Keyboards will be returned as a RawInputKeyboard. var keyboards = devices.OfType(); // List them up. foreach (var device in keyboards) Console.WriteLine( $"{device.DeviceType} {device.VendorId:X4}:{device.ProductId:X4} {device.ProductName}, {device.ManufacturerName}"); // To begin catching inputs, first make a window that listens WM_INPUT. var window = new RawInputReceiverWindow(); window.Input += (sender, e) => { // Catch your input here! var data = e.Data; Console.WriteLine(data); }; try { // Register the HidUsageAndPage to watch any device. RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.ExInputSink | RawInputDeviceFlags.NoLegacy, window.Handle); RawInputDevice.RegisterDevice(HidUsageAndPage.Mouse, RawInputDeviceFlags.ExInputSink | RawInputDeviceFlags.NoLegacy, window.Handle); Application.Run(); } finally { RawInputDevice.UnregisterDevice(HidUsageAndPage.Keyboard); } ================================================ FILE: RawInput.Sharp.SimpleExample/RawInput.Sharp.SimpleExample.csproj ================================================  net6.0-windows Exe false true enable ================================================ FILE: RawInput.Sharp.SimpleExample/RawInputEventArgs.cs ================================================ using System; using Linearstar.Windows.RawInput; namespace RawInput.Sharp.SimpleExample; class RawInputEventArgs : EventArgs { public RawInputEventArgs(RawInputData data) { Data = data; } public RawInputData Data { get; } } ================================================ FILE: RawInput.Sharp.SimpleExample/RawInputReceiverWindow.cs ================================================ using System; using System.Windows.Forms; using Linearstar.Windows.RawInput; namespace RawInput.Sharp.SimpleExample; sealed class RawInputReceiverWindow : NativeWindow { public event EventHandler? Input; public RawInputReceiverWindow() { CreateHandle(new CreateParams { X = 0, Y = 0, Width = 0, Height = 0, Style = 0x800000, }); } protected override void WndProc(ref Message m) { const int WM_INPUT = 0x00FF; if (m.Msg == WM_INPUT) { var data = RawInputData.FromHandle(m.LParam); Input?.Invoke(this, new RawInputEventArgs(data)); } base.WndProc(ref m); } } ================================================ FILE: RawInput.Sharp.SimpleExample/app.config ================================================ ================================================ FILE: RawInput.Sharp.SimpleExample.WPF/App.xaml ================================================  ================================================ FILE: RawInput.Sharp.SimpleExample.WPF/App.xaml.cs ================================================ using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Threading.Tasks; using System.Windows; namespace RawInput.Sharp.SimpleExample.WPF { /// /// Interaction logic for App.xaml /// public partial class App : Application { } } ================================================ FILE: RawInput.Sharp.SimpleExample.WPF/AssemblyInfo.cs ================================================ using System.Windows; [assembly: ThemeInfo( ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located //(used if a resource is not found in the page, // or application resource dictionaries) ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located //(used if a resource is not found in the page, // app, or any theme specific resource dictionaries) )] ================================================ FILE: RawInput.Sharp.SimpleExample.WPF/MainWindow.xaml ================================================  ================================================ FILE: RawInput.Sharp.SimpleExample.WPF/MainWindow.xaml.cs ================================================ using System; using System.Diagnostics; using System.Windows; using System.Windows.Interop; using Linearstar.Windows.RawInput; namespace RawInput.Sharp.SimpleExample.WPF { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); SourceInitialized += MainWindow_SourceInitialized; } private void MainWindow_SourceInitialized(object? sender, EventArgs e) { var windowInteropHelper = new WindowInteropHelper(this); var hwnd = windowInteropHelper.Handle; // Get the devices that can be handled with Raw Input. var devices = RawInputDevice.GetDevices(); // register the keyboard device and you can register device which you need like mouse RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.ExInputSink | RawInputDeviceFlags.NoLegacy, hwnd); HwndSource source = HwndSource.FromHwnd(hwnd); source.AddHook(Hook); } private IntPtr Hook(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam, ref bool handled) { const int WM_INPUT = 0x00FF; // You can read inputs by processing the WM_INPUT message. if (msg == WM_INPUT) { // Create an RawInputData from the handle stored in lParam. var data = RawInputData.FromHandle(lparam); // You can identify the source device using Header.DeviceHandle or just Device. var sourceDeviceHandle = data.Header.DeviceHandle; var sourceDevice = data.Device; // The data will be an instance of either RawInputMouseData, RawInputKeyboardData, or RawInputHidData. // They contain the raw input data in their properties. switch (data) { case RawInputMouseData mouse: Debug.WriteLine(mouse.Mouse); break; case RawInputKeyboardData keyboard: Debug.WriteLine(keyboard.Keyboard); break; case RawInputHidData hid: Debug.WriteLine(hid.Hid); break; } } return IntPtr.Zero; } } } ================================================ FILE: RawInput.Sharp.SimpleExample.WPF/RawInput.Sharp.SimpleExample.WPF.csproj ================================================  WinExe net6.0-windows enable true ================================================ FILE: RawInput.Sharp.SimpleExample.Win32/Program.cs ================================================ using Linearstar.Windows.RawInput; using System.ComponentModel; using System.Runtime.InteropServices; var WindowClass = "HelperWindowClass"; var wind_class = new WNDCLASS { lpszClassName = Marshal.StringToHGlobalUni(WindowClass), lpfnWndProc = (hWnd, msg, wParam, lParam) => { const int WM_INPUT = 0x00FF; // You can read inputs by processing the WM_INPUT message. if (msg == WM_INPUT) { // Create an RawInputData from the handle stored in lParam. var data = RawInputData.FromHandle(lParam); // You can identify the source device using Header.DeviceHandle or just Device. var sourceDeviceHandle = data.Header.DeviceHandle; var sourceDevice = data.Device; // The data will be an instance of either RawInputMouseData, RawInputKeyboardData, or RawInputHidData. // They contain the raw input data in their properties. switch (data) { case RawInputMouseData mouse: Console.WriteLine(mouse.Mouse); break; case RawInputKeyboardData keyboard: Console.WriteLine(keyboard.Keyboard); break; case RawInputHidData hid: Console.WriteLine(hid.Hid); break; } } // The normal way to quit is sending WM_CLOSE message to the window // if (msg == 0x0002) { // WM_DESTORY // PostQuitMessage(0); // return nint.Zero; // } // handle the messages here return DefWindowProc(hWnd, msg, wParam, lParam); } }; ushort classAtom = RegisterClassW(ref wind_class); if (classAtom == 0) throw new Win32Exception(); const uint WS_EX_NOACTIVATE = 0x08000000; const uint WS_POPUP = 0x80000000; IntPtr hWnd = CreateWindowExW( WS_EX_NOACTIVATE, WindowClass, "", WS_POPUP, 0, 0, 0, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero ); if (hWnd == IntPtr.Zero) throw new Win32Exception(); // Get the devices that can be handled with Raw Input. var devices = RawInputDevice.GetDevices(); // register the keyboard device and you can register device which you need like mouse RawInputDevice.RegisterDevice(HidUsageAndPage.Keyboard, RawInputDeviceFlags.ExInputSink | RawInputDeviceFlags.NoLegacy, hWnd); // Message loop while (GetMessage(out var msg, IntPtr.Zero, 0, 0)) { TranslateMessage(msg); DispatchMessage(msg); } [DllImport("user32.dll")] static extern bool GetMessage(out IntPtr lpMsg, IntPtr hWnd, uint wMsgFilterMin, uint wMsgFilterMax); [DllImport("user32.dll")] static extern bool TranslateMessage(in IntPtr lpMsg); [DllImport("user32.dll")] static extern IntPtr DispatchMessage(in IntPtr lpMsg); [DllImport("user32.dll", SetLastError = true)] static extern ushort RegisterClassW([In] ref WNDCLASS lpWndClass); [DllImport("user32.dll", SetLastError = true)] static extern IntPtr CreateWindowExW(uint dwExStyle, [MarshalAs(UnmanagedType.LPWStr)] string lpClassName, [MarshalAs(UnmanagedType.LPWStr)] string lpWindowName, uint dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam); [DllImport("user32.dll")] static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam); delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); [StructLayout(LayoutKind.Sequential)] struct WNDCLASS { public uint style; public WndProc lpfnWndProc; public int cbClsExtra; public int cbWndExtra; public IntPtr hInstance; public IntPtr hIcon; public IntPtr hCursor; public IntPtr hbrBackground; public string lpszMenuName; public nint lpszClassName; } ================================================ FILE: RawInput.Sharp.SimpleExample.Win32/RawInput.Sharp.SimpleExample.Win32.csproj ================================================  Exe net8.0-windows enable enable true true ================================================ FILE: RawInput.Sharp.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.7.33920.267 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RawInput.Sharp", "RawInput.Sharp\RawInput.Sharp.csproj", "{47C40BEA-5191-46A3-BE27-4B7A1B725C31}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8B433D11-9752-4F95-A83A-6C04CB02CE07}" ProjectSection(SolutionItems) = preProject LICENSE.txt = LICENSE.txt README.md = README.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RawInput.Sharp.SimpleExample", "RawInput.Sharp.SimpleExample\RawInput.Sharp.SimpleExample.csproj", "{F129A5A1-95EA-41CF-98E3-ECB61BEB5040}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RawInput.Sharp.SimpleExample.WPF", "RawInput.Sharp.SimpleExample.WPF\RawInput.Sharp.SimpleExample.WPF.csproj", "{63B773C2-7995-4A6A-AAE8-C0DEF73454F9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RawInput.Sharp.DigitizerExample", "RawInput.Sharp.DigitizerExample\RawInput.Sharp.DigitizerExample.csproj", "{59C646E7-8E08-4B06-9F2E-B66962448BA2}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RawInput.Sharp.SimpleExample.Win32", "RawInput.Sharp.SimpleExample.Win32\RawInput.Sharp.SimpleExample.Win32.csproj", "{E3FBC4E2-70F1-447A-980B-0351E9D525A7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {47C40BEA-5191-46A3-BE27-4B7A1B725C31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {47C40BEA-5191-46A3-BE27-4B7A1B725C31}.Debug|Any CPU.Build.0 = Debug|Any CPU {47C40BEA-5191-46A3-BE27-4B7A1B725C31}.Release|Any CPU.ActiveCfg = Release|Any CPU {47C40BEA-5191-46A3-BE27-4B7A1B725C31}.Release|Any CPU.Build.0 = Release|Any CPU {F129A5A1-95EA-41CF-98E3-ECB61BEB5040}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F129A5A1-95EA-41CF-98E3-ECB61BEB5040}.Debug|Any CPU.Build.0 = Debug|Any CPU {F129A5A1-95EA-41CF-98E3-ECB61BEB5040}.Release|Any CPU.ActiveCfg = Release|Any CPU {F129A5A1-95EA-41CF-98E3-ECB61BEB5040}.Release|Any CPU.Build.0 = Release|Any CPU {63B773C2-7995-4A6A-AAE8-C0DEF73454F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {63B773C2-7995-4A6A-AAE8-C0DEF73454F9}.Debug|Any CPU.Build.0 = Debug|Any CPU {63B773C2-7995-4A6A-AAE8-C0DEF73454F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {63B773C2-7995-4A6A-AAE8-C0DEF73454F9}.Release|Any CPU.Build.0 = Release|Any CPU {59C646E7-8E08-4B06-9F2E-B66962448BA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {59C646E7-8E08-4B06-9F2E-B66962448BA2}.Debug|Any CPU.Build.0 = Debug|Any CPU {59C646E7-8E08-4B06-9F2E-B66962448BA2}.Release|Any CPU.ActiveCfg = Release|Any CPU {59C646E7-8E08-4B06-9F2E-B66962448BA2}.Release|Any CPU.Build.0 = Release|Any CPU {E3FBC4E2-70F1-447A-980B-0351E9D525A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E3FBC4E2-70F1-447A-980B-0351E9D525A7}.Debug|Any CPU.Build.0 = Debug|Any CPU {E3FBC4E2-70F1-447A-980B-0351E9D525A7}.Release|Any CPU.ActiveCfg = Release|Any CPU {E3FBC4E2-70F1-447A-980B-0351E9D525A7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {12746CE4-3274-4D3F-A053-FBC6E4D452A4} EndGlobalSection EndGlobal ================================================ FILE: renovate.json ================================================ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" ] }