Skip to content

Commit

Permalink
Add device state management in HidLibrary
Browse files Browse the repository at this point in the history
  • Loading branch information
dancol90 committed Apr 20, 2020
1 parent 3cc6b1a commit abd85ff
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 9 deletions.
9 changes: 9 additions & 0 deletions Source/HidLibrary/HidDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,15 @@ public bool WriteFeatureData(byte[] data)
return success;
}

public void SetState(bool enabled)
{
if (!HidDevices.SetDeviceState(_devicePath, enabled))
{
var verb = enabled ? "enable" : "disable";
throw new Exception($"Cannot {verb} the device");
}
}

protected static void EndRead(IAsyncResult ar)
{
var hidAsyncState = (HidAsyncState)ar.AsyncState;
Expand Down
71 changes: 64 additions & 7 deletions Source/HidLibrary/HidDevices.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,69 @@ public static IEnumerable<string> EnumeratePaths(string filter)
.Where(x => x.Contains(f));
}

public static bool SetDeviceState(string interfacePath, bool state)
{
var iface = EnumerateDevicesInterfaces(false)
.Where(ei =>
{
var devicePath = GetDevicePath(ei.DeviceInfoSet, ei.DeviceInterfaceData);
return devicePath.ToLower() == interfacePath.ToLower();
})
.FirstOrDefault();

if (iface == null)
return false;

var header = new NativeMethods.SP_CLASSINSTALL_HEADER();
header.cbSize = (int)Marshal.SizeOf(header);
header.InstallFunction = NativeMethods.DIF_PROPERTYCHANGE;

var propchangeparams = new NativeMethods.SP_PROPCHANGE_PARAMS
{
ClassInstallHeader = header,
StateChange = state ? NativeMethods.DICS_ENABLE : NativeMethods.DICS_DISABLE,
Scope = NativeMethods.DICS_FLAG_GLOBAL,
HwProfile = 0
};

NativeMethods.SetupDiSetClassInstallParams(iface.DeviceInfoSet, ref iface.DeviceInfoData, ref propchangeparams, (UInt32)Marshal.SizeOf(propchangeparams));

if (Marshal.GetLastWin32Error() != 0)
return false;

NativeMethods.SetupDiChangeState(iface.DeviceInfoSet, ref iface.DeviceInfoData);

if (Marshal.GetLastWin32Error() != 0)
return false;

return true;
}

internal class DeviceInfo { public string Path { get; set; } public string Description { get; set; } }

internal class DeviceInterfaceInfo
{
internal IntPtr DeviceInfoSet;
internal NativeMethods.SP_DEVINFO_DATA DeviceInfoData;
internal NativeMethods.SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
internal int DeviceInterfaceIndex;
};

internal static IEnumerable<DeviceInfo> EnumerateDevices()
{
var devices = new List<DeviceInfo>();
return EnumerateDevicesInterfaces().Select(ei => {
var devicePath = GetDevicePath(ei.DeviceInfoSet, ei.DeviceInterfaceData);
var description = GetBusReportedDeviceDescription(ei.DeviceInfoSet, ref ei.DeviceInfoData) ??
GetDeviceDescription(ei.DeviceInfoSet, ref ei.DeviceInfoData);
return new DeviceInfo { Path = devicePath, Description = description };
});
}

internal static IEnumerable<DeviceInterfaceInfo> EnumerateDevicesInterfaces(bool presentOnly = true)
{
var hidClass = HidClassGuid;
var deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidClass, null, 0, NativeMethods.DIGCF_PRESENT | NativeMethods.DIGCF_DEVICEINTERFACE);
var flags = NativeMethods.DIGCF_DEVICEINTERFACE | (presentOnly ? NativeMethods.DIGCF_PRESENT : 0);
var deviceInfoSet = NativeMethods.SetupDiGetClassDevs(ref hidClass, null, 0, flags);

if (deviceInfoSet.ToInt64() != NativeMethods.INVALID_HANDLE_VALUE)
{
Expand All @@ -81,15 +137,16 @@ internal static IEnumerable<DeviceInfo> EnumerateDevices()
while (NativeMethods.SetupDiEnumDeviceInterfaces(deviceInfoSet, ref deviceInfoData, ref hidClass, deviceInterfaceIndex, ref deviceInterfaceData))
{
deviceInterfaceIndex++;
var devicePath = GetDevicePath(deviceInfoSet, deviceInterfaceData);
var description = GetBusReportedDeviceDescription(deviceInfoSet, ref deviceInfoData) ??
GetDeviceDescription(deviceInfoSet, ref deviceInfoData);
devices.Add(new DeviceInfo { Path = devicePath, Description = description });
yield return new DeviceInterfaceInfo {
DeviceInfoSet = deviceInfoSet,
DeviceInfoData = deviceInfoData,
DeviceInterfaceData = deviceInterfaceData,
DeviceInterfaceIndex = deviceInterfaceIndex
};
}
}
NativeMethods.SetupDiDestroyDeviceInfoList(deviceInfoSet);
}
return devices;
}

private static NativeMethods.SP_DEVINFO_DATA CreateDeviceInfoData()
Expand Down
37 changes: 35 additions & 2 deletions Source/HidLibrary/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,12 @@ internal struct SECURITY_ATTRIBUTES
internal const short DIGCF_DEVICEINTERFACE = 0x10;
internal const int DIGCF_ALLCLASSES = 0x4;

internal const int MAX_DEV_LEN = 1000;
internal const int DIF_PROPERTYCHANGE = 0x12;
internal const int DICS_ENABLE = 1;
internal const int DICS_DISABLE = 2; // disable device
internal const int DICS_FLAG_GLOBAL = 1; // not profile-specific

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;
Expand Down Expand Up @@ -185,6 +190,22 @@ internal struct DEVPROPKEY
public ulong pid;
}

[StructLayout(LayoutKind.Sequential)]
internal struct SP_CLASSINSTALL_HEADER
{
public int cbSize;
public int InstallFunction;
}

[StructLayout(LayoutKind.Sequential)]
internal struct SP_PROPCHANGE_PARAMS
{
public SP_CLASSINSTALL_HEADER ClassInstallHeader;
public int StateChange;
public int Scope;
public int HwProfile;
}

internal static DEVPROPKEY DEVPKEY_Device_BusReportedDeviceDesc =
new DEVPROPKEY { fmtid = new Guid(0x540b947e, 0x8b40, 0x45bc, 0xa8, 0xa2, 0x6a, 0x0b, 0x89, 0x4c, 0xbd, 0xa2), pid = 4 };

Expand Down Expand Up @@ -218,7 +239,19 @@ internal struct DEVPROPKEY
[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("user32.dll")]
[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetupDiSetClassInstallParams(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData,
[In] ref SP_PROPCHANGE_PARAMS classInstallParams,
UInt32 ClassInstallParamsSize);

[DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetupDiChangeState(
IntPtr deviceInfoSet,
[In] ref SP_DEVINFO_DATA deviceInfoData);

[DllImport("user32.dll")]
static internal extern bool UnregisterDeviceNotification(IntPtr handle);

internal const short HIDP_INPUT = 0;
Expand Down

0 comments on commit abd85ff

Please sign in to comment.