/src/Experimental/IO.Usb.Hid/Native.SetupApiAdapter.fs

https://github.com/jwelch222/HidLibrary · F# · 128 lines · 110 code · 15 blank · 3 comment · 13 complexity · 15a01594690cf1f42ed1a772cd2b192a MD5 · raw file

  1. module IO.Usb.Hid.Native.SetupApiAdapter
  2. open System
  3. open System.Text
  4. open IO.Usb.Hid.Native
  5. open IO.Usb.Hid.Native.SetupApi
  6. open System.Runtime.InteropServices
  7. open System.ComponentModel
  8. open System.Collections
  9. open System.Collections.Generic
  10. // ---------------------------- Private Functions ----------------------------
  11. let private GetDeviceInterface handle index deviceInfo deviceClass =
  12. let mutable deviceInterface = new SetupApi.SP_DEVICE_INTERFACE_DATA()
  13. let mutable classFilter = deviceClass
  14. let mutable info = deviceInfo
  15. deviceInterface.Init ()
  16. let success = SetupApi.SetupDiEnumDeviceInterfaces(handle, &info, &classFilter, index, &deviceInterface)
  17. if not success then
  18. let error = Marshal.GetLastWin32Error()
  19. match error with
  20. | SetupApi.ERROR_NO_MORE_ITEMS -> (deviceInterface,false)
  21. | SetupApi.SUCCESS -> (deviceInterface,false)
  22. | _ -> raise (Win32Exception(Marshal.GetLastWin32Error()))
  23. else (deviceInterface,true)
  24. type private DeviceInterfaces(infoSetHandle, deviceInfo, deviceClass) =
  25. member private c.ToSeq =
  26. Seq.unfold (fun index ->
  27. let info, more = GetDeviceInterface infoSetHandle index deviceInfo deviceClass
  28. match more with
  29. | true -> Some (info, index + 1)
  30. | _ -> None
  31. ) 0
  32. interface IEnumerable with
  33. member c.GetEnumerator () = c.ToSeq.GetEnumerator() :> IEnumerator
  34. interface IEnumerable<SP_DEVICE_INTERFACE_DATA> with
  35. member c.GetEnumerator () = c.ToSeq.GetEnumerator()
  36. let private GetDeviceInfoSet deviceClass connectedOnly =
  37. let mutable classFilter = deviceClass
  38. let flags = match connectedOnly with
  39. | true -> DeviceInterfaceGetClassFlags.DEVICEINTERFACE |||
  40. DeviceInterfaceGetClassFlags.PRESENT
  41. | false -> DeviceInterfaceGetClassFlags.DEVICEINTERFACE
  42. let handle = SetupApi.SetupDiGetClassDevs(&classFilter, null, IntPtr.Zero, flags)
  43. let error = Marshal.GetLastWin32Error()
  44. match error with
  45. | 0 -> handle
  46. | _ -> raise (Win32Exception(Marshal.GetLastWin32Error()))
  47. let private GetDeviceInfo handle index =
  48. let mutable deviceInfo = new SetupApi.SP_DEVINFO_DATA()
  49. deviceInfo.Init ()
  50. let success = SetupApi.SetupDiEnumDeviceInfo(handle, index, &deviceInfo)
  51. if not success then
  52. let error = Marshal.GetLastWin32Error()
  53. match error with
  54. | SetupApi.ERROR_NO_MORE_ITEMS -> (deviceInfo,false)
  55. | _ -> raise (Win32Exception(Marshal.GetLastWin32Error()))
  56. else (deviceInfo,true)
  57. let private GetDevicePath handle deviceInterface =
  58. let mutable deviceInterfaceInfo = deviceInterface
  59. let mutable deviceInterfaceDetail = new SetupApi.SP_DEVICE_INTERFACE_DETAIL_DATA()
  60. let mutable requiredSize = 0
  61. deviceInterfaceDetail.Init()
  62. SetupApi.SetupDiGetDeviceInterfaceDetailBufferCheck(handle, &deviceInterfaceInfo, IntPtr.Zero, 0, &requiredSize, IntPtr.Zero) |> ignore
  63. let success = SetupApi.SetupDiGetDeviceInterfaceDetail(handle, &deviceInterfaceInfo, &deviceInterfaceDetail, requiredSize, &requiredSize, IntPtr.Zero)
  64. if not success then
  65. let error = Marshal.GetLastWin32Error()
  66. raise (Win32Exception(Marshal.GetLastWin32Error()))
  67. else deviceInterfaceDetail.DevicePath
  68. let private GetDeviceProperty handle deviceInfo property =
  69. let mutable info = deviceInfo
  70. let mutable valueType = 0
  71. let mutable description = new StringBuilder(256)
  72. let mutable size = 0
  73. let success = SetupApi.SetupDiGetDeviceRegistryProperty(handle, &info, property, &valueType, description, 255, &size)
  74. match success with
  75. | true -> description.ToString()
  76. | false -> String.Empty
  77. // ---------------------------- Nested Types ----------------------------
  78. type DeviceInfo (path:string, description:string, manufacturer:string, connected:bool) =
  79. member p.Path = path
  80. member p.Description = description
  81. member p.Manufacturer = manufacturer
  82. member p.Connected = connected
  83. type private Devices(deviceClass:Guid, connectedOnly:bool) =
  84. let handle = GetDeviceInfoSet deviceClass connectedOnly
  85. member private c.ToSeq =
  86. Seq.unfold (fun index ->
  87. let info, more = GetDeviceInfo handle index
  88. match more with
  89. | true -> let interfaces = new DeviceInterfaces(handle, info, deviceClass)
  90. let defaultInterface = Seq.head interfaces
  91. let path = GetDevicePath handle defaultInterface
  92. Some((handle, defaultInterface, info, path), index + 1)
  93. | _ -> None
  94. ) 0
  95. interface IEnumerable with
  96. member c.GetEnumerator () = c.ToSeq.GetEnumerator() :> IEnumerator
  97. interface IEnumerable<SetupApi.DeviceInfoSetSafeHandle * SP_DEVICE_INTERFACE_DATA * SetupApi.SP_DEVINFO_DATA * string> with
  98. member c.GetEnumerator () = c.ToSeq.GetEnumerator()
  99. interface IDisposable with
  100. member c.Dispose () = handle.Dispose()
  101. // ---------------------------- Public Functions ----------------------------
  102. let GetDevicePaths deviceClass connectedOnly =
  103. use devices = new Devices(deviceClass, connectedOnly)
  104. devices |> Seq.map (fun (handle, inf, info, path) -> path) |> Seq.toList
  105. let GetDevices deviceClass connectedOnly =
  106. use devices = new Devices(deviceClass, connectedOnly)
  107. devices
  108. |> Seq.map (fun (handle, inf, info, path) ->
  109. let description = GetDeviceProperty handle info SetupApi.SetupDeviceRegistryProperty.DEVICEDESC
  110. let manufacturer = GetDeviceProperty handle info SetupApi.SetupDeviceRegistryProperty.MFG
  111. let connected = (inf.Flags &&& SetupApi.SPINT_ACTIVE) = SetupApi.SPINT_ACTIVE
  112. new DeviceInfo(path, description, manufacturer, connected))
  113. |> Seq.toList