diff --git a/MiController.sln b/MiController.sln index 3e7278f..9b4ffa2 100644 --- a/MiController.sln +++ b/MiController.sln @@ -1,18 +1,24 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.30503.244 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32112.339 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiController", "MiController\MiController.csproj", "{AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Debug|Any CPU.Build.0 = Debug|Any CPU {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Debug|x64.ActiveCfg = Debug|Any CPU {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Debug|x64.Build.0 = Debug|Any CPU + {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Release|Any CPU.Build.0 = Release|Any CPU {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Release|x64.ActiveCfg = Release|Any CPU {AA86CCD2-39BC-4CEC-8E9C-29E3AB9E1C24}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection diff --git a/MiController/ControllerHelper.cs b/MiController/ControllerHelper.cs deleted file mode 100644 index ed388ff..0000000 --- a/MiController/ControllerHelper.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System; -using System.ComponentModel; -using System.Linq; -using HidLibrary; -using ScpDriverInterface; -using System.Threading; -using System.Runtime.InteropServices; - -namespace MiController -{ - public class ControllerHelper - { - private static ScpBus _globalScpBus; - static bool ConsoleEventCallback(int eventType) - { - if (eventType == 2) - { - _globalScpBus.UnplugAll(); - } - return false; - } - static ConsoleEventDelegate _handler; // Keeps it from getting garbage collected - // Pinvoke - private delegate bool ConsoleEventDelegate(int eventType); - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add); - - - public static void Run(BackgroundWorker bw) - { - ScpBus scpBus = new ScpBus(); - scpBus.UnplugAll(); - _globalScpBus = scpBus; - - _handler = ConsoleEventCallback; - SetConsoleCtrlHandler(_handler, true); - - Thread.Sleep(400); - - XiaomiGamepad[] gamepads = new XiaomiGamepad[4]; - int index = 1; - var compatibleDevices = HidDevices.Enumerate(0x2717, 0x3144).ToList(); - foreach (var deviceInstance in compatibleDevices) - { - Console.WriteLine(deviceInstance); - HidDevice device = deviceInstance; - try - { - device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.ShareRead | ShareMode.ShareWrite); - } - catch - { - bw.ReportProgress(0, "Could not open gamepad in exclusive mode. Try re-enable device."); - var instanceId = DevicePathToInstanceId(deviceInstance.DevicePath); - if (TryReEnableDevice(instanceId)) - { - try - { - device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.Exclusive); - bw.ReportProgress(0, "Opened in exclusive mode."); - } - catch - { - device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.ShareRead | ShareMode.ShareWrite); - bw.ReportProgress(0, "Opened in shared mode."); - } - } - else - { - device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.ShareRead | ShareMode.ShareWrite); - bw.ReportProgress(0, "Opened in shared mode."); - } - } - - byte[] vibration = { 0x20, 0x00, 0x00 }; - if (device.WriteFeatureData(vibration) == false) - { - bw.ReportProgress(0, "Could not write to gamepad (is it closed?), skipping"); - device.CloseDevice(); - continue; - } - - device.ReadSerialNumber(out _); - device.ReadProduct(out _); - - - gamepads[index - 1] = new XiaomiGamepad(device, scpBus, index); - ++index; - - if (index >= 5) - { - break; - } - } - bw.ReportProgress(0, $"{index - 1} controllers connected"); - - while (!bw.CancellationPending) - { - Thread.Sleep(1000); - } - } - - private static bool TryReEnableDevice(string deviceInstanceId) - { - try - { - bool success; - Guid hidGuid = new Guid(); - HidLibrary.NativeMethods.HidD_GetHidGuid(ref hidGuid); - IntPtr deviceInfoSet = HidLibrary.NativeMethods.SetupDiGetClassDevs(ref hidGuid, deviceInstanceId, 0, HidLibrary.NativeMethods.DIGCF_PRESENT | HidLibrary.NativeMethods.DIGCF_DEVICEINTERFACE); - HidLibrary.NativeMethods.SP_DEVINFO_DATA deviceInfoData = new HidLibrary.NativeMethods.SP_DEVINFO_DATA(); - deviceInfoData.cbSize = Marshal.SizeOf(deviceInfoData); - success = HidLibrary.NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 0, ref deviceInfoData); - if (!success) - { - Console.WriteLine("Error getting device info data, error code = " + Marshal.GetLastWin32Error()); - } - success = HidLibrary.NativeMethods.SetupDiEnumDeviceInfo(deviceInfoSet, 1, ref deviceInfoData); // Checks that we have a unique device - if (success) - { - Console.WriteLine("Can't find unique device"); - } - - HidLibrary.NativeMethods.SP_PROPCHANGE_PARAMS propChangeParams = new HidLibrary.NativeMethods.SP_PROPCHANGE_PARAMS(); - propChangeParams.classInstallHeader.cbSize = Marshal.SizeOf(propChangeParams.classInstallHeader); - propChangeParams.classInstallHeader.installFunction = HidLibrary.NativeMethods.DIF_PROPERTYCHANGE; - propChangeParams.stateChange = HidLibrary.NativeMethods.DICS_DISABLE; - propChangeParams.scope = HidLibrary.NativeMethods.DICS_FLAG_GLOBAL; - propChangeParams.hwProfile = 0; - success = HidLibrary.NativeMethods.SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInfoData, ref propChangeParams, Marshal.SizeOf(propChangeParams)); - if (!success) - { - Console.WriteLine("Error setting class install params, error code = " + Marshal.GetLastWin32Error()); - return false; - } - success = HidLibrary.NativeMethods.SetupDiCallClassInstaller(HidLibrary.NativeMethods.DIF_PROPERTYCHANGE, deviceInfoSet, ref deviceInfoData); - if (!success) - { - Console.WriteLine("Error disabling device, error code = " + Marshal.GetLastWin32Error()); - return false; - - } - propChangeParams.stateChange = HidLibrary.NativeMethods.DICS_ENABLE; - success = HidLibrary.NativeMethods.SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInfoData, ref propChangeParams, Marshal.SizeOf(propChangeParams)); - if (!success) - { - Console.WriteLine("Error setting class install params, error code = " + Marshal.GetLastWin32Error()); - return false; - } - success = HidLibrary.NativeMethods.SetupDiCallClassInstaller(HidLibrary.NativeMethods.DIF_PROPERTYCHANGE, deviceInfoSet, ref deviceInfoData); - if (!success) - { - Console.WriteLine("Error enabling device, error code = " + Marshal.GetLastWin32Error()); - return false; - } - - HidLibrary.NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); - - return true; - } - catch - { - Console.WriteLine("Can't reenable device"); - return false; - } - } - - private static string DevicePathToInstanceId(string devicePath) - { - string deviceInstanceId = devicePath; - deviceInstanceId = deviceInstanceId.Remove(0, deviceInstanceId.LastIndexOf('\\') + 1); - deviceInstanceId = deviceInstanceId.Remove(deviceInstanceId.LastIndexOf('{')); - deviceInstanceId = deviceInstanceId.Replace('#', '\\'); - if (deviceInstanceId.EndsWith("\\")) - { - deviceInstanceId = deviceInstanceId.Remove(deviceInstanceId.Length - 1); - } - return deviceInstanceId; - } - } -} diff --git a/MiController/DriverSetup.cs b/MiController/DriverSetup.cs deleted file mode 100644 index b9c4949..0000000 --- a/MiController/DriverSetup.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using MiController.DriverUtils; - -namespace MiController -{ - public class DriverSetup - { - private static readonly Guid Ds3BusClassGuid = new Guid("f679f562-3164-42ce-a4db-e7ddbe723909"); - public static void Install() - { - string driverDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty, "ScpDriver"); - string devPath = string.Empty; - string instanceId = string.Empty; - - var flags = DifxFlags.DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT; - - //if (force) - // flags |= DifxFlags.DRIVER_PACKAGE_FORCE; - - - if (!Devcon.Find(Ds3BusClassGuid, ref devPath, ref instanceId)) - { - bool virtualBusCreated = Devcon.Create("System", new Guid("{4D36E97D-E325-11CE-BFC1-08002BE10318}"), - "root\\ScpVBus\0\0"); - } - - var installer = Difx.Factory(driverDir); - uint result = installer.Install(Path.Combine(driverDir, @"ScpVBus.inf"), flags, out var rebootRequired); - - - if (result != 0) - { - Uninstall(); - throw new Exception("Driver installation failed. Error code: " + result); - } - } - - public static void Uninstall() - { - string driverDir = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty, "ScpDriver"); - - string devPath = string.Empty; - string instanceId = string.Empty; - - if (!Devcon.Find(Ds3BusClassGuid, ref devPath, ref instanceId)) - { - Devcon.Remove(Ds3BusClassGuid, devPath, instanceId); - } - - var installer = Difx.Factory(driverDir); - installer.Uninstall(Path.Combine(driverDir, @"ScpVBus.inf"), - DifxFlags.DRIVER_PACKAGE_DELETE_FILES, - out var rebootRequired); - } - } -} diff --git a/MiController/DriverUtils/Devcon.cs b/MiController/DriverUtils/Devcon.cs deleted file mode 100644 index 235c331..0000000 --- a/MiController/DriverUtils/Devcon.cs +++ /dev/null @@ -1,242 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace MiController.DriverUtils -{ - public class Devcon - { - public static bool Find(Guid target, ref string path, ref string instanceId, int instance = 0) - { - var deviceInfoSet = IntPtr.Zero; - - try - { - SP_DEVINFO_DATA deviceInterfaceData = new SP_DEVINFO_DATA(), da = new SP_DEVINFO_DATA(); - int bufferSize = 0, memberIndex = 0; - - deviceInfoSet = SetupDiGetClassDevs(ref target, IntPtr.Zero, IntPtr.Zero, - DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - deviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(deviceInterfaceData); - - while (SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref target, memberIndex, - ref deviceInterfaceData)) - { - SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, IntPtr.Zero, 0, - ref bufferSize, ref da); - { - var detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, - (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, detailDataBuffer, - bufferSize, ref bufferSize, ref da)) - { - var pDevicePathName = detailDataBuffer + 4; - - path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(); - - if (memberIndex == instance) - { - var nBytes = 256; - var ptrInstanceBuf = Marshal.AllocHGlobal(nBytes); - - CM_Get_Device_ID(da.Flags, ptrInstanceBuf, nBytes, 0); - instanceId = Marshal.PtrToStringAuto(ptrInstanceBuf).ToUpper(); - - Marshal.FreeHGlobal(ptrInstanceBuf); - return true; - } - } - else Marshal.FreeHGlobal(detailDataBuffer); - } - - memberIndex++; - } - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - public static bool Create(string className, Guid classGuid, string node) - { - var deviceInfoSet = (IntPtr)(-1); - var deviceInfoData = new SP_DEVINFO_DATA(); - - try - { - deviceInfoSet = SetupDiCreateDeviceInfoList(ref classGuid, IntPtr.Zero); - - if (deviceInfoSet == (IntPtr)(-1)) - { - return false; - } - - deviceInfoData.cbSize = Marshal.SizeOf(deviceInfoData); - - if ( - !SetupDiCreateDeviceInfo(deviceInfoSet, className, ref classGuid, null, IntPtr.Zero, - DICD_GENERATE_ID, ref deviceInfoData)) - { - return false; - } - - if ( - !SetupDiSetDeviceRegistryProperty(deviceInfoSet, ref deviceInfoData, SPDRP_HARDWAREID, node, - node.Length * 2)) - { - return false; - } - - if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, deviceInfoSet, ref deviceInfoData)) - { - return false; - } - } - finally - { - if (deviceInfoSet != (IntPtr)(-1)) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return true; - } - - public static bool Remove(Guid classGuid, string path, string instanceId) - { - var deviceInfoSet = IntPtr.Zero; - - try - { - var deviceInterfaceData = new SP_DEVINFO_DATA(); - - deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); - deviceInfoSet = SetupDiGetClassDevs(ref classGuid, IntPtr.Zero, IntPtr.Zero, - DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - if (SetupDiOpenDeviceInfo(deviceInfoSet, instanceId, IntPtr.Zero, 0, ref deviceInterfaceData)) - { - var props = new SP_REMOVEDEVICE_PARAMS {ClassInstallHeader = new SP_CLASSINSTALL_HEADER()}; - - props.ClassInstallHeader.cbSize = Marshal.SizeOf(props.ClassInstallHeader); - props.ClassInstallHeader.InstallFunction = DIF_REMOVE; - - props.Scope = DI_REMOVEDEVICE_GLOBAL; - props.HwProfile = 0x00; - - if (SetupDiSetClassInstallParams(deviceInfoSet, ref deviceInterfaceData, ref props, - Marshal.SizeOf(props))) - { - return SetupDiCallClassInstaller(DIF_REMOVE, deviceInfoSet, ref deviceInterfaceData); - } - } - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - #region Constant and Structure Definitions - - protected const int DIGCF_PRESENT = 0x0002; - protected const int DIGCF_DEVICEINTERFACE = 0x0010; - - protected const int DICD_GENERATE_ID = 0x0001; - protected const int SPDRP_HARDWAREID = 0x0001; - - protected const int DIF_REMOVE = 0x0005; - protected const int DIF_REGISTERDEVICE = 0x0019; - - protected const int DI_REMOVEDEVICE_GLOBAL = 0x0001; - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_DEVINFO_DATA - { - internal int cbSize; - internal Guid ClassGuid; - internal int Flags; - internal IntPtr Reserved; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_CLASSINSTALL_HEADER - { - internal int cbSize; - internal int InstallFunction; - } - - [StructLayout(LayoutKind.Sequential)] - protected struct SP_REMOVEDEVICE_PARAMS - { - internal SP_CLASSINSTALL_HEADER ClassInstallHeader; - internal int Scope; - internal int HwProfile; - } - - #endregion - - #region Interop Definitions - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern IntPtr SetupDiCreateDeviceInfoList(ref Guid ClassGuid, IntPtr hwndParent); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiCreateDeviceInfo(IntPtr DeviceInfoSet, string DeviceName, ref Guid ClassGuid, - string DeviceDescription, IntPtr hwndParent, int CreationFlags, ref SP_DEVINFO_DATA DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiSetDeviceRegistryProperty(IntPtr DeviceInfoSet, - ref SP_DEVINFO_DATA DeviceInfoData, int Property, [MarshalAs(UnmanagedType.LPWStr)] string PropertyBuffer, - int PropertyBufferSize); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiCallClassInstaller(int InstallFunction, IntPtr DeviceInfoSet, - ref SP_DEVINFO_DATA DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, - int Flags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, - ref Guid InterfaceClassGuid, int MemberIndex, ref SP_DEVINFO_DATA DeviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, - ref SP_DEVINFO_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, int DeviceInterfaceDetailDataSize, - ref int RequiredSize, ref SP_DEVINFO_DATA DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern int CM_Get_Device_ID(int DevInst, IntPtr Buffer, int BufferLen, int Flags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiOpenDeviceInfo(IntPtr DeviceInfoSet, string DeviceInstanceId, - IntPtr hwndParent, int Flags, ref SP_DEVINFO_DATA DeviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - protected static extern bool SetupDiSetClassInstallParams(IntPtr DeviceInfoSet, - ref SP_DEVINFO_DATA DeviceInterfaceData, ref SP_REMOVEDEVICE_PARAMS ClassInstallParams, - int ClassInstallParamsSize); - - #endregion - } -} \ No newline at end of file diff --git a/MiController/DriverUtils/Difx.cs b/MiController/DriverUtils/Difx.cs deleted file mode 100644 index 2188013..0000000 --- a/MiController/DriverUtils/Difx.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace MiController.DriverUtils -{ - [Flags] - public enum DifxFlags - { - DRIVER_PACKAGE_REPAIR = 0x00000001, - DRIVER_PACKAGE_SILENT = 0x00000002, - DRIVER_PACKAGE_FORCE = 0x00000004, - DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT = 0x00000008, - DRIVER_PACKAGE_LEGACY_MODE = 0x00000010, - DRIVER_PACKAGE_DELETE_FILES = 0x00000020, - } - - public enum DifxLog - { - DIFXAPI_SUCCESS = 0, - DIFXAPI_INFO = 1, - DIFXAPI_WARNING = 2, - DIFXAPI_ERROR = 3, - } - - /// - /// Driver Install Frameworks API (DIFxAPI) - /// - public class Difx - { - public delegate void DIFLOGCALLBACK( - DifxLog EventType, - Int32 ErrorCode, - [MarshalAs(UnmanagedType.LPTStr)] String EventDescription, - IntPtr CallbackContext - ); - - public void Logger( - DifxLog EventType, - Int32 ErrorCode, - [MarshalAs(UnmanagedType.LPTStr)] String EventDescription, - IntPtr CallbackContext) - { - if (onLogEvent != null) onLogEvent(EventType, ErrorCode, EventDescription); - } - - protected DIFLOGCALLBACK m_LogCallback; - - public delegate void LogEventHandler(DifxLog Event, Int32 Error, String Description); - public LogEventHandler onLogEvent; - - protected Difx() - { - m_LogCallback = new DIFLOGCALLBACK(Logger); - } - - public virtual UInt32 Preinstall(String InfPath, DifxFlags Flags) - { - return 0xFFFFFFFF; - } - - public virtual UInt32 Install(String InfPath, DifxFlags Flags, out Boolean RebootRequired) - { - RebootRequired = false; - return 0xFFFFFFFF; - } - - public virtual UInt32 Uninstall(String InfPath, DifxFlags Flags, out Boolean RebootRequired) - { - RebootRequired = false; - return 0xFFFFFFFF; - } - - public static Difx Factory(string driverDir) - { - Difx retVal; - Environment.CurrentDirectory = driverDir; - if (Environment.Is64BitProcess) - { - retVal = new Difx64(); - } - else - { - retVal = new Difx32(); - } - - return retVal; - } - } - - /// - /// Driver Install Frameworks API (DIFxAPI) for x86 platform. - /// - public class Difx32 : Difx - { - [DllImport(@".\x86\DIFxAPI.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)] - private static extern UInt32 DriverPackagePreinstall( - [MarshalAs(UnmanagedType.LPTStr)] String DriverPackageInfPath, - [MarshalAs(UnmanagedType.U4)] UInt32 Flags - ); - - [DllImport(@".\x86\DIFxAPI.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)] - private static extern UInt32 DriverPackageInstall( - [MarshalAs(UnmanagedType.LPTStr)] String DriverPackageInfPath, - [MarshalAs(UnmanagedType.U4)] UInt32 Flags, - IntPtr pInstallerInfo, - [MarshalAs(UnmanagedType.Bool)] out Boolean pNeedReboot - ); - - [DllImport(@".\x86\DIFxAPI.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)] - private static extern UInt32 DriverPackageUninstall( - [MarshalAs(UnmanagedType.LPTStr)] String DriverPackageInfPath, - [MarshalAs(UnmanagedType.U4)] UInt32 Flags, - IntPtr pInstallerInfo, - [MarshalAs(UnmanagedType.Bool)] out Boolean pNeedReboot - ); - - [DllImport(@".\x86\DIFxAPI.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern void SetDifxLogCallback(DIFLOGCALLBACK LogCallback, IntPtr CallbackContext); - - public Difx32() - { - SetDifxLogCallback(m_LogCallback, IntPtr.Zero); - } - - public override UInt32 Preinstall(String InfPath, DifxFlags Flags) - { - return DriverPackagePreinstall(InfPath, (UInt32)Flags); - } - - public override UInt32 Install(String InfPath, DifxFlags Flags, out Boolean RebootRequired) - { - return DriverPackageInstall(InfPath, (UInt32)Flags, IntPtr.Zero, out RebootRequired); - } - - public override UInt32 Uninstall(String InfPath, DifxFlags Flags, out Boolean RebootRequired) - { - return DriverPackageUninstall(InfPath, (UInt32)Flags, IntPtr.Zero, out RebootRequired); - } - } - - /// - /// Driver Install Frameworks API (DIFxAPI) for x64 platform. - /// - public class Difx64 : Difx - { - [DllImport(@".\amd64\DIFxAPI.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)] - private static extern UInt32 DriverPackagePreinstall( - [MarshalAs(UnmanagedType.LPTStr)] String DriverPackageInfPath, - [MarshalAs(UnmanagedType.U4)] UInt32 Flags - ); - - [DllImport(@".\amd64\DIFxAPI.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)] - private static extern UInt32 DriverPackageInstall( - [MarshalAs(UnmanagedType.LPTStr)] String DriverPackageInfPath, - [MarshalAs(UnmanagedType.U4)] UInt32 Flags, - IntPtr pInstallerInfo, - [MarshalAs(UnmanagedType.Bool)] out Boolean pNeedReboot - ); - - [DllImport(@".\ScpDriver\amd64\DIFxAPI.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)] - private static extern UInt32 DriverPackageUninstall( - [MarshalAs(UnmanagedType.LPTStr)] String DriverPackageInfPath, - [MarshalAs(UnmanagedType.U4)] UInt32 Flags, - IntPtr pInstallerInfo, - [MarshalAs(UnmanagedType.Bool)] out Boolean pNeedReboot - ); - - [DllImport(@".\amd64\DIFxAPI.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern void SetDifxLogCallback(DIFLOGCALLBACK LogCallback, IntPtr CallbackContext); - - - public Difx64() - { - SetDifxLogCallback(m_LogCallback, IntPtr.Zero); - } - - public override UInt32 Preinstall(String InfPath, DifxFlags Flags) - { - return DriverPackagePreinstall(InfPath, (UInt32)Flags); - } - - public override UInt32 Install(String InfPath, DifxFlags Flags, out Boolean RebootRequired) - { - return DriverPackageInstall(InfPath, (UInt32)Flags, (IntPtr)0, out RebootRequired); - } - - public override UInt32 Uninstall(String InfPath, DifxFlags Flags, out Boolean RebootRequired) - { - return DriverPackageUninstall(InfPath, (UInt32)Flags, (IntPtr)0, out RebootRequired); - } - } -} diff --git a/MiController/HidLibrary/HidDevice.cs b/MiController/HidLibrary/HidDevice.cs index 48bd34d..09f753f 100644 --- a/MiController/HidLibrary/HidDevice.cs +++ b/MiController/HidLibrary/HidDevice.cs @@ -27,10 +27,6 @@ namespace HidLibrary private delegate bool WriteDelegate(byte[] data, int timeout); private delegate bool WriteReportDelegate(HidReport report, int timeout); - private NativeOverlapped read_overlapped; - private byte[] read_buffer; - private bool reading; - internal HidDevice(string devicePath, string description = null) { _deviceEventMonitor = new HidDeviceEventMonitor(this); @@ -106,10 +102,6 @@ namespace HidLibrary } IsOpen = Handle.ToInt32() != NativeMethods.INVALID_HANDLE_VALUE; - if (!IsOpen) - { - throw new Exception("Error opening HID device."); - } } @@ -127,7 +119,9 @@ namespace HidLibrary public HidDeviceData Read(int timeout) { - if (IsOpen == false) return new HidDeviceData(HidDeviceData.ReadStatus.ReadError); + if (IsConnected) + { + if (IsOpen == false) OpenDevice(_deviceReadMode, _deviceWriteMode, _deviceShareMode); try { return ReadData(timeout); @@ -136,6 +130,9 @@ namespace HidLibrary { return new HidDeviceData(HidDeviceData.ReadStatus.ReadError); } + + } + return new HidDeviceData(HidDeviceData.ReadStatus.NotConnected); } public void Read(ReadCallback callback) @@ -184,6 +181,22 @@ namespace HidLibrary return await Task.Factory.FromAsync(readReportDelegate.BeginInvoke, readReportDelegate.EndInvoke, timeout, null); } + /// + /// Reads an input report from the Control channel. This method provides access to report data for devices that + /// do not use the interrupt channel to communicate for specific usages. + /// + /// The report ID to read from the device + /// The HID report that is read. The report will contain the success status of the read request + /// + public HidReport ReadReportSync(byte reportId) + { + byte[] cmdBuffer = new byte[Capabilities.InputReportByteLength]; + cmdBuffer[0] = reportId; + bool bSuccess = NativeMethods.HidD_GetInputReport(Handle, cmdBuffer, cmdBuffer.Length); + HidDeviceData deviceData = new HidDeviceData(cmdBuffer, bSuccess ? HidDeviceData.ReadStatus.Success : HidDeviceData.ReadStatus.NoDataRead); + return new HidReport(Capabilities.InputReportByteLength, deviceData); + } + public bool ReadFeatureData(out byte[] data, byte reportId = 0) { if (_deviceCapabilities.FeatureReportByteLength <= 0) @@ -314,6 +327,8 @@ namespace HidLibrary public bool Write(byte[] data, int timeout) { + if (IsConnected) + { if (IsOpen == false) OpenDevice(_deviceReadMode, _deviceWriteMode, _deviceShareMode); try { @@ -323,6 +338,8 @@ namespace HidLibrary { return false; } + } + return false; } public void Write(byte[] data, WriteCallback callback) @@ -365,6 +382,25 @@ namespace HidLibrary writeReportDelegate.BeginInvoke(report, timeout, EndWriteReport, asyncState); } + /// + /// Handle data transfers on the control channel. This method places data on the control channel for devices + /// that do not support the interupt transfers + /// + /// The outbound HID report + /// The result of the tranfer request: true if successful otherwise false + /// + public bool WriteReportSync(HidReport report) + { + + if (null != report) + { + byte[] buffer = report.GetBytes(); + return (NativeMethods.HidD_SetOutputReport(Handle, buffer, buffer.Length)); + } + else + throw new ArgumentException("The output report is null, it must be allocated before you call this method", "report"); + } + public async Task WriteReportAsync(HidReport report, int timeout = 0) { var writeReportDelegate = new WriteReportDelegate(WriteReport); @@ -392,7 +428,7 @@ namespace HidLibrary if (IsOpen) hidHandle = Handle; else - return false; + hidHandle = OpenDeviceIO(_devicePath, NativeMethods.ACCESS_NONE); //var overlapped = new NativeOverlapped(); success = NativeMethods.HidD_SetFeature(hidHandle, buffer, buffer.Length); @@ -401,7 +437,11 @@ namespace HidLibrary { throw new Exception(string.Format("Error accessing HID device '{0}'.", _devicePath), exception); } - + finally + { + if (hidHandle != IntPtr.Zero && hidHandle != Handle) + CloseDeviceIO(hidHandle); + } return success; } @@ -547,58 +587,70 @@ namespace HidLibrary { var buffer = new byte[] { }; var status = HidDeviceData.ReadStatus.NoDataRead; + IntPtr nonManagedBuffer; if (_deviceCapabilities.InputReportByteLength > 0) { uint bytesRead = 0; - + buffer = CreateInputBuffer(); + nonManagedBuffer = Marshal.AllocHGlobal(buffer.Length); if (_deviceReadMode == DeviceMode.Overlapped) { - if (!reading) - { - read_buffer = CreateInputBuffer(); - var security = new NativeMethods.SECURITY_ATTRIBUTES(); - read_overlapped = new NativeOverlapped(); + var security = new NativeMethods.SECURITY_ATTRIBUTES(); + var overlapped = new NativeOverlapped(); + var overlapTimeout = timeout <= 0 ? NativeMethods.WAIT_INFINITE : timeout; - security.lpSecurityDescriptor = IntPtr.Zero; - security.bInheritHandle = true; - security.nLength = Marshal.SizeOf(security); + security.lpSecurityDescriptor = IntPtr.Zero; + security.bInheritHandle = true; + security.nLength = Marshal.SizeOf(security); - read_overlapped.OffsetLow = 0; - read_overlapped.OffsetHigh = 0; - read_overlapped.EventHandle = NativeMethods.CreateEvent(ref security, Convert.ToInt32(false), Convert.ToInt32(true), string.Empty); - NativeMethods.ReadFile(Handle, read_buffer, (uint)read_buffer.Length, out bytesRead, ref read_overlapped); - } + overlapped.OffsetLow = 0; + overlapped.OffsetHigh = 0; + overlapped.EventHandle = NativeMethods.CreateEvent(ref security, Convert.ToInt32(false), Convert.ToInt32(true), string.Empty); try { - var result = NativeMethods.WaitForSingleObject(read_overlapped.EventHandle, timeout); + var success = NativeMethods.ReadFile(Handle, nonManagedBuffer, (uint)buffer.Length, out bytesRead, ref overlapped); - switch (result) + if (success) { - case NativeMethods.WAIT_OBJECT_0: status = HidDeviceData.ReadStatus.Success; reading = false; CloseDeviceIO(read_overlapped.EventHandle); return new HidDeviceData(read_buffer, status); - case NativeMethods.WAIT_TIMEOUT: - status = HidDeviceData.ReadStatus.WaitTimedOut; - buffer = new byte[] { }; - reading = true; - break; - case NativeMethods.WAIT_FAILED: - status = HidDeviceData.ReadStatus.WaitFail; - buffer = new byte[] { }; - reading = false; - CloseDeviceIO(read_overlapped.EventHandle); - break; - default: - status = HidDeviceData.ReadStatus.NoDataRead; - buffer = new byte[] { }; - reading = false; - CloseDeviceIO(read_overlapped.EventHandle); - break; + status = HidDeviceData.ReadStatus.Success; // No check here to see if bytesRead > 0 . Perhaps not necessary? } + else + { + var result = NativeMethods.WaitForSingleObject(overlapped.EventHandle, overlapTimeout); + + switch (result) + { + case NativeMethods.WAIT_OBJECT_0: + status = HidDeviceData.ReadStatus.Success; + NativeMethods.GetOverlappedResult(Handle, ref overlapped, out bytesRead, false); + break; + case NativeMethods.WAIT_TIMEOUT: + status = HidDeviceData.ReadStatus.WaitTimedOut; + NativeMethods.CancelIo(Handle); + buffer = new byte[] { }; + break; + case NativeMethods.WAIT_FAILED: + status = HidDeviceData.ReadStatus.WaitFail; + buffer = new byte[] { }; + break; + default: + status = HidDeviceData.ReadStatus.NoDataRead; + buffer = new byte[] { }; + break; + } + } + Marshal.Copy(nonManagedBuffer, buffer, 0, (int)bytesRead); + } + catch { status = HidDeviceData.ReadStatus.ReadError; } + finally + { + CloseDeviceIO(overlapped.EventHandle); + Marshal.FreeHGlobal(nonManagedBuffer); } - catch { status = HidDeviceData.ReadStatus.ReadError; reading = false; CloseDeviceIO(read_overlapped.EventHandle); } } else { @@ -606,10 +658,12 @@ namespace HidLibrary { var overlapped = new NativeOverlapped(); - NativeMethods.ReadFile(Handle, buffer, (uint)buffer.Length, out bytesRead, ref overlapped); + NativeMethods.ReadFile(Handle, nonManagedBuffer, (uint)buffer.Length, out bytesRead, ref overlapped); status = HidDeviceData.ReadStatus.Success; + Marshal.Copy(nonManagedBuffer, buffer, 0, (int)bytesRead); } catch { status = HidDeviceData.ReadStatus.ReadError; } + finally { Marshal.FreeHGlobal(nonManagedBuffer); } } } return new HidDeviceData(buffer, status); diff --git a/MiController/HidLibrary/HidDeviceData.cs b/MiController/HidLibrary/HidDeviceData.cs index 5830fbe..22f61dc 100644 --- a/MiController/HidLibrary/HidDeviceData.cs +++ b/MiController/HidLibrary/HidDeviceData.cs @@ -13,10 +13,10 @@ } public HidDeviceData(ReadStatus status) - { - Data = new byte[] {}; - Status = status; - } + { + Data = new byte[] { }; + Status = status; + } public HidDeviceData(byte[] data, ReadStatus status) { diff --git a/MiController/HidLibrary/HidDevices.cs b/MiController/HidLibrary/HidDevices.cs index e66fc6e..d71f33c 100644 --- a/MiController/HidLibrary/HidDevices.cs +++ b/MiController/HidLibrary/HidDevices.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; +using System.Text; namespace HidLibrary { @@ -32,23 +33,31 @@ namespace HidLibrary public static IEnumerable Enumerate(int vendorId, params int[] productIds) { - return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId && + return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId && productIds.Contains(x.Attributes.ProductId)); } + public static IEnumerable Enumerate(int vendorId, int productId, ushort UsagePage) + { + return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId && + productId == (ushort)x.Attributes.ProductId && (ushort)x.Capabilities.UsagePage == UsagePage); + } + public static IEnumerable Enumerate(int vendorId) { return EnumerateDevices().Select(x => new HidDevice(x.Path, x.Description)).Where(x => x.Attributes.VendorId == vendorId); } - private class DeviceInfo { public string Path { get; set; } public string Description { get; set; } } + public class DeviceInfo { public string Path { get; set; } public string Description { get; set; } public string InstanceID { get; set; } } - private static IEnumerable EnumerateDevices() + public static IEnumerable EnumerateDevices() { var devices = new List(); var hidClass = HidClassGuid; var deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidClass, null, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE); + var buf = new char[1024]; + if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE) { var deviceInfoData = CreateDeviceInfoData(); @@ -58,6 +67,9 @@ namespace HidLibrary { deviceIndex += 1; + NativeMethods.SetupDiGetDeviceInstanceId(deviceInfoSet, ref deviceInfoData, buf, buf.Length, out var requiredSize); + var instid = new string(buf, 0, requiredSize - 1); + var deviceInterfaceData = new NativeMethods.SP_DEVICE_INTERFACE_DATA(); deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData); var deviceInterfaceIndex = 0; @@ -66,9 +78,9 @@ namespace HidLibrary { deviceInterfaceIndex++; var devicePath = GetDevicePath(deviceInfoSet, deviceInterfaceData); - var description = GetBusReportedDeviceDescription(deviceInfoSet, ref deviceInfoData) ?? + var description = GetBusReportedDeviceDescription(deviceInfoSet, ref deviceInfoData) ?? GetDeviceDescription(deviceInfoSet, ref deviceInfoData); - devices.Add(new DeviceInfo { Path = devicePath, Description = description }); + devices.Add(new DeviceInfo { Path = devicePath, Description = description, InstanceID = instid }); } } NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); @@ -95,7 +107,7 @@ namespace HidLibrary NativeMethods.SetupDiGetDeviceInterfaceDetailBuffer(deviceInfoSet, ref deviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, IntPtr.Zero); - return NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref interfaceDetail, bufferSize, ref bufferSize, IntPtr.Zero) ? + return NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref deviceInterfaceData, ref interfaceDetail, bufferSize, ref bufferSize, IntPtr.Zero) ? interfaceDetail.DevicePath : null; } diff --git a/MiController/HidLibrary/HidFastReadEnumerator.cs b/MiController/HidLibrary/HidFastReadEnumerator.cs new file mode 100644 index 0000000..d555330 --- /dev/null +++ b/MiController/HidLibrary/HidFastReadEnumerator.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; + +namespace HidLibrary +{ + public class HidFastReadEnumerator : IHidEnumerator + { + public bool IsConnected(string devicePath) + { + return HidDevices.IsConnected(devicePath); + } + + public IHidDevice GetDevice(string devicePath) + { + return Enumerate(devicePath).FirstOrDefault() as IHidDevice; + } + + public IEnumerable Enumerate() + { + return HidDevices.EnumerateDevices(). + Select(d => new HidFastReadDevice(d.Path, d.Description) as IHidDevice); + } + + public IEnumerable Enumerate(string devicePath) + { + return HidDevices.EnumerateDevices().Where(x => x.Path == devicePath). + Select(d => new HidFastReadDevice(d.Path, d.Description) as IHidDevice); + } + + public IEnumerable Enumerate(int vendorId, params int[] productIds) + { + return HidDevices.EnumerateDevices().Select(d => new HidFastReadDevice(d.Path, d.Description)). + Where(f => f.Attributes.VendorId == vendorId && productIds.Contains(f.Attributes.ProductId)). + Select(d => d as IHidDevice); + } + + public IEnumerable Enumerate(int vendorId) + { + return HidDevices.EnumerateDevices().Select(d => new HidFastReadDevice(d.Path, d.Description)). + Where(f => f.Attributes.VendorId == vendorId). + Select(d => d as IHidDevice); + } + } +} \ No newline at end of file diff --git a/MiController/HidLibrary/HidReport.cs b/MiController/HidLibrary/HidReport.cs index c550fd3..cbd9f49 100644 --- a/MiController/HidLibrary/HidReport.cs +++ b/MiController/HidLibrary/HidReport.cs @@ -5,7 +5,7 @@ namespace HidLibrary public class HidReport { private byte _reportId; - private byte[] _data = new byte[] {}; + private byte[] _data = new byte[] { }; private readonly HidDeviceData.ReadStatus _status; diff --git a/MiController/HidLibrary/IHidDevice.cs b/MiController/HidLibrary/IHidDevice.cs index d352c28..66f6cc6 100644 --- a/MiController/HidLibrary/IHidDevice.cs +++ b/MiController/HidLibrary/IHidDevice.cs @@ -34,7 +34,7 @@ namespace HidLibrary bool IsConnected { get; } string Description { get; } HidDeviceCapabilities Capabilities { get; } - HidDeviceAttributes Attributes { get; } + HidDeviceAttributes Attributes { get; } string DevicePath { get; } bool MonitorDeviceEvents { get; set; } @@ -42,7 +42,7 @@ namespace HidLibrary void OpenDevice(); void OpenDevice(DeviceMode readMode, DeviceMode writeMode, ShareMode shareMode); - + void CloseDevice(); HidDeviceData Read(); diff --git a/MiController/HidLibrary/NativeMethods.cs b/MiController/HidLibrary/NativeMethods.cs index 861c89c..fea85f1 100644 --- a/MiController/HidLibrary/NativeMethods.cs +++ b/MiController/HidLibrary/NativeMethods.cs @@ -4,369 +4,344 @@ using System.Runtime.InteropServices; namespace HidLibrary { internal static class NativeMethods - { - internal const int FILE_FLAG_OVERLAPPED = 0x40000000; - internal const short FILE_SHARE_READ = 0x1; - internal const short FILE_SHARE_WRITE = 0x2; - internal const uint GENERIC_READ = 0x80000000; - internal const uint GENERIC_WRITE = 0x40000000; - internal const int ACCESS_NONE = 0; - internal const int INVALID_HANDLE_VALUE = -1; - internal const short OPEN_EXISTING = 3; - internal const int WAIT_TIMEOUT = 0x102; - internal const uint WAIT_OBJECT_0 = 0; - internal const uint WAIT_FAILED = 0xffffffff; + { + internal const int FILE_FLAG_OVERLAPPED = 0x40000000; + internal const short FILE_SHARE_READ = 0x1; + internal const short FILE_SHARE_WRITE = 0x2; + internal const uint GENERIC_READ = 0x80000000; + internal const uint GENERIC_WRITE = 0x40000000; + internal const int ACCESS_NONE = 0; + internal const int INVALID_HANDLE_VALUE = -1; + internal const short OPEN_EXISTING = 3; + internal const int WAIT_TIMEOUT = 0x102; + internal const uint WAIT_OBJECT_0 = 0; + internal const uint WAIT_FAILED = 0xffffffff; - internal const int WAIT_INFINITE = 0xffff; - [StructLayout(LayoutKind.Sequential)] - internal struct OVERLAPPED - { - public int Internal; - public int InternalHigh; - public int Offset; - public int OffsetHigh; - public int hEvent; - } + internal const int WAIT_INFINITE = -1; + [StructLayout(LayoutKind.Sequential)] + internal struct OVERLAPPED + { + public int Internal; + public int InternalHigh; + public int Offset; + public int OffsetHigh; + public int hEvent; + } - [StructLayout(LayoutKind.Sequential)] - internal struct SECURITY_ATTRIBUTES - { - public int nLength; - public IntPtr lpSecurityDescriptor; - public bool bInheritHandle; - } + [StructLayout(LayoutKind.Sequential)] + internal struct SECURITY_ATTRIBUTES + { + public int nLength; + public IntPtr lpSecurityDescriptor; + public bool bInheritHandle; + } - [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - static internal extern bool CancelIo(IntPtr hFile); + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + static internal extern bool CancelIo(IntPtr hFile); - [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - static internal extern bool CancelIoEx(IntPtr hFile, IntPtr lpOverlapped); + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + static internal extern bool CancelIoEx(IntPtr hFile, IntPtr lpOverlapped); - [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - static internal extern bool CloseHandle(IntPtr hObject); + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + static internal extern bool CloseHandle(IntPtr hObject); - [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] - static internal extern bool CancelSynchronousIo(IntPtr hObject); + [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] + static internal extern bool CancelSynchronousIo(IntPtr hObject); - [DllImport("kernel32.dll", CharSet = CharSet.Auto)] - static internal extern IntPtr CreateEvent(ref SECURITY_ATTRIBUTES securityAttributes, int bManualReset, int bInitialState, string lpName); + [DllImport("kernel32.dll", CharSet = CharSet.Auto)] + static internal extern IntPtr CreateEvent(ref SECURITY_ATTRIBUTES securityAttributes, int bManualReset, int bInitialState, string lpName); - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - static internal extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, ref SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile); + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + static internal extern IntPtr CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, ref SECURITY_ATTRIBUTES lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile); - [DllImport("kernel32.dll", SetLastError = true)] - static internal extern bool ReadFile(IntPtr hFile, [Out] byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [In] ref System.Threading.NativeOverlapped lpOverlapped); + [DllImport("kernel32.dll", SetLastError = true)] + static internal extern bool ReadFile(IntPtr hFile, IntPtr lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, [In] ref System.Threading.NativeOverlapped lpOverlapped); - [DllImport("kernel32.dll")] - static internal extern uint WaitForSingleObject(IntPtr hHandle, int dwMilliseconds); + [DllImport("kernel32.dll")] + static internal extern uint WaitForSingleObject(IntPtr hHandle, int dwMilliseconds); - [DllImport("kernel32.dll")] - static internal extern bool GetOverlappedResult(IntPtr hFile, [In] ref System.Threading.NativeOverlapped lpOverlapped, out uint lpNumberOfBytesRead, bool bwait); + [DllImport("kernel32.dll", SetLastError = true)] + static internal extern bool GetOverlappedResult(IntPtr hFile, [In] ref System.Threading.NativeOverlapped lpOverlapped, out uint lpNumberOfBytesTransferred, bool bWait); - [DllImport("kernel32.dll")] - static internal extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] ref System.Threading.NativeOverlapped lpOverlapped); + [DllImport("kernel32.dll")] + static internal extern bool WriteFile(IntPtr hFile, byte[] lpBuffer, uint nNumberOfBytesToWrite, out uint lpNumberOfBytesWritten, [In] ref System.Threading.NativeOverlapped lpOverlapped); - internal const int DBT_DEVICEARRIVAL = 0x8000; - internal const int DBT_DEVICEREMOVECOMPLETE = 0x8004; - internal const int DBT_DEVTYP_DEVICEINTERFACE = 5; - internal const int DBT_DEVTYP_HANDLE = 6; - internal const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4; - internal const int DEVICE_NOTIFY_SERVICE_HANDLE = 1; - internal const int DEVICE_NOTIFY_WINDOW_HANDLE = 0; - internal const int WM_DEVICECHANGE = 0x219; - internal const short DIGCF_PRESENT = 0x2; - internal const short DIGCF_DEVICEINTERFACE = 0x10; - internal const int DIGCF_ALLCLASSES = 0x4; + internal const int DBT_DEVICEARRIVAL = 0x8000; + internal const int DBT_DEVICEREMOVECOMPLETE = 0x8004; + internal const int DBT_DEVTYP_DEVICEINTERFACE = 5; + internal const int DBT_DEVTYP_HANDLE = 6; + internal const int DEVICE_NOTIFY_ALL_INTERFACE_CLASSES = 4; + internal const int DEVICE_NOTIFY_SERVICE_HANDLE = 1; + internal const int DEVICE_NOTIFY_WINDOW_HANDLE = 0; + internal const int WM_DEVICECHANGE = 0x219; + internal const short DIGCF_PRESENT = 0x2; + internal const short DIGCF_DEVICEINTERFACE = 0x10; + internal const int DIGCF_ALLCLASSES = 0x4; - internal const int MAX_DEV_LEN = 1000; - internal const int SPDRP_ADDRESS = 0x1c; - internal const int SPDRP_BUSNUMBER = 0x15; - internal const int SPDRP_BUSTYPEGUID = 0x13; - internal const int SPDRP_CAPABILITIES = 0xf; - internal const int SPDRP_CHARACTERISTICS = 0x1b; - internal const int SPDRP_CLASS = 7; - internal const int SPDRP_CLASSGUID = 8; - internal const int SPDRP_COMPATIBLEIDS = 2; - internal const int SPDRP_CONFIGFLAGS = 0xa; - internal const int SPDRP_DEVICE_POWER_DATA = 0x1e; - internal const int SPDRP_DEVICEDESC = 0; - internal const int SPDRP_DEVTYPE = 0x19; - internal const int SPDRP_DRIVER = 9; - internal const int SPDRP_ENUMERATOR_NAME = 0x16; - internal const int SPDRP_EXCLUSIVE = 0x1a; - internal const int SPDRP_FRIENDLYNAME = 0xc; - internal const int SPDRP_HARDWAREID = 1; - internal const int SPDRP_LEGACYBUSTYPE = 0x14; - internal const int SPDRP_LOCATION_INFORMATION = 0xd; - internal const int SPDRP_LOWERFILTERS = 0x12; - internal const int SPDRP_MFG = 0xb; - internal const int SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0xe; - internal const int SPDRP_REMOVAL_POLICY = 0x1f; - internal const int SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x20; - internal const int SPDRP_REMOVAL_POLICY_OVERRIDE = 0x21; - internal const int SPDRP_SECURITY = 0x17; - internal const int SPDRP_SECURITY_SDS = 0x18; - internal const int SPDRP_SERVICE = 4; - internal const int SPDRP_UI_NUMBER = 0x10; - internal const int SPDRP_UI_NUMBER_DESC_FORMAT = 0x1d; + internal const int MAX_DEV_LEN = 1000; + internal const int SPDRP_ADDRESS = 0x1c; + internal const int SPDRP_BUSNUMBER = 0x15; + internal const int SPDRP_BUSTYPEGUID = 0x13; + internal const int SPDRP_CAPABILITIES = 0xf; + internal const int SPDRP_CHARACTERISTICS = 0x1b; + internal const int SPDRP_CLASS = 7; + internal const int SPDRP_CLASSGUID = 8; + internal const int SPDRP_COMPATIBLEIDS = 2; + internal const int SPDRP_CONFIGFLAGS = 0xa; + internal const int SPDRP_DEVICE_POWER_DATA = 0x1e; + internal const int SPDRP_DEVICEDESC = 0; + internal const int SPDRP_DEVTYPE = 0x19; + internal const int SPDRP_DRIVER = 9; + internal const int SPDRP_ENUMERATOR_NAME = 0x16; + internal const int SPDRP_EXCLUSIVE = 0x1a; + internal const int SPDRP_FRIENDLYNAME = 0xc; + internal const int SPDRP_HARDWAREID = 1; + internal const int SPDRP_LEGACYBUSTYPE = 0x14; + internal const int SPDRP_LOCATION_INFORMATION = 0xd; + internal const int SPDRP_LOWERFILTERS = 0x12; + internal const int SPDRP_MFG = 0xb; + internal const int SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = 0xe; + internal const int SPDRP_REMOVAL_POLICY = 0x1f; + internal const int SPDRP_REMOVAL_POLICY_HW_DEFAULT = 0x20; + internal const int SPDRP_REMOVAL_POLICY_OVERRIDE = 0x21; + internal const int SPDRP_SECURITY = 0x17; + internal const int SPDRP_SECURITY_SDS = 0x18; + internal const int SPDRP_SERVICE = 4; + internal const int SPDRP_UI_NUMBER = 0x10; + internal const int SPDRP_UI_NUMBER_DESC_FORMAT = 0x1d; - internal const int DIF_PROPERTYCHANGE = 0x12; - internal const int DICS_ENABLE = 1; - internal const int DICS_DISABLE = 2; - internal const int DICS_FLAG_GLOBAL = 1; + internal const int SPDRP_UPPERFILTERS = 0x11; + [StructLayout(LayoutKind.Sequential)] + internal class DEV_BROADCAST_DEVICEINTERFACE + { + internal int dbcc_size; + internal int dbcc_devicetype; + internal int dbcc_reserved; + internal Guid dbcc_classguid; + internal short dbcc_name; + } - internal const int SPDRP_UPPERFILTERS = 0x11; + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal class DEV_BROADCAST_DEVICEINTERFACE_1 + { + internal int dbcc_size; + internal int dbcc_devicetype; + internal int dbcc_reserved; + [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] + internal byte[] dbcc_classguid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] + internal char[] dbcc_name; + } - [StructLayout(LayoutKind.Sequential)] - internal class DEV_BROADCAST_DEVICEINTERFACE - { - internal int dbcc_size; - internal int dbcc_devicetype; - internal int dbcc_reserved; - internal Guid dbcc_classguid; - internal short dbcc_name; - } + [StructLayout(LayoutKind.Sequential)] + internal class DEV_BROADCAST_HANDLE + { + internal int dbch_size; + internal int dbch_devicetype; + internal int dbch_reserved; + internal int dbch_handle; + internal int dbch_hdevnotify; + } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal class DEV_BROADCAST_DEVICEINTERFACE_1 - { - internal int dbcc_size; - internal int dbcc_devicetype; - internal int dbcc_reserved; - [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U1, SizeConst = 16)] - internal byte[] dbcc_classguid; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)] - internal char[] dbcc_name; - } + [StructLayout(LayoutKind.Sequential)] + internal class DEV_BROADCAST_HDR + { + internal int dbch_size; + internal int dbch_devicetype; + internal int dbch_reserved; + } - [StructLayout(LayoutKind.Sequential)] - internal class DEV_BROADCAST_HANDLE - { - internal int dbch_size; - internal int dbch_devicetype; - internal int dbch_reserved; - internal int dbch_handle; - internal int dbch_hdevnotify; - } + [StructLayout(LayoutKind.Sequential)] + internal struct SP_DEVICE_INTERFACE_DATA + { + internal int cbSize; + internal System.Guid InterfaceClassGuid; + internal int Flags; + internal IntPtr Reserved; + } - [StructLayout(LayoutKind.Sequential)] - internal class DEV_BROADCAST_HDR - { - internal int dbch_size; - internal int dbch_devicetype; - internal int dbch_reserved; - } + [StructLayout(LayoutKind.Sequential)] + internal struct SP_DEVINFO_DATA + { + internal int cbSize; + internal Guid ClassGuid; + internal int DevInst; + internal IntPtr Reserved; + } - [StructLayout(LayoutKind.Sequential)] - internal struct SP_DEVICE_INTERFACE_DATA - { - internal int cbSize; - internal System.Guid InterfaceClassGuid; - internal int Flags; - internal IntPtr Reserved; - } + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + internal struct SP_DEVICE_INTERFACE_DETAIL_DATA + { + internal int Size; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + internal string DevicePath; + } - [StructLayout(LayoutKind.Sequential)] - internal struct SP_DEVINFO_DATA - { - internal int cbSize; - internal Guid ClassGuid; - internal int DevInst; - internal IntPtr Reserved; - } + [StructLayout(LayoutKind.Sequential)] + internal struct DEVPROPKEY + { + public Guid fmtid; + public ulong pid; + } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - internal struct SP_DEVICE_INTERFACE_DETAIL_DATA - { - internal int Size; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - internal string DevicePath; - } + internal static DEVPROPKEY DEVPKEY_Device_BusReportedDeviceDesc = + new DEVPROPKEY { fmtid = new Guid(0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2), pid = 4 }; - [StructLayout(LayoutKind.Sequential)] - internal struct DEVPROPKEY - { - public Guid fmtid; - public ulong pid; - } + [DllImport("setupapi.dll", EntryPoint = "SetupDiGetDeviceRegistryProperty")] + public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, int propertyVal, ref int propertyRegDataType, byte[] propertyBuffer, int propertyBufferSize, ref int requiredSize); - internal static DEVPROPKEY DEVPKEY_Device_BusReportedDeviceDesc = - new DEVPROPKEY { fmtid = new Guid(0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2), pid = 4 }; + [DllImport("setupapi.dll", EntryPoint = "SetupDiGetDevicePropertyW", SetLastError = true)] + public static extern bool SetupDiGetDeviceProperty(IntPtr deviceInfo, ref SP_DEVINFO_DATA deviceInfoData, ref DEVPROPKEY propkey, ref ulong propertyDataType, byte[] propertyBuffer, int propertyBufferSize, ref int requiredSize, uint flags); - [DllImport("setupapi.dll", EntryPoint = "SetupDiGetDeviceRegistryProperty")] - public static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, int propertyVal, ref int propertyRegDataType, byte[] propertyBuffer, int propertyBufferSize, ref int requiredSize); + [DllImport("setupapi.dll")] + static internal extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet, int memberIndex, ref SP_DEVINFO_DATA deviceInfoData); - [DllImport("setupapi.dll", EntryPoint = "SetupDiGetDevicePropertyW", SetLastError = true)] - public static extern bool SetupDiGetDeviceProperty(IntPtr deviceInfo, ref SP_DEVINFO_DATA deviceInfoData, ref DEVPROPKEY propkey, ref ulong propertyDataType, byte[] propertyBuffer, int propertyBufferSize, ref int requiredSize, uint flags); + [DllImport("user32.dll", CharSet = CharSet.Auto)] + static internal extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr notificationFilter, Int32 flags); - [DllImport("setupapi.dll")] - static internal extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet, int memberIndex, ref SP_DEVINFO_DATA deviceInfoData); + [DllImport("setupapi.dll")] + internal static extern int SetupDiCreateDeviceInfoList(ref Guid classGuid, int hwndParent); - [DllImport("user32.dll", CharSet = CharSet.Auto)] - static internal extern IntPtr RegisterDeviceNotification(IntPtr hRecipient, IntPtr notificationFilter, Int32 flags); + [DllImport("setupapi.dll")] + static internal extern int SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); - [DllImport("setupapi.dll")] - internal static extern int SetupDiCreateDeviceInfoList(ref Guid classGuid, int hwndParent); + [DllImport("setupapi.dll")] + static internal extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); - [DllImport("setupapi.dll")] - static internal extern int SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static internal extern IntPtr SetupDiGetClassDevs(ref System.Guid classGuid, string enumerator, int hwndParent, int flags); - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - static internal extern bool SetupDiSetClassInstallParams(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, ref SP_PROPCHANGE_PARAMS classInstallParams, int classInstallParamsSize); + [DllImport("setupapi.dll", CharSet = CharSet.Auto, EntryPoint = "SetupDiGetDeviceInterfaceDetail")] + static internal extern bool SetupDiGetDeviceInterfaceDetailBuffer(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, IntPtr deviceInfoData); - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - static internal extern bool SetupDiCallClassInstaller(int installFunction, IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData); + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static internal extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, IntPtr deviceInfoData); - [DllImport("setupapi.dll")] - static internal extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, ref SP_DEVINFO_DATA deviceInfoData, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static internal extern bool SetupDiGetDeviceInstanceId(IntPtr DeviceInfoSet, ref SP_DEVINFO_DATA DeviceInfoData, [Out] char[] DeviceInstanceId, int DeviceInstanceIdSize, out int RequiredSize); - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - static internal extern IntPtr SetupDiGetClassDevs(ref System.Guid classGuid, string enumerator, int hwndParent, int flags); + [DllImport("user32.dll")] + static internal extern bool UnregisterDeviceNotification(IntPtr handle); - [DllImport("setupapi.dll", CharSet = CharSet.Auto, EntryPoint = "SetupDiGetDeviceInterfaceDetail")] - static internal extern bool SetupDiGetDeviceInterfaceDetailBuffer(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, IntPtr deviceInfoData); + internal const short HIDP_INPUT = 0; + internal const short HIDP_OUTPUT = 1; - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - static internal extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, ref SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, IntPtr deviceInfoData); + internal const short HIDP_FEATURE = 2; + [StructLayout(LayoutKind.Sequential)] + internal struct HIDD_ATTRIBUTES + { + internal int Size; + internal ushort VendorID; + internal ushort ProductID; + internal short VersionNumber; + } - [DllImport("user32.dll")] - static internal extern bool UnregisterDeviceNotification(IntPtr handle); + [StructLayout(LayoutKind.Sequential)] + internal struct HIDP_CAPS + { + internal short Usage; + internal short UsagePage; + internal short InputReportByteLength; + internal short OutputReportByteLength; + internal short FeatureReportByteLength; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + internal short[] Reserved; + internal short NumberLinkCollectionNodes; + internal short NumberInputButtonCaps; + internal short NumberInputValueCaps; + internal short NumberInputDataIndices; + internal short NumberOutputButtonCaps; + internal short NumberOutputValueCaps; + internal short NumberOutputDataIndices; + internal short NumberFeatureButtonCaps; + internal short NumberFeatureValueCaps; + internal short NumberFeatureDataIndices; + } - internal const short HIDP_INPUT = 0; - internal const short HIDP_OUTPUT = 1; + [StructLayout(LayoutKind.Sequential)] + internal struct HIDP_VALUE_CAPS + { + internal short UsagePage; + internal byte ReportID; + internal int IsAlias; + internal short BitField; + internal short LinkCollection; + internal short LinkUsage; + internal short LinkUsagePage; + internal int IsRange; + internal int IsStringRange; + internal int IsDesignatorRange; + internal int IsAbsolute; + internal int HasNull; + internal byte Reserved; + internal short BitSize; + internal short ReportCount; + internal short Reserved2; + internal short Reserved3; + internal short Reserved4; + internal short Reserved5; + internal short Reserved6; + internal int LogicalMin; + internal int LogicalMax; + internal int PhysicalMin; + internal int PhysicalMax; + internal short UsageMin; + internal short UsageMax; + internal short StringMin; + internal short StringMax; + internal short DesignatorMin; + internal short DesignatorMax; + internal short DataIndexMin; + internal short DataIndexMax; + } - internal const short HIDP_FEATURE = 2; - [StructLayout(LayoutKind.Sequential)] - internal struct HIDD_ATTRIBUTES - { - internal int Size; - internal ushort VendorID; - internal ushort ProductID; - internal short VersionNumber; - } + [DllImport("hid.dll")] + static internal extern bool HidD_FlushQueue(IntPtr hidDeviceObject); - [StructLayout(LayoutKind.Sequential)] - internal struct HIDP_CAPS - { - internal short Usage; - internal short UsagePage; - internal short InputReportByteLength; - internal short OutputReportByteLength; - internal short FeatureReportByteLength; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - internal short[] Reserved; - internal short NumberLinkCollectionNodes; - internal short NumberInputButtonCaps; - internal short NumberInputValueCaps; - internal short NumberInputDataIndices; - internal short NumberOutputButtonCaps; - internal short NumberOutputValueCaps; - internal short NumberOutputDataIndices; - internal short NumberFeatureButtonCaps; - internal short NumberFeatureValueCaps; - internal short NumberFeatureDataIndices; - } + [DllImport("hid.dll")] + static internal extern bool HidD_GetAttributes(IntPtr hidDeviceObject, ref HIDD_ATTRIBUTES attributes); - [StructLayout(LayoutKind.Sequential)] - internal struct SP_CLASSINSTALL_HEADER - { - internal int cbSize; - internal int installFunction; - } + [DllImport("hid.dll")] + static internal extern bool HidD_GetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); - [StructLayout(LayoutKind.Sequential)] - internal struct SP_PROPCHANGE_PARAMS - { - internal SP_CLASSINSTALL_HEADER classInstallHeader; - internal int stateChange; - internal int scope; - internal int hwProfile; - } + [DllImport("hid.dll")] + static internal extern bool HidD_GetInputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); - [StructLayout(LayoutKind.Sequential)] - internal struct HIDP_VALUE_CAPS - { - internal short UsagePage; - internal byte ReportID; - internal int IsAlias; - internal short BitField; - internal short LinkCollection; - internal short LinkUsage; - internal short LinkUsagePage; - internal int IsRange; - internal int IsStringRange; - internal int IsDesignatorRange; - internal int IsAbsolute; - internal int HasNull; - internal byte Reserved; - internal short BitSize; - internal short ReportCount; - internal short Reserved2; - internal short Reserved3; - internal short Reserved4; - internal short Reserved5; - internal short Reserved6; - internal int LogicalMin; - internal int LogicalMax; - internal int PhysicalMin; - internal int PhysicalMax; - internal short UsageMin; - internal short UsageMax; - internal short StringMin; - internal short StringMax; - internal short DesignatorMin; - internal short DesignatorMax; - internal short DataIndexMin; - internal short DataIndexMax; - } + [DllImport("hid.dll")] + static internal extern void HidD_GetHidGuid(ref Guid hidGuid); - [DllImport("hid.dll")] - static internal extern bool HidD_FlushQueue(IntPtr hidDeviceObject); + [DllImport("hid.dll")] + static internal extern bool HidD_GetNumInputBuffers(IntPtr hidDeviceObject, ref int numberBuffers); - [DllImport("hid.dll")] - static internal extern bool HidD_GetAttributes(IntPtr hidDeviceObject, ref HIDD_ATTRIBUTES attributes); + [DllImport("hid.dll")] + static internal extern bool HidD_GetPreparsedData(IntPtr hidDeviceObject, ref IntPtr preparsedData); - [DllImport("hid.dll")] - static internal extern bool HidD_GetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); + [DllImport("hid.dll")] + static internal extern bool HidD_FreePreparsedData(IntPtr preparsedData); - [DllImport("hid.dll")] - static internal extern bool HidD_GetInputReport(IntPtr hidDeviceObject, ref byte lpReportBuffer, int reportBufferLength); + [DllImport("hid.dll")] + static internal extern bool HidD_SetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); - [DllImport("hid.dll")] - static internal extern void HidD_GetHidGuid(ref Guid hidGuid); + [DllImport("hid.dll")] + static internal extern bool HidD_SetNumInputBuffers(IntPtr hidDeviceObject, int numberBuffers); - [DllImport("hid.dll")] - static internal extern bool HidD_GetNumInputBuffers(IntPtr hidDeviceObject, ref int numberBuffers); + [DllImport("hid.dll")] + static internal extern bool HidD_SetOutputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); - [DllImport("hid.dll")] - static internal extern bool HidD_GetPreparsedData(IntPtr hidDeviceObject, ref IntPtr preparsedData); + [DllImport("hid.dll")] + static internal extern int HidP_GetCaps(IntPtr preparsedData, ref HIDP_CAPS capabilities); - [DllImport("hid.dll")] - static internal extern bool HidD_FreePreparsedData(IntPtr preparsedData); + [DllImport("hid.dll")] + static internal extern int HidP_GetValueCaps(short reportType, ref byte valueCaps, ref short valueCapsLength, IntPtr preparsedData); - [DllImport("hid.dll")] - static internal extern bool HidD_SetFeature(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); + [DllImport("hid.dll", CharSet = CharSet.Unicode)] + internal static extern bool HidD_GetProductString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength); - [DllImport("hid.dll")] - static internal extern bool HidD_SetNumInputBuffers(IntPtr hidDeviceObject, int numberBuffers); + [DllImport("hid.dll", CharSet = CharSet.Unicode)] + internal static extern bool HidD_GetManufacturerString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength); - [DllImport("hid.dll")] - static internal extern bool HidD_SetOutputReport(IntPtr hidDeviceObject, byte[] lpReportBuffer, int reportBufferLength); - - [DllImport("hid.dll")] - static internal extern int HidP_GetCaps(IntPtr preparsedData, ref HIDP_CAPS capabilities); - - [DllImport("hid.dll")] - static internal extern int HidP_GetValueCaps(short reportType, ref byte valueCaps, ref short valueCapsLength, IntPtr preparsedData); - - [DllImport("hid.dll", CharSet = CharSet.Unicode)] - internal static extern bool HidD_GetProductString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength); - - [DllImport("hid.dll", CharSet = CharSet.Unicode)] - internal static extern bool HidD_GetManufacturerString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int ReportBufferLength); - - [DllImport("hid.dll", CharSet = CharSet.Unicode)] - internal static extern bool HidD_GetSerialNumberString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int reportBufferLength); - } + [DllImport("hid.dll", CharSet = CharSet.Unicode)] + internal static extern bool HidD_GetSerialNumberString(IntPtr hidDeviceObject, ref byte lpReportBuffer, int reportBufferLength); + } } diff --git a/MiController/MiApplicationContext .cs b/MiController/MiApplicationContext .cs index 5450b32..f710348 100644 --- a/MiController/MiApplicationContext .cs +++ b/MiController/MiApplicationContext .cs @@ -1,25 +1,26 @@ using MiController.Properties; using System; -using System.ComponentModel; using System.Drawing; -using System.Globalization; using System.Windows.Forms; +using MiController.Win32; +using Nefarius.ViGEm.Client.Exceptions; namespace MiController { public class MiApplicationContext : ApplicationContext { private readonly NotifyIcon _trayIcon; - private BackgroundWorker _backgroundWorker; private ContextMenuStrip _contextMenuStrip; - private ToolStripMenuItem _connectToolStripMenuItem; - private ToolStripMenuItem _disconnectToolStripMenuItem; private ToolStripMenuItem _statusToolStripMenuItem; + private const string XIAOMI_GAMEPAD_HARDWARE_FILTER = @"VID&00022717_PID&3144"; + private XInputManager _manager; + private HidMonitor _monitor; public MiApplicationContext() { - InitBackgroundWorker(); + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; InitContextMenu(); - + InitGamepad(); + StartGamePad(); // Initialize Tray Icon _trayIcon = new NotifyIcon { @@ -29,69 +30,33 @@ namespace MiController }; } + private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs args) + { + var message = args.ExceptionObject is Exception exception ? exception.Message : args.ExceptionObject.ToString(); + _statusToolStripMenuItem.Text = $"Error: {message}"; + } + private void exitToolStripMenuItem_Click(object sender, EventArgs e) { - _backgroundWorker.CancelAsync(); _trayIcon.Visible = false; - + _monitor.Stop(); Application.Exit(); } - private void connectToolStripMenuItem_Click(object sender, EventArgs e) - { - StartStopProcess(true); - _backgroundWorker.RunWorkerAsync(); - } - private void disconnectToolStripMenuItem_Click(object sender, EventArgs e) - { - _backgroundWorker.CancelAsync(); - } - - private void InitBackgroundWorker() - { - _backgroundWorker = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true}; - _backgroundWorker.DoWork += backgroundWorker_DoWork; - _backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged; - _backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted; - } - private void StartStopProcess(bool start) { - _connectToolStripMenuItem.Enabled = !start; - _disconnectToolStripMenuItem.Enabled = start; + _statusToolStripMenuItem.Image = start ? Resources.ConnectPlugged : Resources.ConnectUnplugged; _trayIcon.Icon = start ? Resources.MiLogo : Resources.MiLogoGrey; } private void InitContextMenu() { - _connectToolStripMenuItem = new ToolStripMenuItem - { - Name = "connectToolStripMenuItem", - Size = new Size(180, 22), - Text = "Connect", - Image = Resources.ConnectPlugged, - ImageTransparentColor = Color.Magenta - }; - _connectToolStripMenuItem.Click += connectToolStripMenuItem_Click; - - _disconnectToolStripMenuItem = new ToolStripMenuItem - { - Name = "disconnectToolStripMenuItem", - Size = new Size(180, 22), - Text = "Disconnect", - Image = Resources.ConnectUnplugged, - ImageTransparentColor = Color.Magenta, - Enabled = false - }; - _disconnectToolStripMenuItem.Click += disconnectToolStripMenuItem_Click; - var toolStripSeparator1 = new ToolStripSeparator { Name = "toolStripSeparator1", Size = new Size(322, 6) }; - _statusToolStripMenuItem = new ToolStripMenuItem { Name = "statusToolStripMenuItem", Size = new Size(180, 22), Text = "ready", - Enabled = false + Enabled = true }; var toolStripSeparator2 = new ToolStripSeparator { Name = "toolStripSeparator2", Size = new Size(322, 6) }; @@ -107,58 +72,57 @@ namespace MiController exitToolStripMenuItem.Click += exitToolStripMenuItem_Click; _contextMenuStrip = new ContextMenuStrip { Renderer = new NoHighlightRenderer() }; - _contextMenuStrip.Items.Add(_connectToolStripMenuItem); - _contextMenuStrip.Items.Add(_disconnectToolStripMenuItem); - _contextMenuStrip.Items.Add(toolStripSeparator1); _contextMenuStrip.Items.Add(_statusToolStripMenuItem); _contextMenuStrip.Items.Add(toolStripSeparator2); _contextMenuStrip.Items.Add(exitToolStripMenuItem); } - private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) + + private void Manager_GamepadRunning(object sender, EventArgs eventArgs) { - var bw = sender as BackgroundWorker; - - ControllerHelper.Run(bw); - - if (bw != null) - { - if (bw.CancellationPending) - { - e.Cancel = true; - } - } + _statusToolStripMenuItem.Text = "Gamepad connected"; + StartStopProcess(true); } - private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) + private void Manager_GamepadRemoved(object sender, EventArgs eventArgs) { - if (e.Cancelled) - { - _statusToolStripMenuItem.Text = "disconnected"; - } - else if (e.Error != null) - { - string msg = String.Format(CultureInfo.InvariantCulture, "An error occurred: {0}", e.Error.Message); - _statusToolStripMenuItem.Text = msg; - } + _statusToolStripMenuItem.Text = "Gamepad disconnected"; StartStopProcess(false); } - private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) + private void Monitor_DeviceAttached(object sender, DeviceEventArgs e) { - _statusToolStripMenuItem.Text = (string)e.UserState; + //_statusToolStripMenuItem.Text = $"New HID device connected: {e.Description} {e.Path}"; + _manager.AddAndStart(e.Path, e.InstanceId); } - } - - internal class NoHighlightRenderer : ToolStripProfessionalRenderer - { - protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) + private void Monitor_DeviceRemoved(object sender, DeviceEventArgs e) { - if (e.Item.Enabled) + //_statusToolStripMenuItem.Text = $"HID device disconnected: {e.Description} {e.Path}"; + _manager.StopAndRemove(e.Path); + } + + private void InitGamepad() + { + try { - base.OnRenderMenuItemBackground(e); + _manager = new XInputManager(); + _manager.GamepadRunning += Manager_GamepadRunning; + _manager.GamepadRemoved += Manager_GamepadRemoved; } + catch (VigemBusNotFoundException ex) + { + throw new ApplicationException("ViGEm Bus not found. Please make sure that is installed correctly", ex); + } + + _monitor = new HidMonitor(XIAOMI_GAMEPAD_HARDWARE_FILTER); + _monitor.DeviceAttached += Monitor_DeviceAttached; + _monitor.DeviceRemoved += Monitor_DeviceRemoved; + } + + private void StartGamePad() + { + _monitor.Start(); } } } diff --git a/MiController/MiController.csproj b/MiController/MiController.csproj index 8491878..59b229f 100644 --- a/MiController/MiController.csproj +++ b/MiController/MiController.csproj @@ -8,7 +8,7 @@ WinExe MiController MiController - v4.7.2 + v4.8 512 true true @@ -27,9 +27,10 @@ false false true + - x64 + AnyCPU true full false @@ -37,6 +38,7 @@ DEBUG;TRACE prompt 4 + false x64 @@ -51,7 +53,7 @@ MiLogo.ico - true + false MiControllerKey.snk @@ -60,6 +62,9 @@ app.manifest + + ..\packages\Nefarius.ViGEm.Client.1.17.178\lib\net452\Nefarius.ViGEm.Client.dll + @@ -74,8 +79,6 @@ - - @@ -85,18 +88,24 @@ + - + - - - + + + + + + + + ResXFileCodeGenerator @@ -108,8 +117,10 @@ Resources.resx True + + SettingsSingleFileGenerator Settings.Designer.cs @@ -120,21 +131,6 @@ True - - Always - - - Always - - - Always - - - Always - - - Always - @@ -164,11 +160,6 @@ - - - Always - - "C:\Program Files (x86)\Inno Setup 6\ISCC.exe" "$(ProjectDir)Setup.iss" diff --git a/MiController/NoHighlightRenderer.cs b/MiController/NoHighlightRenderer.cs new file mode 100644 index 0000000..a1ad83b --- /dev/null +++ b/MiController/NoHighlightRenderer.cs @@ -0,0 +1,15 @@ +using System.Windows.Forms; + +namespace MiController +{ + internal class NoHighlightRenderer : ToolStripProfessionalRenderer + { + protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) + { + if (e.Item.Enabled) + { + base.OnRenderMenuItemBackground(e); + } + } + } +} \ No newline at end of file diff --git a/MiController/Program.cs b/MiController/Program.cs index 8e469cb..f3e428e 100644 --- a/MiController/Program.cs +++ b/MiController/Program.cs @@ -9,24 +9,8 @@ namespace MiController /// The main entry point for the application. /// [STAThread] - static void Main(string[] args) + static void Main() { - if (args.Length > 0) - { - string cmd = args[0].ToLowerInvariant(); - if (cmd == "/install") - { - DriverSetup.Uninstall(); - DriverSetup.Install(); - } - else if (cmd == "/uninstall") - { - DriverSetup.Uninstall(); - return; - } - } - - Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new MiApplicationContext()); diff --git a/MiController/Properties/AssemblyInfo.cs b/MiController/Properties/AssemblyInfo.cs index d8ca80d..496d851 100644 --- a/MiController/Properties/AssemblyInfo.cs +++ b/MiController/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -10,7 +9,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("TomaSoft")] [assembly: AssemblyProduct("MiController")] -[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyCopyright("Copyright © 2022")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +31,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] diff --git a/MiController/Properties/Resources.Designer.cs b/MiController/Properties/Resources.Designer.cs index f3b5874..ad9c253 100644 --- a/MiController/Properties/Resources.Designer.cs +++ b/MiController/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace MiController.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/MiController/Properties/Settings.Designer.cs b/MiController/Properties/Settings.Designer.cs index 371a25d..f2a1c35 100644 --- a/MiController/Properties/Settings.Designer.cs +++ b/MiController/Properties/Settings.Designer.cs @@ -8,21 +8,17 @@ // //------------------------------------------------------------------------------ -namespace MiController.Properties -{ - - +namespace MiController.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/MiController/ScpDriver/ScpVBus.cat b/MiController/ScpDriver/ScpVBus.cat deleted file mode 100644 index 895bd16..0000000 Binary files a/MiController/ScpDriver/ScpVBus.cat and /dev/null differ diff --git a/MiController/ScpDriver/ScpVBus.inf b/MiController/ScpDriver/ScpVBus.inf deleted file mode 100644 index 18a4df4..0000000 --- a/MiController/ScpDriver/ScpVBus.inf +++ /dev/null @@ -1,71 +0,0 @@ -; ============================================================================= -; Copyright (c) Scarlet.Crush Productions. All rights reserved. -; -; ScpVBus.inf -; ============================================================================= - -[Version] -Provider = %SCProd% -Signature = "$Windows NT$" -Class = System -ClassGuid = {4D36E97D-E325-11CE-BFC1-08002BE10318} -DriverVer = 05/05/2013, 1.0.0.103 -CatalogFile = ScpVBus.cat - -; ============================================================================= - -[DestinationDirs] -DefaultDestDir = 12 - -[Manufacturer] -%SCProd% = SCProductions, NTx86, NTamd64 - -[SourceDisksNames] -1 = %MediaDescription% - -[SCProductions.NTx86] -%ScpVBus.DeviceDesc% = ScpVBus_Install, root\ScpVBus - -[SourceDisksFiles.x86] -ScpVBus.sys = 1, .\x86, - -[SCProductions.NTamd64] -%ScpVBus.DeviceDesc% = ScpVBus_Install, root\ScpVBus - -[SourceDisksFiles.amd64] -ScpVBus.sys = 1, .\amd64, - -; ============================================================================= - -[ScpVBus_Install.NT] -CopyFiles = ScpVBus_Install.NT.Copy - -[ScpVBus_Install.NT.hw] -AddReg = ScpVBus_Device_AddReg - -[ScpVBus_Install.NT.Copy] -ScpVBus.sys - -[ScpVBus_Device_AddReg] - -[ScpVBus_Install.NT.Services] -AddService = ScpVBus, %SPSVCINST_ASSOCSERVICE%, ScpVBus_Service_Inst - -[ScpVBus_Service_Inst] -DisplayName = %ScpVBus.SVCDESC% -ServiceType = 1 ; SERVICE_KERNEL_DRIVER -StartType = 3 ; SERVICE_DEMAND_START -ErrorControl = 1 ; SERVICE_ERROR_NORMAL -ServiceBinary = %12%\ScpVBus.sys -LoadOrderGroup = Extended Base - -; ============================================================================= - -[Strings] -SCProd = "Scarlet.Crush Productions" -MediaDescription = "Scp Virtual Bus Installation Media" -ScpVBus.DeviceDesc = "Scp Virtual Bus Driver" -ScpVBus.SVCDESC = "Scp Virtual Bus Driver" -SPSVCINST_ASSOCSERVICE = 0x00000002 - -; ============================================================================= diff --git a/MiController/ScpDriver/amd64/DIFxAPI.dll b/MiController/ScpDriver/amd64/DIFxAPI.dll deleted file mode 100644 index 76ccfd2..0000000 Binary files a/MiController/ScpDriver/amd64/DIFxAPI.dll and /dev/null differ diff --git a/MiController/ScpDriver/amd64/ScpVBus.sys b/MiController/ScpDriver/amd64/ScpVBus.sys deleted file mode 100644 index ae55f41..0000000 Binary files a/MiController/ScpDriver/amd64/ScpVBus.sys and /dev/null differ diff --git a/MiController/ScpDriver/x86/DIFxAPI.dll b/MiController/ScpDriver/x86/DIFxAPI.dll deleted file mode 100644 index 9de2917..0000000 Binary files a/MiController/ScpDriver/x86/DIFxAPI.dll and /dev/null differ diff --git a/MiController/ScpDriver/x86/ScpVBus.sys b/MiController/ScpDriver/x86/ScpVBus.sys deleted file mode 100644 index d4fc51b..0000000 Binary files a/MiController/ScpDriver/x86/ScpVBus.sys and /dev/null differ diff --git a/MiController/ScpDriverInterface/ScpBus.cs b/MiController/ScpDriverInterface/ScpBus.cs deleted file mode 100644 index 57ad259..0000000 --- a/MiController/ScpDriverInterface/ScpBus.cs +++ /dev/null @@ -1,312 +0,0 @@ -/* - * ScpDriverInterface - by Mogzol (and of course Scarlet.Crush) - Jan, 2016 - * - * This is a simple little DLL which allows you to use Scarlet.Crush's SCP Virtual - * Bus Driver to emulate XBox 360 Controllers. - * - * Most of the code here has been ripped out of his ScpControl source code, mostly - * from the ScpDevice and BusDevice classes, so obviously credit and major props to - * Scarlet.Crush, without him this wouldn't be possible. You can download his - * original source code from here: - * http://forums.pcsx2.net/Thread-XInput-Wrapper-for-DS3-and-Play-com-USB-Dual-DS2-Controller - * - * Note that for this to work the SCP Virtual Bus Driver must be installed. (Duh) - */ - -using System; -using System.Globalization; -using System.IO; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; -[assembly: CLSCompliant(true)] - -namespace ScpDriverInterface -{ - /// - /// Emulates XBox 360 controllers via Scarlet.Crush's SCP Virtual Bus Driver. - /// - public class ScpBus : IDisposable - { - private const string SCP_BUS_CLASS_GUID = "{F679F562-3164-42CE-A4DB-E7DDBE723909}"; - private const int ReportSize = 28; - - private readonly SafeFileHandle _deviceHandle; - - /// - /// Creates a new ScpBus object, which will then try to get a handle to the SCP Virtual Bus device. If it is unable to get the handle, an IOException will be thrown. - /// - public ScpBus() : this(0) { } - - /// - /// Creates a new ScpBus object, which will then try to get a handle to the SCP Virtual Bus device. If it is unable to get the handle, an IOException will be thrown. - /// - /// Specifies which SCP Virtual Bus device to use. This is 0-based. - public ScpBus(int instance) - { - string devicePath = ""; - - if (Find(new Guid(SCP_BUS_CLASS_GUID), ref devicePath, instance)) - { - _deviceHandle = GetHandle(devicePath); - } - else - { - throw new IOException("SCP Virtual Bus Device not found"); - } - } - - /// - /// Creates a new ScpBus object, which will then try to get a handle to the specified SCP Virtual Bus device. If it is unable to get the handle, an IOException will be thrown. - /// - /// The path to the SCP Virtual Bus device that you want to use. - public ScpBus(string devicePath) - { - _deviceHandle = GetHandle(devicePath); - } - - /// - /// Closes the handle to the SCP Virtual Bus device. Call this when you are done with your instance of ScpBus. - /// - /// (This method does the same thing as the Dispose() method. Use one or the other.) - /// - public void Close() - { - Dispose(); - } - - /// - /// Closes the handle to the SCP Virtual Bus device. Call this when you are done with your instance of ScpBus. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - protected virtual void Dispose(bool disposing) - { - if (_deviceHandle != null && !_deviceHandle.IsInvalid) - { - _deviceHandle.Dispose(); - } - } - - /// - /// Plugs in an emulated XBox 360 controller. - /// - /// Used to identify the controller. Give each controller you plug in a different number. Number must be non-zero. - /// True if the operation was successful, false otherwise. - public bool PlugIn(int controllerNumber) - { - if (_deviceHandle.IsInvalid) - throw new ObjectDisposedException("SCP Virtual Bus device handle is closed"); - - int transfered = 0; - byte[] buffer = new byte[16]; - - buffer[0] = 0x10; - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - - buffer[4] = (byte)((controllerNumber) & 0xFF); - buffer[5] = (byte)((controllerNumber >> 8) & 0xFF); - buffer[6] = (byte)((controllerNumber >> 16) & 0xFF); - buffer[7] = (byte)((controllerNumber >> 24) & 0xFF); - - return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A4000, buffer, buffer.Length, null, 0, ref transfered, IntPtr.Zero); - } - - /// - /// Unplugs an emulated XBox 360 controller. - /// - /// The controller you want to unplug. - /// True if the operation was successful, false otherwise. - public bool Unplug(int controllerNumber) - { - if (_deviceHandle.IsInvalid) - throw new ObjectDisposedException("SCP Virtual Bus device handle is closed"); - - int transfered = 0; - byte[] buffer = new Byte[16]; - - buffer[0] = 0x10; - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - - buffer[4] = (byte)((controllerNumber) & 0xFF); - buffer[5] = (byte)((controllerNumber >> 8) & 0xFF); - buffer[6] = (byte)((controllerNumber >> 16) & 0xFF); - buffer[7] = (byte)((controllerNumber >> 24) & 0xFF); - - return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A4004, buffer, buffer.Length, null, 0, ref transfered, IntPtr.Zero); - } - - /// - /// Unplugs all emulated XBox 360 controllers. - /// - /// True if the operation was successful, false otherwise. - public bool UnplugAll() - { - if (_deviceHandle.IsInvalid) - throw new ObjectDisposedException("SCP Virtual Bus device handle is closed"); - - int transfered = 0; - byte[] buffer = new byte[16]; - - buffer[0] = 0x10; - buffer[1] = 0x00; - buffer[2] = 0x00; - buffer[3] = 0x00; - - return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A4004, buffer, buffer.Length, null, 0, ref transfered, IntPtr.Zero); - } - - /// - /// Sends an input report for the current state of the specified emulated XBox 360 controller. Note: Only use this if you don't care about rumble data, otherwise use the 3-parameter version of Report(). - /// - /// The controller to report. - /// The controller report. If using the included X360Controller class, this can be generated with the GetReport() method. Otherwise see http://free60.org/wiki/GamePad#Input_report for details. - /// True if the operation was successful, false otherwise. - public bool Report(int controllerNumber, byte[] controllerReport) - { - return Report(controllerNumber, controllerReport, null); - } - - /// - /// Sends an input report for the current state of the specified emulated XBox 360 controller. If you care about rumble data, make sure you check the output report for rumble data every time you call this. - /// - /// The controller to report. - /// The controller report. If using the included X360Controller class, this can be generated with the GetReport() method. Otherwise see http://free60.org/wiki/GamePad#Input_report for details. - /// The buffer for the output report, which takes the form specified here: http://free60.org/wiki/GamePad#Output_report. Use an 8-byte buffer if you care about rumble data, or null otherwise. - /// True if the operation was successful, false otherwise. - public bool Report(int controllerNumber, byte[] controllerReport, byte[] outputBuffer) - { - if (_deviceHandle.IsInvalid) - throw new ObjectDisposedException("SCP Virtual Bus device handle is closed"); - - byte[] head = new byte[8]; - - head[0] = 0x1C; - head[4] = (byte)((controllerNumber) & 0xFF); - head[5] = (byte)((controllerNumber >> 8) & 0xFF); - head[6] = (byte)((controllerNumber >> 16) & 0xFF); - head[7] = (byte)((controllerNumber >> 24) & 0xFF); - - byte[] fullReport = new byte[28]; - - Buffer.BlockCopy(head, 0, fullReport, 0, head.Length); - Buffer.BlockCopy(controllerReport, 0, fullReport, head.Length, controllerReport.Length); - - int transferred = 0; - return NativeMethods.DeviceIoControl(_deviceHandle, 0x2A400C, fullReport, fullReport.Length, outputBuffer, outputBuffer?.Length ?? 0, ref transferred, IntPtr.Zero) && transferred > 0; - } - - private static bool Find(Guid target, ref string path, int instance = 0) - { - IntPtr detailDataBuffer = IntPtr.Zero; - IntPtr deviceInfoSet = IntPtr.Zero; - - try - { - NativeMethods.SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new NativeMethods.SP_DEVICE_INTERFACE_DATA(), da = new NativeMethods.SP_DEVICE_INTERFACE_DATA(); - int bufferSize = 0, memberIndex = 0; - - deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref target, IntPtr.Zero, IntPtr.Zero, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE); - - DeviceInterfaceData.cbSize = da.cbSize = Marshal.SizeOf(DeviceInterfaceData); - - while (NativeMethods.SetupDiEnumDeviceInterfaces(deviceInfoSet, IntPtr.Zero, ref target, memberIndex, ref DeviceInterfaceData)) - { - NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, IntPtr.Zero, 0, ref bufferSize, ref da); - detailDataBuffer = Marshal.AllocHGlobal(bufferSize); - - Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8); - - if (NativeMethods.SetupDiGetDeviceInterfaceDetail(deviceInfoSet, ref DeviceInterfaceData, detailDataBuffer, bufferSize, ref bufferSize, ref da)) - { - IntPtr pDevicePathName = detailDataBuffer + 4; - - path = Marshal.PtrToStringAuto(pDevicePathName).ToUpper(CultureInfo.InvariantCulture); - Marshal.FreeHGlobal(detailDataBuffer); - - if (memberIndex == instance) return true; - } - else Marshal.FreeHGlobal(detailDataBuffer); - - - memberIndex++; - } - } - finally - { - if (deviceInfoSet != IntPtr.Zero) - { - NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet); - } - } - - return false; - } - - private static SafeFileHandle GetHandle(string devicePath) - { - devicePath = devicePath.ToUpper(CultureInfo.InvariantCulture); - - SafeFileHandle handle = NativeMethods.CreateFile(devicePath, (NativeMethods.GENERIC_WRITE | NativeMethods.GENERIC_READ), NativeMethods.FILE_SHARE_READ | NativeMethods.FILE_SHARE_WRITE, IntPtr.Zero, NativeMethods.OPEN_EXISTING, NativeMethods.FILE_ATTRIBUTE_NORMAL | NativeMethods.FILE_FLAG_OVERLAPPED, UIntPtr.Zero); - - if (handle == null || handle.IsInvalid) - { - throw new IOException("Unable to get SCP Virtual Bus Device handle"); - } - - return handle; - } - } - - internal static class NativeMethods - { - [StructLayout(LayoutKind.Sequential)] - internal struct SP_DEVICE_INTERFACE_DATA - { - internal int cbSize; - internal Guid InterfaceClassGuid; - internal int Flags; - internal IntPtr Reserved; - } - - internal const uint FILE_ATTRIBUTE_NORMAL = 0x80; - internal const uint FILE_FLAG_OVERLAPPED = 0x40000000; - internal const uint FILE_SHARE_READ = 1; - internal const uint FILE_SHARE_WRITE = 2; - internal const uint GENERIC_READ = 0x80000000; - internal const uint GENERIC_WRITE = 0x40000000; - internal const uint OPEN_EXISTING = 3; - internal const int DIGCF_PRESENT = 0x0002; - internal const int DIGCF_DEVICEINTERFACE = 0x0010; - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, UIntPtr hTemplateFile); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool DeviceIoControl(SafeFileHandle hDevice, int dwIoControlCode, byte[] lpInBuffer, int nInBufferSize, byte[] lpOutBuffer, int nOutBufferSize, ref int lpBytesReturned, IntPtr lpOverlapped); - - [DllImport("setupapi.dll", SetLastError = true)] - internal static extern int SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); - - [DllImport("setupapi.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool SetupDiEnumDeviceInterfaces(IntPtr hDevInfo, IntPtr devInfo, ref Guid interfaceClassGuid, int memberIndex, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern IntPtr SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, IntPtr hwndParent, int flags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr hDevInfo, ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData, IntPtr deviceInterfaceDetailData, int deviceInterfaceDetailDataSize, ref int requiredSize, ref SP_DEVICE_INTERFACE_DATA deviceInfoData); - } -} - - diff --git a/MiController/ScpDriverInterface/X360Controller.cs b/MiController/ScpDriverInterface/X360Controller.cs deleted file mode 100644 index 193627a..0000000 --- a/MiController/ScpDriverInterface/X360Controller.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; - -namespace ScpDriverInterface -{ - /// - /// A virtual XBox 360 Controller. After setting the desired values, use the GetReport() method to generate a controller report that can be used with ScpBus's Report() method. - /// - public class X360Controller - { - /// - /// Generates a new X360Controller object with the default initial state (no buttons pressed, all analog inputs 0). - /// - public X360Controller() - { - Buttons = X360Buttons.None; - LeftTrigger = 0; - RightTrigger = 0; - LeftStickX = 0; - LeftStickY = 0; - RightStickX = 0; - RightStickY = 0; - } - - /// - /// Generates a new X360Controller object. Optionally, you can specify the initial state of the controller. - /// - /// The pressed buttons. Use like flags (i.e. (X360Buttons.A | X360Buttons.X) would be mean both A and X are pressed). - /// Left trigger analog input. 0 to 255. - /// Right trigger analog input. 0 to 255. - /// Left stick X-axis. -32,768 to 32,767. - /// Left stick Y-axis. -32,768 to 32,767. - /// Right stick X-axis. -32,768 to 32,767. - /// Right stick Y-axis. -32,768 to 32,767. - public X360Controller(X360Buttons buttons, byte leftTrigger, byte rightTrigger, short leftStickX, short leftStickY, short rightStickX, short rightStickY) - { - Buttons = buttons; - LeftTrigger = leftTrigger; - RightTrigger = rightTrigger; - LeftStickX = leftStickX; - LeftStickY = leftStickY; - RightStickX = rightStickX; - RightStickY = rightStickY; - } - - /// - /// Generates a new X360Controller object with the same values as the specified X360Controller object. - /// - /// An X360Controller object to copy values from. - public X360Controller(X360Controller controller) - { - Buttons = controller.Buttons; - LeftTrigger = controller.LeftTrigger; - RightTrigger = controller.RightTrigger; - LeftStickX = controller.LeftStickX; - LeftStickY = controller.LeftStickY; - RightStickX = controller.RightStickX; - RightStickY = controller.RightStickY; - } - - /// - /// The controller's currently pressed buttons. Use the X360Button values like flags (i.e. (X360Buttons.A | X360Buttons.X) would be mean both A and X are pressed). - /// - public X360Buttons Buttons { get; set; } - - /// - /// The controller's left trigger analog input. Value can range from 0 to 255. - /// - public byte LeftTrigger { get; set; } - - /// - /// The controller's right trigger analog input. Value can range from 0 to 255. - /// - public byte RightTrigger { get; set; } - - /// - /// The controller's left stick X-axis. Value can range from -32,768 to 32,767. - /// - public short LeftStickX { get; set; } - - /// - /// The controller's left stick Y-axis. Value can range from -32,768 to 32,767. - /// - public short LeftStickY { get; set; } - - /// - /// The controller's right stick X-axis. Value can range from -32,768 to 32,767. - /// - public short RightStickX { get; set; } - - /// - /// The controller's right stick Y-axis. Value can range from -32,768 to 32,767. - /// - public short RightStickY { get; set; } - - /// - /// Generates a XBox 360 controller report as specified here: http://free60.org/wiki/GamePad#Input_report. This can be used with ScpBus's Report() method. - /// - /// A 20-byte XBox 360 controller report. - public byte[] GetReport() - { - byte[] bytes = new byte[20]; - - bytes[0] = 0x00; // Message type (input report) - bytes[1] = 0x14; // Message size (20 bytes) - - bytes[2] = (byte)((ushort)Buttons & 0xFF); // Buttons low - bytes[3] = (byte)((ushort)Buttons >> 8 & 0xFF); // Buttons high - - bytes[4] = LeftTrigger; // Left trigger - bytes[5] = RightTrigger; // Right trigger - - bytes[6] = (byte)(LeftStickX & 0xFF); // Left stick X-axis low - bytes[7] = (byte)(LeftStickX >> 8 & 0xFF); // Left stick X-axis high - bytes[8] = (byte)(LeftStickY & 0xFF); // Left stick Y-axis low - bytes[9] = (byte)(LeftStickY >> 8 & 0xFF); // Left stick Y-axis high - - bytes[10] = (byte)(RightStickX & 0xFF); // Right stick X-axis low - bytes[11] = (byte)(RightStickX >> 8 & 0xFF); // Right stick X-axis high - bytes[12] = (byte)(RightStickY & 0xFF); // Right stick Y-axis low - bytes[13] = (byte)(RightStickY >> 8 & 0xFF); // Right stick Y-axis high - - // Remaining bytes are unused - - return bytes; - } - } - - /// - /// The buttons to be used with an X360Controller object. - /// - [Flags] - public enum X360Buttons - { - None = 0, - - Up = 1 << 0, - Down = 1 << 1, - Left = 1 << 2, - Right = 1 << 3, - - Start = 1 << 4, - Back = 1 << 5, - - LeftStick = 1 << 6, - RightStick = 1 << 7, - - LeftBumper = 1 << 8, - RightBumper = 1 << 9, - - Logo = 1 << 10, - - A = 1 << 12, - B = 1 << 13, - X = 1 << 14, - Y = 1 << 15, - } -} diff --git a/MiController/Setup.iss b/MiController/Setup.iss index a7051aa..38dca0f 100644 --- a/MiController/Setup.iss +++ b/MiController/Setup.iss @@ -2,7 +2,7 @@ ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! #define MyAppName "Mi Controller" -#define MyAppVersion "1.0" +#define MyAppVersion "1.1" #define MyAppPublisher "TomaSoft" #define MyAppExeName "MiController.exe" @@ -35,7 +35,6 @@ Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{ [Files] Source: ".\bin\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion -Source: ".\bin\Release\ScpDriver\*"; DestDir: "{app}\ScpDriver"; Flags: ignoreversion recursesubdirs createallsubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files @@ -45,8 +44,6 @@ Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: de [Run] Filename: schtasks; Parameters: "/Create /SC ONLOGON /TN ""{#MyAppName}"" /TR ""{app}\{#MyAppExeName}"" /RL HIGHEST" ; Flags: nowait -Filename: "{app}\{#MyAppExeName}"; Parameters: "/install"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait skipifsilent [UninstallRun] -Filename: "{app}\{#MyAppExeName}"; Parameters: "/uninstall"; Filename: schtasks; Parameters: "/Delete /TN ""{#MyAppName}"" /F " diff --git a/MiController/Win32/DeviceEventArgs.cs b/MiController/Win32/DeviceEventArgs.cs new file mode 100644 index 0000000..6aec4a5 --- /dev/null +++ b/MiController/Win32/DeviceEventArgs.cs @@ -0,0 +1,19 @@ +using System; +using HidLibrary; + +namespace MiController.Win32 +{ + public class DeviceEventArgs : EventArgs + { + internal DeviceEventArgs(HidDevices.DeviceInfo info) + { + Path = info.Path; + Description = info.Description; + InstanceId = info.InstanceID; + } + + public string Path { get; set; } + public string Description { get; set; } + public string InstanceId { get; set; } + } +} \ No newline at end of file diff --git a/MiController/Win32/DeviceInfoEqualityComparer.cs b/MiController/Win32/DeviceInfoEqualityComparer.cs new file mode 100644 index 0000000..b921e61 --- /dev/null +++ b/MiController/Win32/DeviceInfoEqualityComparer.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using HidLibrary; + +namespace MiController.Win32 +{ + public class DeviceInfoEqualityComparer : IEqualityComparer + { + public bool Equals(HidDevices.DeviceInfo x, HidDevices.DeviceInfo y) => y != null && x != null && x.Path == y.Path; + public int GetHashCode(HidDevices.DeviceInfo di) => di.Path.GetHashCode(); + } +} \ No newline at end of file diff --git a/MiController/Win32/HidHide.cs b/MiController/Win32/HidHide.cs new file mode 100644 index 0000000..2f1d96c --- /dev/null +++ b/MiController/Win32/HidHide.cs @@ -0,0 +1,219 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using MiController.Win32.Native; +using Microsoft.Win32.SafeHandles; + +namespace MiController.Win32 +{ + public class HidHide : IDisposable + { + private const uint IOCTL_GET_WHITELIST = 0x80016000; + private const uint IOCTL_SET_WHITELIST = 0x80016004; + private const uint IOCTL_GET_BLACKLIST = 0x80016008; + private const uint IOCTL_SET_BLACKLIST = 0x8001600C; + private const uint IOCTL_GET_ACTIVE = 0x80016010; + private const uint IOCTL_SET_ACTIVE = 0x80016014; + + public HidHide() + { + Handle = Kernel32.CreateFile(@"\\.\HidHide", FileAccess.Read, FileShare.ReadWrite, IntPtr.Zero, + FileMode.Open, FileAttributes.Normal, IntPtr.Zero); + } + + private SafeFileHandle Handle { get; set; } + + public bool EnableHiding + { + get + { + var buffer = Marshal.AllocHGlobal(sizeof(bool)); + + try + { + Kernel32.DeviceIoControl( + Handle, IOCTL_GET_ACTIVE, + IntPtr.Zero, 0, // Input: none + buffer, 1, out _, // Output: buffer of length 1 + IntPtr.Zero + ); + + return Convert.ToBoolean(Marshal.ReadByte(buffer)); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + set + { + var buffer = Marshal.AllocHGlobal(sizeof(bool)); + + // Enable blocking logic, if not enabled already + try + { + Marshal.WriteByte(buffer, Convert.ToByte(value)); + + // Check return value for success + Kernel32.DeviceIoControl( + Handle, IOCTL_SET_ACTIVE, + buffer, sizeof(bool), // Input: 1 bool + IntPtr.Zero, 0, out _, // Output: ignored + IntPtr.Zero + ); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + } + + public bool SetDeviceHideStatus(string instance, bool state) + { + var buffer = IntPtr.Zero; + + try + { + var blacklist = GetBlacklist(); + var newlist = state + ? blacklist.Concat(new[] { instance }).Distinct() + : blacklist.Where(e => e != instance).Distinct(); + + buffer = newlist.StringArrayToMultiSzPointer(out var length); + + // Submit new list + // Check return value for success + return Kernel32.DeviceIoControl( + Handle, IOCTL_SET_BLACKLIST, + buffer, length, + IntPtr.Zero, 0, out _, + IntPtr.Zero + ); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + public bool WhitelistCurrentApplication() + { + var buffer = IntPtr.Zero; + + // Manipulate allow-list and submit it + try + { + var appPath = Assembly.GetEntryAssembly()?.Location; + var dosPath = VolumeHelper.PathToDosDevicePath(appPath); + + var whitelist = GetWhitelist().ToArray(); + + if (whitelist.Contains(dosPath)) + return true; + + buffer = whitelist + .Concat(new[] { dosPath }) + .Distinct() + .StringArrayToMultiSzPointer(out var length); + + // Submit new list + // Check return value for success + return Kernel32.DeviceIoControl( + Handle, + IOCTL_SET_WHITELIST, + buffer, length, + IntPtr.Zero, 0, out _, + IntPtr.Zero + ); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + private IEnumerable GetBlacklist() + { + // List of blocked instances + var buffer = IntPtr.Zero; + + // Get existing list of blocked instances + // This is important to not discard entries other processes potentially made + // Always get the current list before altering/submitting it + try + { + // Get required buffer size + Kernel32.DeviceIoControl( + Handle, IOCTL_GET_BLACKLIST, + IntPtr.Zero, 0, // Input: none + IntPtr.Zero, 0, out var required, // Output: buffer size + IntPtr.Zero + ); + + buffer = Marshal.AllocHGlobal(required); + + // Get actual buffer content + Kernel32.DeviceIoControl( + Handle, IOCTL_GET_BLACKLIST, + IntPtr.Zero, 0, // Input: none + buffer, required, out _, // Output: buffer of known length + IntPtr.Zero + ); + + // Store existing block-list in a more manageable "C#" fashion + return buffer.MultiSzPointerToStringArray(required).ToList(); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + private IEnumerable GetWhitelist() + { + // List of blocked instances + var buffer = IntPtr.Zero; + + // Get existing list of blocked instances + // This is important to not discard entries other processes potentially made + // Always get the current list before altering/submitting it + try + { + // Get required buffer size + Kernel32.DeviceIoControl( + Handle, IOCTL_GET_WHITELIST, + IntPtr.Zero, 0, // Input: none + IntPtr.Zero, 0, out var required, // Output: buffer size + IntPtr.Zero + ); + + buffer = Marshal.AllocHGlobal(required); + + // Get actual buffer content + Kernel32.DeviceIoControl( + Handle, IOCTL_GET_WHITELIST, + IntPtr.Zero, 0, // Input: none + buffer, required, out _, // Output: buffer of known length + IntPtr.Zero + ); + + // Store existing block-list in a more manageable "C#" fashion + return buffer.MultiSzPointerToStringArray(required).ToList(); + } + finally + { + Marshal.FreeHGlobal(buffer); + } + } + + public void Dispose() + { + Handle.Dispose(); + } + } +} diff --git a/MiController/Win32/HidMonitor.cs b/MiController/Win32/HidMonitor.cs new file mode 100644 index 0000000..191fcec --- /dev/null +++ b/MiController/Win32/HidMonitor.cs @@ -0,0 +1,74 @@ +using System; +using System.Linq; +using System.Threading; +using HidLibrary; + +namespace MiController.Win32 +{ + public class HidMonitor + { + public event EventHandler DeviceAttached; + public event EventHandler DeviceRemoved; + private readonly Timer _monitorTimer; + private readonly string _filter; + private HidDevices.DeviceInfo[] _seenDevices; + + + public HidMonitor(string filter) + { + // Initializing HID device monitor with filter + _filter = filter; + _monitorTimer = new Timer(SearchForDevice); + _seenDevices = Array.Empty(); + } + + public void Start() + { + // Start monitoring for filter + _monitorTimer.Change(0, 5000); + } + + public void Stop() + { + // Stop monitoring for filter + _monitorTimer.Change(Timeout.Infinite, Timeout.Infinite); + } + + private void SearchForDevice(object state) + { + var filter = _filter.ToLower(); + var devices = HidDevices + .EnumerateDevices() + .Where(p => p.Path.ToLower().Contains(filter)) + .ToArray(); + + var comp = new DeviceInfoEqualityComparer(); + + // Get all the devices that has connected since the last check + var newDevices = devices.Except(_seenDevices, comp); + + // Get all the device that has disconnected since the last check + var removedDevices = _seenDevices.Except(devices, comp); + + foreach (var device in newDevices) + { + // Detected attached HID devices matching filter + DeviceAttached?.Invoke(this, new DeviceEventArgs(device)); + } + + foreach (var device in removedDevices) + { + // Detected removed HID devices matching filter + DeviceRemoved?.Invoke(this, new DeviceEventArgs(device)); + } + + _seenDevices = devices; + } + + public void Dispose() + { + // De-initilizing HID monitor + _monitorTimer.Dispose(); + } + } +} diff --git a/MiController/Win32/Native/Kernel32.cs b/MiController/Win32/Native/Kernel32.cs new file mode 100644 index 0000000..7ea9a05 --- /dev/null +++ b/MiController/Win32/Native/Kernel32.cs @@ -0,0 +1,31 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace MiController.Win32.Native +{ + public static class Kernel32 + { + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern SafeFileHandle CreateFile( + string lpFileName, + [MarshalAs(UnmanagedType.U4)] FileAccess dwDesiredAccess, + [MarshalAs(UnmanagedType.U4)] FileShare dwShareMode, + IntPtr lpSecurityAttributes, + [MarshalAs(UnmanagedType.U4)] FileMode dwCreationDisposition, + [MarshalAs(UnmanagedType.U4)] FileAttributes dwFlagsAndAttributes, + IntPtr hTemplateFile); + + [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool DeviceIoControl( + SafeFileHandle hDevice, + uint dwIoControlCode, + IntPtr lpInBuffer, + int nInBufferSize, + IntPtr lpOutBuffer, + int nOutBufferSize, + out int lpBytesReturned, + IntPtr lpOverlapped); + } +} \ No newline at end of file diff --git a/MiController/Win32/VolumeHelper.cs b/MiController/Win32/VolumeHelper.cs new file mode 100644 index 0000000..2c037ce --- /dev/null +++ b/MiController/Win32/VolumeHelper.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace MiController.Win32 +{ + // Taken from https://vigem.org/projects/HidHide/API-Documentation/ + + /// + /// Path manipulation and volume helper methods. + /// + internal static class VolumeHelper + { + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + private static extern bool GetVolumePathNamesForVolumeNameW( + [MarshalAs(UnmanagedType.LPWStr)] string lpszVolumeName, + [MarshalAs(UnmanagedType.LPWStr)] [Out] + StringBuilder lpszVolumeNamePaths, uint cchBuferLength, + ref uint lpcchReturnLength); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern IntPtr FindFirstVolume([Out] StringBuilder lpszVolumeName, + uint cchBufferLength); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern bool FindNextVolume(IntPtr hFindVolume, [Out] StringBuilder lpszVolumeName, + uint cchBufferLength); + + [DllImport("kernel32.dll", SetLastError = true)] + private static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax); + + private class VolumeMeta + { + public string DriveLetter { get; set; } + + public string VolumeName { get; set; } + + public string DevicePath { get; set; } + } + + /// + /// Curates and returns a collection of volume to path mappings. + /// + /// A collection of . + private static IEnumerable GetVolumeMappings() + { + var volumeName = new StringBuilder(ushort.MaxValue); + var pathName = new StringBuilder(ushort.MaxValue); + var mountPoint = new StringBuilder(ushort.MaxValue); + uint returnLength = 0; + + var volumeHandle = FindFirstVolume(volumeName, ushort.MaxValue); + + do + { + var volume = volumeName.ToString(); + + if (!GetVolumePathNamesForVolumeNameW(volume, mountPoint, ushort.MaxValue, ref returnLength)) + continue; + + // Extract volume name for use with QueryDosDevice + var deviceName = volume.Substring(4, volume.Length - 1 - 4); + + // Grab device path + returnLength = QueryDosDevice(deviceName, pathName, ushort.MaxValue); + + if (returnLength <= 0) + continue; + + yield return new VolumeMeta + { + DriveLetter = mountPoint.ToString(), + VolumeName = volume, + DevicePath = pathName.ToString() + }; + } while (FindNextVolume(volumeHandle, volumeName, ushort.MaxValue)); + } + + /// + /// Checks if a path is a junction point. + /// + /// A instance. + /// True if it's a junction, false otherwise. + private static bool IsPathReparsePoint(FileSystemInfo di) + { + return di.Attributes.HasFlag(FileAttributes.ReparsePoint); + } + + /// + /// Helper to make paths comparable. + /// + /// The source path. + /// The normalized path. + private static string NormalizePath(string path) + { + return Path.GetFullPath(new Uri(path).LocalPath) + .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) + .ToUpperInvariant(); + } + + /// + /// Translates a user-land file path to "DOS device" path. + /// + /// The file path in normal namespace format. + /// The device namespace path (DOS device). + public static string PathToDosDevicePath(string path) + { + if (!File.Exists(path)) + throw new ArgumentException("The supplied file path doesn't exist", nameof(path)); + + var filePart = Path.GetFileName(path); + var pathPart = Path.GetDirectoryName(path); + + if (string.IsNullOrEmpty(pathPart)) + throw new IOException("Couldn't resolve directory"); + + var pathNoRoot = string.Empty; + var devicePath = string.Empty; + + // Walk up the directory tree to get the "deepest" potential junction + for (var current = new DirectoryInfo(pathPart); + current != null && current.Exists; + current = Directory.GetParent(current.FullName)) + { + if (!IsPathReparsePoint(current)) continue; + + devicePath = GetVolumeMappings().FirstOrDefault(m => + !string.IsNullOrEmpty(m.DriveLetter) && + NormalizePath(m.DriveLetter) == NormalizePath(current.FullName)) + ?.DevicePath; + + pathNoRoot = pathPart.Substring(current.FullName.Length); + + break; + } + + // No junctions found, translate original path + if (string.IsNullOrEmpty(devicePath)) + { + var driveLetter = Path.GetPathRoot(pathPart); + devicePath = GetVolumeMappings().FirstOrDefault(m => + m.DriveLetter.Equals(driveLetter, StringComparison.InvariantCultureIgnoreCase))?.DevicePath; + pathNoRoot = pathPart.Substring(Path.GetPathRoot(pathPart).Length); + } + + if (string.IsNullOrEmpty(devicePath)) + throw new IOException("Couldn't resolve device path"); + + var fullDevicePath = new StringBuilder(); + + // Build new DOS Device path + fullDevicePath.AppendFormat("{0}{1}", devicePath, Path.DirectorySeparatorChar); + fullDevicePath.Append(Path.Combine(pathNoRoot, filePart).TrimStart(Path.DirectorySeparatorChar)); + + return fullDevicePath.ToString(); + } + } +} diff --git a/MiController/Win32/WideStringHelper.cs b/MiController/Win32/WideStringHelper.cs new file mode 100644 index 0000000..37117d9 --- /dev/null +++ b/MiController/Win32/WideStringHelper.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace MiController.Win32 +{ + // Taken from https://vigem.org/projects/HidHide/API-Documentation/ + + /// + /// String manipulation helper methods. + /// + internal static class WideStringHelper + { + /// + /// Converts an array of into a double-null-terminated multi-byte character memory block. + /// + /// Source array of strings. + /// The length of the resulting byte array. + /// The allocated memory buffer. + public static IntPtr StringArrayToMultiSzPointer(this IEnumerable instances, out int length) + { + // Temporary byte array + IEnumerable multiSz = new List(); + + // Convert each string into wide multi-byte and add NULL-terminator in between + multiSz = instances.Aggregate(multiSz, + (current, entry) => current.Concat(Encoding.Unicode.GetBytes(entry)) + .Concat(Encoding.Unicode.GetBytes(new[] { char.MinValue }))); + + // Add another NULL-terminator to signal end of the list + multiSz = multiSz.Concat(Encoding.Unicode.GetBytes(new[] { char.MinValue })); + + // Convert expression to array + var multiSzArray = multiSz.ToArray(); + + // Convert array to managed native buffer + var buffer = Marshal.AllocHGlobal(multiSzArray.Length); + Marshal.Copy(multiSzArray, 0, buffer, multiSzArray.Length); + + length = multiSzArray.Length; + + // Return usable buffer, don't forget to free! + return buffer; + } + + /// + /// Converts a double-null-terminated multi-byte character memory block into a string array. + /// + /// The memory buffer. + /// The size in bytes of the memory buffer. + /// The extracted string array. + public static IEnumerable MultiSzPointerToStringArray(this IntPtr buffer, int length) + { + // Temporary byte array + var rawBuffer = new byte[length]; + + // Grab data from buffer + Marshal.Copy(buffer, rawBuffer, 0, length); + + // Trims away potential redundant NULL-characters and splits at NULL-terminator + return Encoding.Unicode.GetString(rawBuffer) + .TrimEnd(char.MinValue) + .Split(char.MinValue) + .Where(s => !String.IsNullOrWhiteSpace(s)); + } + } +} diff --git a/MiController/XInputManager.cs b/MiController/XInputManager.cs new file mode 100644 index 0000000..3ce470b --- /dev/null +++ b/MiController/XInputManager.cs @@ -0,0 +1,132 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using MiController.Win32; +using Nefarius.ViGEm.Client; + + +namespace MiController +{ + public class XInputManager : IDisposable + { + public event EventHandler GamepadRunning; + public event EventHandler GamepadRemoved; + + private readonly Dictionary _gamepads; + private readonly ViGEmClient _viGEmClient; + + private readonly SynchronizationContext _syncContext; + + public XInputManager() + { + //Initializing ViGEm client + _viGEmClient = new ViGEmClient(); + _gamepads = new Dictionary(); + _syncContext = SynchronizationContext.Current; + } + + public void Dispose() + { + // Cleaning up running gamepads + + // When calling Stop() the device will get removed from the dictionary, do this to avoid exceptions in enumeration + var devices = _gamepads.Values.ToArray(); + + foreach (var device in devices) + { + StopAndRemove(device); + device.Dispose(); + } + + // Deinitializing ViGEm client + _viGEmClient.Dispose(); + } + + + public bool AddAndStart(string device, string instance) + { + if (Contains(device)) + { + // Requested addition of already existing device + return false; + } + + // Adding device + var gamepad = new XiaomiGamepad(device, instance, _viGEmClient); + if (_gamepads.ContainsKey(device)) + { + _gamepads[device].Dispose(); + _gamepads.Remove(device); + } + _gamepads.Add(device, gamepad); + + gamepad.Started += Gamepad_Started; + gamepad.Ended += Gamepad_Ended; + + // Hiding device instance + using (var hh = new HidHide()) + { + hh.SetDeviceHideStatus(gamepad.InstanceId, true); + } + + // Starting device + gamepad.Start(); + + return true; + } + + public void StopAndRemove(string device) + { + if (!Contains(device)) + { + // Requested removal of non-existing device + return; + } + var gamepad = _gamepads[device]; + StopAndRemove(gamepad); + } + + private void StopAndRemove(XiaomiGamepad gamepad) + { + // Stopping device + if (gamepad.IsActive) + { + gamepad.Stop(); + } + + gamepad.Started -= Gamepad_Started; + gamepad.Ended -= Gamepad_Ended; + + // Un-hiding device instance + using (var hh = new HidHide()) + { + hh.SetDeviceHideStatus(gamepad.InstanceId, false); + } + + // De-initializing and removing device + _gamepads.Remove(gamepad.Device.DevicePath); + gamepad.Dispose(); + } + + public bool Contains(string device) => _gamepads.ContainsKey(device); + + private void Gamepad_Ended(object sender, EventArgs eventArgs) + { + _syncContext.Post(o => + { + if (sender is XiaomiGamepad gamepad && !gamepad.CleanEnd) + { + StopAndRemove(gamepad); + } + + GamepadRemoved?.Invoke(this, EventArgs.Empty); + }, null); + } + + private void Gamepad_Started(object sender, EventArgs eventArgs) + { + _syncContext.Post(o => GamepadRunning?.Invoke(this, EventArgs.Empty), null); + } + } +} diff --git a/MiController/XiaomiGamepad.cs b/MiController/XiaomiGamepad.cs index 7ef2b9f..21f808e 100644 --- a/MiController/XiaomiGamepad.cs +++ b/MiController/XiaomiGamepad.cs @@ -1,200 +1,309 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Threading; using System.Threading.Tasks; using HidLibrary; -using ScpDriverInterface; -using System.Threading; -using System.Runtime.InteropServices; - +using Nefarius.ViGEm.Client; +using Nefarius.ViGEm.Client.Exceptions; +using Nefarius.ViGEm.Client.Targets; +using Nefarius.ViGEm.Client.Targets.Xbox360; namespace MiController { - public class XiaomiGamepad + public class XiaomiGamepad : IDisposable { - private byte[] Vibration = { 0x20, 0x00, 0x00 }; - private Mutex rumble_mutex = new Mutex(); + private static readonly Xbox360Button[][] HatSwitches = { + new [] { Xbox360Button.Up }, + new [] { Xbox360Button.Up, Xbox360Button.Right }, + new [] { Xbox360Button.Right }, + new [] { Xbox360Button.Right, Xbox360Button.Down }, + new [] { Xbox360Button.Down }, + new [] { Xbox360Button.Down, Xbox360Button.Left }, + new [] { Xbox360Button.Left }, + new [] { Xbox360Button.Left, Xbox360Button.Up }, + }; - public XiaomiGamepad(HidDevice Device, ScpBus scpBus, int index) + public event EventHandler Started; + public event EventHandler Ended; + + private readonly IXbox360Controller _target; + private readonly Thread _inputThread; + private readonly CancellationTokenSource _cts; + private readonly Timer _vibrationTimer; + private static readonly IHidEnumerator DeviceEnumerator = new HidFastReadEnumerator(); + + public XiaomiGamepad(string device, string instance, ViGEmClient client) { - Device.WriteFeatureData(Vibration); + Device = DeviceEnumerator.GetDevice(device) as HidFastReadDevice; + if (Device != null) + { + Device.MonitorDeviceEvents = false; + } - Thread rThread = new Thread(() => rumble_thread(Device)); - // rThread.Priority = ThreadPriority.BelowNormal; - rThread.Start(); + _target = client.CreateXbox360Controller(); + _target.AutoSubmitReport = false; + _target.FeedbackReceived += Target_OnFeedbackReceived; - Thread iThread = new Thread(() => input_thread(Device, scpBus, index)); - iThread.Priority = ThreadPriority.Highest; - iThread.Start(); + // TODO mark the threads as background? + _inputThread = new Thread(DeviceWorker); + + _cts = new CancellationTokenSource(); + _vibrationTimer = new Timer(VibrationTimer_Trigger); + + LedNumber = 0xFF; + InstanceId = instance; } - private void rumble_thread(HidDevice Device) + public HidFastReadDevice Device { get; } + + public ushort LedNumber { get; set; } + + public ushort BatteryLevel { get; private set; } + + public bool IsActive => _inputThread.IsAlive; + public bool CleanEnd => _cts.IsCancellationRequested; + + public bool ExclusiveMode { get; private set; } + + public string InstanceId { get; } + + public void Dispose() { - byte[] local_vibration = { 0x20, 0x00, 0x00 }; - while (true) + // De-initializing XiaomiGamepad handler for device + if (_inputThread.IsAlive) + Stop(); + + Device.Dispose(); + _cts.Dispose(); + } + + public void Start() + { + _inputThread.Start(); + } + + public void Stop() + { + if (_cts.IsCancellationRequested) { - rumble_mutex.WaitOne(); - if (local_vibration[2] != Vibration[2] || Vibration[1] != local_vibration[1]) + // Thread stop for device already requested + return; + } + + // Requesting thread stop for device + _cts.Cancel(); + _inputThread.Join(); + } + + private void DeviceWorker() + { + // Starting worker thread for device + + // Open HID device to read input from the gamepad + Device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.Exclusive); + ExclusiveMode = true; + + // If exclusive mode is not available, retry in shared mode. + if (!Device.IsOpen) + { + // Cannot access HID device in exclusive mode, retrying in shared mode + + Device.OpenDevice(DeviceMode.Overlapped, DeviceMode.Overlapped, ShareMode.ShareRead | ShareMode.ShareWrite); + ExclusiveMode = false; + + if (!Device.IsOpen) { - local_vibration[2] = Vibration[2]; - local_vibration[1] = Vibration[1]; - rumble_mutex.ReleaseMutex(); - Device.WriteFeatureData(local_vibration); - //Console.WriteLine("Big Motor: {0}, Small Motor: {1}", Vibration[2], Vibration[1]); + // Cannot open HID device + Device.CloseDevice(); + Ended?.Invoke(this, EventArgs.Empty); + return; } - else + } + + // Init Xiaomi Gamepad vibration + Device.WriteFeatureData(new byte[] { 0x20, 0x00, 0x00 }); + + // Connect the virtual Xbox360 gamepad + try + { + // Connecting to ViGEm client + _target.Connect(); + } + catch (VigemAlreadyConnectedException) + { + // ViGEm client was already opened, closing and reopening it + _target.Disconnect(); + _target.Connect(); + } + + Started?.Invoke(this, EventArgs.Empty); + + var token = _cts.Token; + + while (!token.IsCancellationRequested) + { + // Is device has been closed, exit the loop + if (!Device.IsOpen) + break; + + // Otherwise read a report + var hidReport = Device.FastReadReport(1000); + + if (hidReport.ReadStatus == HidDeviceData.ReadStatus.WaitTimedOut) + continue; + if (hidReport.ReadStatus != HidDeviceData.ReadStatus.Success) { - rumble_mutex.ReleaseMutex(); + // Cannot read HID report for device + break; } - Thread.Sleep(20); + + var data = hidReport.Data; + + /* + [0] Buttons state, 1 bit per button + [1] Buttons state, 1 bit per button + [2] 0x00 + [3] D-Pad + [4] Left thumb, X axis + [5] Left thumb, Y axis + [6] Right thumb, X axis + [7] Right thumb, Y axis + [8] 0x00 + [9] 0x00 + [10] L trigger + [11] R trigger + [12] Accelerometer axis 1 + [13] Accelerometer axis 1 + [14] Accelerometer axis 2 + [15] Accelerometer axis 2 + [16] Accelerometer axis 3 + [17] Accelerometer axis 3 + [18] Battery level + [19] MI button + */ + + lock (_target) + { + _target.SetButtonState(Xbox360Button.A, GetBit(data[0], 0)); + _target.SetButtonState(Xbox360Button.B, GetBit(data[0], 1)); + _target.SetButtonState(Xbox360Button.X, GetBit(data[0], 3)); + _target.SetButtonState(Xbox360Button.Y, GetBit(data[0], 4)); + _target.SetButtonState(Xbox360Button.LeftShoulder, GetBit(data[0], 6)); + _target.SetButtonState(Xbox360Button.RightShoulder, GetBit(data[0], 7)); + + _target.SetButtonState(Xbox360Button.Back, GetBit(data[1], 2)); + _target.SetButtonState(Xbox360Button.Start, GetBit(data[1], 3)); + _target.SetButtonState(Xbox360Button.LeftThumb, GetBit(data[1], 5)); + _target.SetButtonState(Xbox360Button.RightThumb, GetBit(data[1], 6)); + + // Reset Hat switch status, as is set to 15 (all directions set, impossible state) + _target.SetButtonState(Xbox360Button.Up, false); + _target.SetButtonState(Xbox360Button.Left, false); + _target.SetButtonState(Xbox360Button.Down, false); + _target.SetButtonState(Xbox360Button.Right, false); + + if (data[3] < 8) + { + var btns = HatSwitches[data[3]]; + // Hat Switch is a number from 0 to 7, where 0 is Up, 1 is Up-Left, etc. + foreach (var b in btns) + _target.SetButtonState(b, true); + } + + // Analog axis + _target.SetAxisValue(Xbox360Axis.LeftThumbX, MapAnalog(data[4])); + _target.SetAxisValue(Xbox360Axis.LeftThumbY, MapAnalog(data[5], true)); + _target.SetAxisValue(Xbox360Axis.RightThumbX, MapAnalog(data[6])); + _target.SetAxisValue(Xbox360Axis.RightThumbY, MapAnalog(data[7], true)); + + // Triggers + _target.SetSliderValue(Xbox360Slider.LeftTrigger, data[10]); + _target.SetSliderValue(Xbox360Slider.RightTrigger, data[11]); + + // Logo ("home") button + if (GetBit(data[19], 0)) + { + _target.SetButtonState(Xbox360Button.Guide, true); + Task.Delay(200, token).ContinueWith(DelayedReleaseGuideButton); + } + + // Update battery level + BatteryLevel = data[18]; + + _target.SubmitReport(); + } + + } + + // Disconnect the virtual Xbox360 gamepad + // Let Dispose handle that, otherwise it will rise a NotPluggedIn exception + // Disconnecting ViGEm client + _target.Disconnect(); + + // Close the HID device + // Closing HID device + Device.CloseDevice(); + + // Exiting worker thread for device + Ended?.Invoke(this, EventArgs.Empty); + } + + private static bool GetBit(byte b, int bit) + { + return ((b >> bit) & 1) != 0; + } + + private static short MapAnalog(byte value, bool invert = false) + { + return (short)(value * 257 * (invert ? -1 : 1) + short.MinValue); + } + + private void DelayedReleaseGuideButton(Task t) + { + lock (_target) + { + _target.SetButtonState(Xbox360Button.Guide, false); + _target.SubmitReport(); } } - private void input_thread(HidDevice Device, ScpBus scpBus, int index) + private void VibrationTimer_Trigger(object o) { - scpBus.PlugIn(index); - X360Controller controller = new X360Controller(); - int timeout = 30; - long last_changed = 0; - long last_mi_button = 0; - while (true) + Task.Run(() => { + lock (_vibrationTimer) + { + if (Device.IsOpen) + Device.WriteFeatureData(new byte[] { 0x20, 0x00, 0x00 }); + + // Vibration feedback reset after 3 seconds for device + } + }); + } + + private void Target_OnFeedbackReceived(object sender, Xbox360FeedbackReceivedEventArgs e) + { + byte[] data = { 0x20, e.SmallMotor, e.LargeMotor }; + + Task.Run(() => { + + lock (_vibrationTimer) + { + if (!Device.IsOpen) + return; + + Device.WriteFeatureData(data); + } + + var timeout = e.SmallMotor > 0 || e.LargeMotor > 0 ? 3000 : Timeout.Infinite; + _vibrationTimer.Change(timeout, Timeout.Infinite); + + }); + + if (LedNumber != e.LedNumber) { - HidDeviceData data = Device.Read(timeout); - var currentState = data.Data; - bool changed = false; - if (data.Status == HidDeviceData.ReadStatus.Success && currentState.Length >= 21 && currentState[0] == 4) - { - //Console.WriteLine(Program.ByteArrayToHexString(currentState)); - X360Buttons Buttons = X360Buttons.None; - if ((currentState[1] & 1) != 0) Buttons |= X360Buttons.A; - if ((currentState[1] & 2) != 0) Buttons |= X360Buttons.B; - if ((currentState[1] & 8) != 0) Buttons |= X360Buttons.X; - if ((currentState[1] & 16) != 0) Buttons |= X360Buttons.Y; - if ((currentState[1] & 64) != 0) Buttons |= X360Buttons.LeftBumper; - if ((currentState[1] & 128) != 0) Buttons |= X360Buttons.RightBumper; - - if ((currentState[2] & 32) != 0) Buttons |= X360Buttons.LeftStick; - if ((currentState[2] & 64) != 0) Buttons |= X360Buttons.RightStick; - - if (currentState[4] != 15) - { - if (currentState[4] == 0 || currentState[4] == 1 || currentState[4] == 7) Buttons |= X360Buttons.Up; - if (currentState[4] == 4 || currentState[4] == 3 || currentState[4] == 5) Buttons |= X360Buttons.Down; - if (currentState[4] == 6 || currentState[4] == 5 || currentState[4] == 7) Buttons |= X360Buttons.Left; - if (currentState[4] == 2 || currentState[4] == 1 || currentState[4] == 3) Buttons |= X360Buttons.Right; - } - - if ((currentState[2] & 8) != 0) Buttons |= X360Buttons.Start; - if ((currentState[2] & 4) != 0) Buttons |= X360Buttons.Back; - - - - if ((currentState[20] & 1) != 0) - { - last_mi_button = (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond); - Buttons |= X360Buttons.Logo; - } - if (last_mi_button != 0) Buttons |= X360Buttons.Logo; - - - if (controller.Buttons != Buttons) - { - changed = true; - controller.Buttons = Buttons; - } - - short LeftStickX = (short)((Math.Max(-127.0, currentState[5] - 128) / 127) * 32767); - if (LeftStickX == -32767) - LeftStickX = -32768; - - if (LeftStickX != controller.LeftStickX) - { - changed = true; - controller.LeftStickX = LeftStickX; - } - - short LeftStickY = (short)((Math.Max(-127.0, currentState[6] - 128) / 127) * -32767); - if (LeftStickY == -32767) - LeftStickY = -32768; - - if (LeftStickY != controller.LeftStickY) - { - changed = true; - controller.LeftStickY = LeftStickY; - } - - short RightStickX = (short)((Math.Max(-127.0, currentState[7] - 128) / 127) * 32767); - if (RightStickX == -32767) - RightStickX = -32768; - - if (RightStickX != controller.RightStickX) - { - changed = true; - controller.RightStickX = RightStickX; - } - - short RightStickY = (short)((Math.Max(-127.0, currentState[8] - 128) / 127) * -32767); - if (RightStickY == -32767) - RightStickY = -32768; - - if (RightStickY != controller.RightStickY) - { - changed = true; - controller.RightStickY = RightStickY; - } - - if (controller.LeftTrigger != currentState[11]) - { - changed = true; - controller.LeftTrigger = currentState[11]; - } - - if (controller.RightTrigger != currentState[12]) - { - changed = true; - controller.RightTrigger = currentState[12]; - - } - } - - if (data.Status == HidDeviceData.ReadStatus.WaitTimedOut || (!changed && ((last_changed + timeout) < (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond)))) - { - changed = true; - } - - if (changed) - { - //Console.WriteLine("changed"); - //Console.WriteLine((DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond)); - byte[] outputReport = new byte[8]; - scpBus.Report(index, controller.GetReport(), outputReport); - - if (outputReport[1] == 0x08) - { - byte bigMotor = outputReport[3]; - byte smallMotor = outputReport[4]; - rumble_mutex.WaitOne(); - if (bigMotor != Vibration[2] || Vibration[1] != smallMotor) - { - Vibration[1] = smallMotor; - Vibration[2] = bigMotor; - } - rumble_mutex.ReleaseMutex(); - } - - if (last_mi_button != 0) - { - if ((last_mi_button + 100) < (DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond)) - { - last_mi_button = 0; - controller.Buttons ^= X360Buttons.Logo; - } - } - - last_changed = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; - } + LedNumber = e.LedNumber; + // TODO raise event here } } + } } diff --git a/MiController/app.config b/MiController/app.config new file mode 100644 index 0000000..04fcaeb --- /dev/null +++ b/MiController/app.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/MiController/packages.config b/MiController/packages.config new file mode 100644 index 0000000..8f3ff2b --- /dev/null +++ b/MiController/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file