【问题标题】:Working with USB devices in .NET在 .NET 中使用 USB 设备
【发布时间】:2010-09-09 06:50:28
【问题描述】:

使用 .Net (C#),如何使用 USB 设备?

如何检测 USB 事件(连接/断开)以及如何与设备通信(读/写)。

是否有本地 .Net 解决方案来执行此操作?

【问题讨论】:

标签: .net usb communication


【解决方案1】:

我尝试使用 SharpUSBLib,但它搞砸了我的电脑(需要系统还原)。也发生在同一个项目的同事身上。

我在 LibUSBDotNet 中找到了替代方案:http://sourceforge.net/projects/libusbdotnet 还没有使用太多,但看起来不错并且最近更新了(与 Sharp 不同)。

编辑:截至 2017 年 2 月中旬,LibUSBDotNet 大约在 2 周前更新。同时 SharpUSBLib 自 2004 年以来一直没有更新。

【讨论】:

    【解决方案2】:

    没有本地(例如,系统库)解决方案。这就是moobaa提到的SharpUSBLib存在的原因。

    如果您想为 USB 设备推出自己的处理程序,您可以查看SerialPort class of System.IO.Ports

    【讨论】:

    • 如何通过 SerialPort 类使用 USB 端口?
    • 嗯,早在 08 年,USB 1.0 就没有串行端口技术了。 :D 不知道如何用 USB 2.0 和 USB 3.0 做到这一点
    • 我只是用 CreateFile winapi 访问usb :)
    【解决方案3】:

    我使用以下代码检测 USB 设备何时从我的计算机上插入和拔出:

    class USBControl : IDisposable
        {
            // used for monitoring plugging and unplugging of USB devices.
            private ManagementEventWatcher watcherAttach;
            private ManagementEventWatcher watcherRemove;
    
            public USBControl()
            {
                // Add USB plugged event watching
                watcherAttach = new ManagementEventWatcher();
                //var queryAttach = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
                watcherAttach.EventArrived += new EventArrivedEventHandler(watcher_EventArrived);
                watcherAttach.Query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
                watcherAttach.Start();
    
                // Add USB unplugged event watching
                watcherRemove = new ManagementEventWatcher();
                //var queryRemove = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
                watcherRemove.EventArrived += new EventArrivedEventHandler(watcher_EventRemoved);
                watcherRemove.Query = new WqlEventQuery("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
                watcherRemove.Start();
            }
    
            /// <summary>
            /// Used to dispose of the USB device watchers when the USBControl class is disposed of.
            /// </summary>
            public void Dispose()
            {
                watcherAttach.Stop();
                watcherRemove.Stop();
                //Thread.Sleep(1000);
                watcherAttach.Dispose();
                watcherRemove.Dispose();
                //Thread.Sleep(1000);
            }
    
            void watcher_EventArrived(object sender, EventArrivedEventArgs e)
            {
                Debug.WriteLine("watcher_EventArrived");
            }
    
            void watcher_EventRemoved(object sender, EventArrivedEventArgs e)
            {
                Debug.WriteLine("watcher_EventRemoved");
            }
    
            ~USBControl()
            {
                this.Dispose();
            }
    
    
        }
    

    您必须确保在关闭应用程序时调用 Dispose() 方法。否则,关闭时会在运行时收到 COM 对象错误。

    【讨论】:

      【解决方案4】:

      我推荐LibUSBDotNet,我已经使用了 2 年的库。 如果您必须使用 USB 设备(发送请求、处理响应),这个库是我能找到的最佳解决方案。

      优点:

      • 拥有在同步或异步模式下工作所需的所有方法。
      • 提供源代码
      • 足够的样本可以立即开始使用。

      缺点:

      • 文档不足(这是开源项目的常见问题)。基本上,您可以在 CHM 帮助文件中找到常见的方法描述,仅此而已。 但我仍然发现提供的示例和源代码足以进行编码。 只是有时我看到一个奇怪的行为,想知道为什么它是这样实现的,甚至无法得到任何提示......
      • 似乎不再受支持。上一个版本是 2010 年 10 月发布的。有时很难得到答案。

      【讨论】:

      【解决方案5】:

      USB 设备通常分为两类:Hid 和 USB。 USB 设备可能是也可能不是 Hid 设备,反之亦然。 Hid 通常比直接 USB 更容易使用。不同的平台有不同的 API 来处理 USB 和 Hid。

      这是 UWP 的文档:

      USB: https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/how-to-connect-to-a-usb-device--uwp-app-

      隐藏: https://docs.microsoft.com/en-us/uwp/api/windows.devices.humaninterfacedevice

      这里是 Android 的文档: https://developer.xamarin.com/api/namespace/Android.Hardware.Usb/

      以下是在原始 Windows API 级别处理 USB/Hid 的两个类:

      https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Hid.Net/Windows/HidAPICalls.cs

      public static class HidAPICalls 
      {
          #region Constants
          private const int DigcfDeviceinterface = 16;
          private const int DigcfPresent = 2;
          private const uint FileShareRead = 1;
          private const uint FileShareWrite = 2;
          private const uint GenericRead = 2147483648;
          private const uint GenericWrite = 1073741824;
          private const uint OpenExisting = 3;
          private const int HIDP_STATUS_SUCCESS = 0x110000;
          private const int HIDP_STATUS_INVALID_PREPARSED_DATA = -0x3FEF0000;
          #endregion
      
          #region API Calls
      
          [DllImport("hid.dll", SetLastError = true)]
          private static extern bool HidD_GetPreparsedData(SafeFileHandle hidDeviceObject, out IntPtr pointerToPreparsedData);
      
          [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
          private static extern bool HidD_GetManufacturerString(SafeFileHandle hidDeviceObject, IntPtr pointerToBuffer, uint bufferLength);
      
          [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
          private static extern bool HidD_GetProductString(SafeFileHandle hidDeviceObject, IntPtr pointerToBuffer, uint bufferLength);
      
          [DllImport("hid.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
          private static extern bool HidD_GetSerialNumberString(SafeFileHandle hidDeviceObject, IntPtr pointerToBuffer, uint bufferLength);
      
          [DllImport("hid.dll", SetLastError = true)]
          private static extern int HidP_GetCaps(IntPtr pointerToPreparsedData, out HidCollectionCapabilities hidCollectionCapabilities);
      
          [DllImport("hid.dll", SetLastError = true)]
          private static extern bool HidD_GetAttributes(SafeFileHandle hidDeviceObject, out HidAttributes attributes);
      
          [DllImport("hid.dll", SetLastError = true)]
          private static extern bool HidD_FreePreparsedData(ref IntPtr pointerToPreparsedData);
      
          [DllImport("hid.dll", SetLastError = true)]
          private static extern void HidD_GetHidGuid(ref Guid hidGuid);
      
          private delegate bool GetString(SafeFileHandle hidDeviceObject, IntPtr pointerToBuffer, uint bufferLength);
      
          #endregion
      
          #region Helper Methods
      
          #region Public Methods
          public static HidAttributes GetHidAttributes(SafeFileHandle safeFileHandle)
          {
              var isSuccess = HidD_GetAttributes(safeFileHandle, out var hidAttributes);
              WindowsDeviceBase.HandleError(isSuccess, "Could not get Hid Attributes");
              return hidAttributes;
          }
      
          public static HidCollectionCapabilities GetHidCapabilities(SafeFileHandle readSafeFileHandle)
          {
              var isSuccess = HidD_GetPreparsedData(readSafeFileHandle, out var pointerToPreParsedData);
              WindowsDeviceBase.HandleError(isSuccess, "Could not get pre parsed data");
      
              var result = HidP_GetCaps(pointerToPreParsedData, out var hidCollectionCapabilities);
              if (result != HIDP_STATUS_SUCCESS)
              {
                  throw new Exception($"Could not get Hid capabilities. Return code: {result}");
              }
      
              isSuccess = HidD_FreePreparsedData(ref pointerToPreParsedData);
              WindowsDeviceBase.HandleError(isSuccess, "Could not release handle for getting Hid capabilities");
      
              return hidCollectionCapabilities;
          }
      
          public static string GetManufacturer(SafeFileHandle safeFileHandle)
          {
              return GetHidString(safeFileHandle, HidD_GetManufacturerString);
          }
      
          public static string GetProduct(SafeFileHandle safeFileHandle)
          {
              return GetHidString(safeFileHandle, HidD_GetProductString);
          }
      
          public static string GetSerialNumber(SafeFileHandle safeFileHandle)
          {
              return GetHidString(safeFileHandle, HidD_GetSerialNumberString);
          }
          #endregion
      
          #region Private Static Methods
          private static string GetHidString(SafeFileHandle safeFileHandle, GetString getString)
          {
              var pointerToBuffer = Marshal.AllocHGlobal(126);
              var isSuccess = getString(safeFileHandle, pointerToBuffer, 126);
              Marshal.FreeHGlobal(pointerToBuffer);
              WindowsDeviceBase.HandleError(isSuccess, "Could not get Hid string");
              return Marshal.PtrToStringUni(pointerToBuffer);     
          }
          #endregion
      
          #endregion
      

      }

      https://github.com/MelbourneDeveloper/Device.Net/blob/master/src/Usb.Net/Windows/WinUsbApiCalls.cs

      public static partial class WinUsbApiCalls
      {
          #region Constants
          public const int EnglishLanguageID = 1033;
          public const uint DEVICE_SPEED = 1;
          public const byte USB_ENDPOINT_DIRECTION_MASK = 0X80;
          public const int WritePipeId = 0x80;
      
          /// <summary>
          /// Not sure where this constant is defined...
          /// </summary>
          public const int DEFAULT_DESCRIPTOR_TYPE = 0x01;
          public const int USB_STRING_DESCRIPTOR_TYPE = 0x03;
          #endregion
      
          #region API Calls
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_ControlTransfer(IntPtr InterfaceHandle, WINUSB_SETUP_PACKET SetupPacket, byte[] Buffer, uint BufferLength, ref uint LengthTransferred, IntPtr Overlapped);
      
          [DllImport("winusb.dll", SetLastError = true, CharSet = CharSet.Auto)]
          public static extern bool WinUsb_GetAssociatedInterface(SafeFileHandle InterfaceHandle, byte AssociatedInterfaceIndex, out SafeFileHandle AssociatedInterfaceHandle);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, ushort LanguageID, out USB_DEVICE_DESCRIPTOR deviceDesc, uint BufferLength, out uint LengthTransfered);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_GetDescriptor(SafeFileHandle InterfaceHandle, byte DescriptorType, byte Index, UInt16 LanguageID, byte[] Buffer, UInt32 BufferLength, out UInt32 LengthTransfered);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_Free(SafeFileHandle InterfaceHandle);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_Initialize(SafeFileHandle DeviceHandle, out SafeFileHandle InterfaceHandle);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_QueryDeviceInformation(IntPtr InterfaceHandle, uint InformationType, ref uint BufferLength, ref byte Buffer);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_QueryInterfaceSettings(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, out USB_INTERFACE_DESCRIPTOR UsbAltInterfaceDescriptor);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_QueryPipe(SafeFileHandle InterfaceHandle, byte AlternateInterfaceNumber, byte PipeIndex, out WINUSB_PIPE_INFORMATION PipeInformation);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_ReadPipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_SetPipePolicy(IntPtr InterfaceHandle, byte PipeID, uint PolicyType, uint ValueLength, ref uint Value);
      
          [DllImport("winusb.dll", SetLastError = true)]
          public static extern bool WinUsb_WritePipe(SafeFileHandle InterfaceHandle, byte PipeID, byte[] Buffer, uint BufferLength, out uint LengthTransferred, IntPtr Overlapped);
          #endregion
      
          #region Public Methods
          public static string GetDescriptor(SafeFileHandle defaultInterfaceHandle, byte index, string errorMessage)
          {
              var buffer = new byte[256];
              var isSuccess = WinUsb_GetDescriptor(defaultInterfaceHandle, USB_STRING_DESCRIPTOR_TYPE, index, EnglishLanguageID, buffer, (uint)buffer.Length, out var transfered);
              WindowsDeviceBase.HandleError(isSuccess, errorMessage);
              var descriptor = new string(Encoding.Unicode.GetChars(buffer, 2, (int)transfered));
              return descriptor.Substring(0, descriptor.Length - 1);
          }
          #endregion
      }
      

      使用这些解决方案中的任何一种,您要么需要按时间间隔轮询设备,要么使用 API 的本机设备侦听类之一。但是,这个库在所有平台上跨 Hid 和 USB 放置了一个层,以便您可以轻松检测连接和断开连接:https://github.com/MelbourneDeveloper/Device.Net/wiki/Device-Listener。这就是你将如何使用它:

      internal class TrezorExample : IDisposable
      {
          #region Fields
          //Define the types of devices to search for. This particular device can be connected to via USB, or Hid
          private readonly List<FilterDeviceDefinition> _DeviceDefinitions = new List<FilterDeviceDefinition>
          {
              new FilterDeviceDefinition{ DeviceType= DeviceType.Hid, VendorId= 0x534C, ProductId=0x0001, Label="Trezor One Firmware 1.6.x", UsagePage=65280 },
              new FilterDeviceDefinition{ DeviceType= DeviceType.Usb, VendorId= 0x534C, ProductId=0x0001, Label="Trezor One Firmware 1.6.x (Android Only)" },
              new FilterDeviceDefinition{ DeviceType= DeviceType.Usb, VendorId= 0x1209, ProductId=0x53C1, Label="Trezor One Firmware 1.7.x" },
              new FilterDeviceDefinition{ DeviceType= DeviceType.Usb, VendorId= 0x1209, ProductId=0x53C0, Label="Model T" }
          };
          #endregion
      
          #region Events
          public event EventHandler TrezorInitialized;
          public event EventHandler TrezorDisconnected;
          #endregion
      
          #region Public Properties
          public IDevice TrezorDevice { get; private set; }
          public DeviceListener DeviceListener { get; private set; }
          #endregion
      
          #region Event Handlers
          private void DevicePoller_DeviceInitialized(object sender, DeviceEventArgs e)
          {
              TrezorDevice = e.Device;
              TrezorInitialized?.Invoke(this, new EventArgs());
          }
      
          private void DevicePoller_DeviceDisconnected(object sender, DeviceEventArgs e)
          {
              TrezorDevice = null;
              TrezorDisconnected?.Invoke(this, new EventArgs());
          }
          #endregion
      
          #region Public Methods
          public void StartListening()
          {
              TrezorDevice?.Dispose();
              DeviceListener = new DeviceListener(_DeviceDefinitions, 3000);
              DeviceListener.DeviceDisconnected += DevicePoller_DeviceDisconnected;
              DeviceListener.DeviceInitialized += DevicePoller_DeviceInitialized;
          }
      
          public async Task InitializeTrezorAsync()
          {
              //Get the first available device and connect to it
              var devices = await DeviceManager.Current.GetDevices(_DeviceDefinitions);
              TrezorDevice = devices.FirstOrDefault();
              await TrezorDevice.InitializeAsync();
          }
      
          public async Task<byte[]> WriteAndReadFromDeviceAsync()
          {
              //Create a buffer with 3 bytes (initialize)
              var writeBuffer = new byte[64];
              writeBuffer[0] = 0x3f;
              writeBuffer[1] = 0x23;
              writeBuffer[2] = 0x23;
      
              //Write the data to the device
              return await TrezorDevice.WriteAndReadAsync(writeBuffer);
          }
      
          public void Dispose()
          {
              TrezorDevice?.Dispose();
          }
          #endregion
      }
      

      【讨论】:

        【解决方案6】:

        这里有一个使用 C# 获取 SharpUSBLib 库和 HID 驱动程序的教程:

        http://www.developerfusion.com/article/84338/making-usb-c-friendly/

        【讨论】:

          【解决方案7】:

          有一个通用工具包WinDriver 用于在支持#.NET 的用户模式下编写USB 驱动程序

          【讨论】:

            【解决方案8】:

            如果您的 PC 上有 National Instruments 软件,您可以使用他们的“NI-VISA 驱动程序向导”创建 USB 驱动程序。

            创建 USB 驱动程序的步骤:http://www.ni.com/tutorial/4478/en/

            创建驱动程序后,您将能够向任何 USB 设备写入和读取字节。

            确保设备管理器下的 Windows 可以看到驱动程序:

            C#代码:

                using NationalInstruments.VisaNS;
            
                #region UsbRaw
                /// <summary>
                /// Class to communicate with USB Devices using the UsbRaw Class of National Instruments
                /// </summary>
                public class UsbRaw
                {
                    private NationalInstruments.VisaNS.UsbRaw usbRaw;
                    private List<byte> DataReceived = new List<byte>();
            
                    /// <summary>
                    /// Initialize the USB Device to interact with
                    /// </summary>
                    /// <param name="ResourseName">In this format: "USB0::0x1448::0x8CA0::NI-VISA-30004::RAW".  Use the NI-VISA Driver Wizard from Start»All Programs»National Instruments»VISA»Driver Wizard to create the USB Driver for the device you need to talk to.</param>
                    public UsbRaw(string ResourseName)
                    {
                        usbRaw = new NationalInstruments.VisaNS.UsbRaw(ResourseName, AccessModes.NoLock, 10000, false);
                        usbRaw.UsbInterrupt += new UsbRawInterruptEventHandler(OnUSBInterrupt);
                        usbRaw.EnableEvent(UsbRawEventType.UsbInterrupt, EventMechanism.Handler);
                    }
            
                    /// <summary>
                    /// Clears a USB Device from any previous commands
                    /// </summary>
                    public void Clear()
                    {
                        usbRaw.Clear();
                    }
            
                    /// <summary>
                    /// Writes Bytes to the USB Device
                    /// </summary>
                    /// <param name="EndPoint">USB Bulk Out Pipe attribute to send the data to.  For example: If you see on the Bus Hound sniffer tool that data is coming out from something like 28.4 (Device column), this means that the USB is using Endpoint 4 (Number after the dot)</param>
                    /// <param name="BytesToSend">Data to send to the USB device</param>
                    public void Write(short EndPoint, byte[] BytesToSend)
                    {
                        usbRaw.BulkOutPipe = EndPoint;
                        usbRaw.Write(BytesToSend);       // Write to USB
                    }
            
                    /// <summary>
                    /// Reads bytes from a USB Device
                    /// </summary>
                    /// <returns>Bytes Read</returns>
                    public byte[] Read()
                    {
                        usbRaw.ReadByteArray();     // This fires the UsbRawInterruptEventHandler                
            
                        byte[] rxBytes = DataReceived.ToArray();      // Collects the data received
            
                        return rxBytes;
                    }
            
                    /// <summary>
                    /// This is used to get the data received by the USB device
                    /// </summary>
                    /// <param name="sender"></param>
                    /// <param name="e"></param>
                    private void OnUSBInterrupt(object sender, UsbRawInterruptEventArgs e)
                    {
                        try
                        {
                            DataReceived.Clear();     // Clear previous data received
                            DataReceived.AddRange(e.DataBuffer);                    
                        }
                        catch (Exception exp)
                        {
                            string errorMsg = "Error: " + exp.Message;
                            DataReceived.AddRange(ASCIIEncoding.ASCII.GetBytes(errorMsg));
                        }
                    }
            
                    /// <summary>
                    /// Use this function to clean up the UsbRaw class
                    /// </summary>
                    public void Dispose()
                    {
                        usbRaw.DisableEvent(UsbRawEventType.UsbInterrupt, EventMechanism.Handler);
            
                        if (usbRaw != null)
                        {
                            usbRaw.Dispose();
                        }              
                    }
            
                }
                #endregion UsbRaw
            

            用法:

            UsbRaw usbRaw = new UsbRaw("USB0::0x1448::0x8CA0::NI-VISA-30004::RAW");
            
            byte[] sendData = new byte[] { 0x53, 0x4c, 0x56 };
            usbRaw.Write(4, sendData);      // Write bytes to the USB Device
            byte[] readData = usbRaw.Read();   // Read bytes from the USB Device
            
            usbRaw.Dispose();
            

            希望这对某人有所帮助。

            【讨论】:

              【解决方案9】:

              大多数 USB 芯片组都附带驱动程序。 Silicon Labs 有一个。

              【讨论】:

              • 这样我对驱动本身有依赖?是否有适用于所有机器的通用解决方案?
              • 您将依赖于 dll 和硬件依赖于所使用的芯片组。但是,如果您可以同时控制软件和硬件,这是启动和运行 USB 通信的最快方法。
              【解决方案10】:

              我得到了一个与Teensy 的接口,使用this article 工作得很好

              【讨论】:

                【解决方案11】:

                我尝试了其中的几个建议,但都没有成功。我最终使用 Java 和 hid4java 库编写了一个可行的解决方案。作为一个控制台应用程序,我可以使用 Process.Start() 从 C# 中对它进行处理,传递参数以及读取响应。这提供了基本的 HID I/O,但没有连接/断开事件。为此,我需要重写它以作为守护程序/服务运行并使用命名管道或其他一些服务器/客户端传输。现在,完成这项工作就足够了,因为 hi4java 库“正常工作”。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-05-31
                  • 2013-08-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-02-13
                  相关资源
                  最近更新 更多