it-swarm-vi.tech

Làm việc với các thiết bị USB trong .NET

Sử dụng .Net (C #), làm thế nào bạn có thể làm việc với các thiết bị USB? 

Làm cách nào bạn có thể phát hiện các sự kiện USB (kết nối/ngắt kết nối) và cách bạn giao tiếp với các thiết bị (đọc/ghi).

Có một giải pháp .Net bản địa để làm điều này?

42
David Thibault

Không có giải pháp gốc (ví dụ: Thư viện hệ thống) cho việc này. Đó là lý do tại sao SharpUSBLib tồn tại như được đề cập bởi moobaa .

Nếu bạn muốn cuộn trình xử lý của riêng mình cho các thiết bị USB, bạn có thể kiểm tra lớp SerialPort của System.IO.Ports .

21
Jon Limjap

Tôi đã thử sử dụng SharpUSBLib và nó đã làm hỏng máy tính của tôi (cần khôi phục hệ thống). Đã xảy ra với một đồng nghiệp trong cùng một dự án.

Tôi đã tìm thấy một thay thế trong LibUSBDotNet: http://sourceforge.net/projects/libusbdotnet

EDIT: Tính đến giữa tháng 2 năm 2017, LibUSBDotNet đã được cập nhật khoảng 2 tuần trước. Trong khi đó, SharpUSBLib chưa được cập nhật từ năm 2004.

24
Sofox

Tôi đã sử dụng mã sau đây để phát hiện khi thiết bị USB được cắm và rút khỏi máy tính của mình:

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();
        }


    }

Bạn phải chắc chắn rằng bạn gọi phương thức Dispose () khi đóng ứng dụng của bạn. Nếu không, bạn sẽ nhận được một lỗi đối tượng COM khi chạy khi đóng.

12
Syn
11
moobaa

Tôi muốn giới thiệu LibUSBDotNet , thư viện tôi đã sử dụng được 2 năm . Nếu bạn phải làm việc với thiết bị USB (gửi yêu cầu, xử lý phản hồi), thư viện này là giải pháp tốt nhất tôi có thể tìm thấy.

Ưu điểm:

  • Có tất cả các phương thức bạn cần để làm việc ở chế độ đồng bộ hoặc asynch.
  • Mã nguồn được cung cấp
  • Đủ mẫu để bắt đầu sử dụng ngay lập tức.

Nhược điểm:

  • Tài liệu kém (đó là vấn đề phổ biến đối với các dự án nguồn mở). Về cơ bản, bạn chỉ có thể tìm thấy mô tả chung về các phương thức trong tệp trợ giúp CHM và đó là nó. Nhưng tôi vẫn thấy các mẫu được cung cấp và mã nguồn là đủ để mã hóa . Chỉ đôi khi tôi thấy một hành vi lạ và muốn biết tại sao nó được thực hiện theo cách này và thậm chí không thể nhận được gợi ý ...
  • Có vẻ không được hỗ trợ nữa. Phiên bản cuối cùng được phát hành vào tháng 10 năm 2010. Và đôi khi thật khó để có câu trả lời.
4
Alex Klaus

Có một hướng dẫn về cách để thư viện SharpUSBLib và trình điều khiển HID hoạt động với C # tại đây: 

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

2
James Crowley

Nếu bạn có phần mềm National Cụ trên PC, bạn có thể tạo Trình điều khiển USB bằng cách sử dụng "Trình hướng dẫn Trình điều khiển NI-VISA"

Các bước để tạo Trình điều khiển USB: http://www.ni.com/tutorial/4478/vi/

Khi bạn đã tạo trình điều khiển, bạn sẽ có thể Viết và Đọc byte cho bất kỳ Thiết bị USB nào.

Đảm bảo trình điều khiển được nhìn thấy bởi các cửa sổ trong Trình quản lý thiết bị:

enter image description here

Mã 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

Sử dụng:

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();

Hy vọng điều này sẽ giúp được ai đó.

1
Pabinator

Các thiết bị USB thường thuộc hai loại: Hid và USB. Thiết bị USB có thể hoặc không thể là thiết bị Hid và ngược lại. Hid thường dễ làm việc hơn một chút so với USB trực tiếp. Các nền tảng khác nhau có các API khác nhau để xử lý cả USB và Hid. 

Đây là tài liệu cho UWP:

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

Hid: https://docs.Microsoft.com/en-us/uwp/api/windows.devices.humaninterfacesevice

Dưới đây là tài liệu dành cho Android: https://developer.xamarin.com/api/namespace/Android.Hardware.Usb/

Đây là hai lớp để xử lý USB/Hid ở cấp API Windows thô:

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
}

Với bất kỳ giải pháp nào trong số này, bạn sẽ cần thăm dò ý kiến ​​cho thiết bị trong một khoảng thời gian hoặc sử dụng một trong các lớp nghe thiết bị gốc của API. Tuy nhiên, thư viện này đặt một lớp trên Hid và USB trên tất cả các nền tảng để bạn có thể dễ dàng phát hiện các kết nối và ngắt kết nối: https://github.com/MelbourneDeveloper/Device.Net/wiki/Device-Listener . Đây là cách bạn sẽ sử dụng nó:

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
}
1
Melbourne Developer

Có một bộ công cụ chung WinDriver để viết Trình điều khiển USB ở chế độ người dùng hỗ trợ # .NET 

1
Ilya

Tôi đã thử một vài trong số những gợi ý này mà không có may mắn. Cuối cùng tôi đã viết một giải pháp làm việc bằng cách sử dụng Java và thư viện hid4Java . Là một ứng dụng bảng điều khiển, tôi có thể trình bày nó từ C # bằng cách sử dụng Process.Start(), truyền tham số cũng như đọc phản hồi. Điều này cung cấp I/O HID cơ bản nhưng không có sự kiện kết nối/ngắt kết nối. Vì vậy, tôi cần phải viết lại nó để chạy như một daemon/dịch vụ và sử dụng các đường ống có tên hoặc một số vận chuyển máy chủ/máy khách khác. Hiện tại, đã đủ để hoàn thành công việc vì thư viện hi4Java "chỉ hoạt động".

0
Mike Lowery

Tôi đã nhận được một giao diện cho Teensy hoạt động khá tốt, sử dụng bài viết này

0
Brad Bruce

Hầu hết các chipset USB đi kèm với trình điều khiển. Phòng thí nghiệm Silicon có một.

0
Nick