it-swarm-eu.dev

Arbeiten mit USB-Geräten in .NET

Wie können Sie mit .Net (C #) mit USB-Geräten arbeiten? 

Wie können Sie USB-Ereignisse (Verbindungen/Verbindungen) erkennen und wie Sie mit Geräten kommunizieren (lesen/schreiben).

Gibt es eine native .NET-Lösung, um dies zu tun?

42
David Thibault

Dafür gibt es keine native - Lösung (z. B. Systembibliotheken). Aus diesem Grund existiert SharpUSBLib , wie von moobaa erwähnt.

Wenn Sie einen eigenen Handler für USB-Geräte ausführen möchten, können Sie die Klasse SerialPort von System.IO.Ports überprüfen.

21
Jon Limjap

Ich habe versucht, SharpUSBLib zu verwenden, und es hat meinen Computer vermasselt (Systemwiederherstellung erforderlich). Ist auch mit einem Mitarbeiter des gleichen Projekts passiert.

Ich habe in LibUSBDotNet eine Alternative gefunden: http://sourceforge.net/projects/libusbdotnet Habe es noch nicht viel benutzt, scheint aber gut zu sein und wurde (im Gegensatz zu Sharp) kürzlich aktualisiert.

EDIT: Seit Mitte Februar 2017 wurde LibUSBDotNet vor etwa 2 Wochen aktualisiert. Inzwischen wurde SharpUSBLib seit 2004 nicht aktualisiert.

24
Sofox

Ich habe den folgenden Code verwendet, um festzustellen, wann USB-Geräte an meinen Computer angeschlossen und von diesem getrennt wurden:

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


    }

Sie müssen sicherstellen, dass Sie die Dispose () -Methode aufrufen, wenn Sie Ihre Anwendung schließen. Andernfalls erhalten Sie beim Schließen einen COM-Objektfehler zur Laufzeit.

12
Syn
11
moobaa

Ich empfehle LibUSBDotNet , die Bibliothek, die ich seit 2 Jahren verwende ... Wenn Sie mit einem USB-Gerät arbeiten müssen (Anfragen senden, Antworten verarbeiten), war diese Bibliothek die beste Lösung, die ich finden konnte.

Pros:

  • Verfügt über alle Methoden, um synchron oder asynchron zu arbeiten.
  • Quellcode bereitgestellt
  • Genug Proben, um sofort loslegen zu können.

Nachteile:

  • Mangelhafte Dokumentation (häufig bei Open-Source-Projekten). Grundsätzlich können Sie in der CHM-Hilfedatei nur eine allgemeine Beschreibung von Methoden finden. Aber ich finde immer noch bereitgestellte Beispiele und Quellcode reicht für das Codieren aus. Nur manchmal sehe ich ein seltsames Verhalten und möchte wissen, warum es auf diese Weise implementiert wurde, und nicht einmal einen Hinweis bekommen kann.
  • Scheint nicht mehr unterstützt. Die letzte Version wurde im Oktober 2010 herausgegeben. Manchmal ist es schwierig, Antworten zu erhalten.
4
Alex Klaus

In diesem Tutorial erfahren Sie, wie Sie die SharpUSBLib-Bibliothek und die HID-Treiber mit C # verwenden können: 

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

2
James Crowley

Wenn Sie über die Software National Instruments auf Ihrem PC verfügen, können Sie einen USB-Treiber mit dem "NI-VISA Driver Wizard" erstellen. 

Schritte zum Erstellen des USB-Treibers: http://www.ni.com/tutorial/4478/de/

Nachdem Sie den Treiber erstellt haben, können Sie Bytes auf ein beliebiges USB-Gerät schreiben und lesen.

Stellen Sie sicher, dass der Treiber unter Windows im Geräte-Manager angezeigt wird:

enter image description here

C # -Code:

    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

Verwendungszweck:

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

Hoffe das hilft jemandem.

1
Pabinator

USB-Geräte lassen sich normalerweise in zwei Kategorien einteilen: Verborgen und USB. Ein USB-Gerät kann ein verstecktes Gerät sein oder nicht und umgekehrt. Verborgen ist normalerweise etwas einfacher als direkter USB. Verschiedene Plattformen verfügen über unterschiedliche APIs für den Umgang mit USB und Hid. 

Hier ist die Dokumentation für UWP:

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

Versteckt: https://docs.Microsoft.com/en-us/uwp/api/windows.devices.humaninterfacedevice

Hier ist die Dokumentation für Android: https://developer.xamarin.com/api/namespace/Android.Hardware.Usb/

Hier sind zwei Klassen für den Umgang mit USB/Hid auf der Basisebene von Windows API:

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
}

Bei einer dieser Lösungen müssen Sie entweder das Gerät in einem bestimmten Intervall abfragen oder eine der systemeigenen Geräte-Überwachungsklassen der API verwenden. Durch diese Bibliothek wird jedoch auf allen Plattformen eine Schicht über Hid und USB eingefügt, sodass Sie Verbindungen und Verbindungsabbrüche problemlos erkennen können: https://github.com/MelbourneDeveloper/Device.Net/wiki/Device-Listener . So würden Sie es verwenden:

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

Es gibt ein generisches Toolkit WinDriver zum Schreiben von USB-Treibern im Benutzermodus, das auch # .NET unterstützt 

1
Ilya

Ich habe mehrere dieser Vorschläge ohne Erfolg ausprobiert. Am Ende habe ich eine funktionierende Lösung geschrieben, die Java und die Bibliothek hid4Java verwendet. Als Konsolen-App kann ich aus C # heraus mit Process.Start() darauf zugreifen, Parameter übergeben und Antworten lesen. Dies bietet grundlegende HID-E/A, jedoch keine Verbindungs-/Trennungsereignisse. Dafür müsste ich es neu schreiben, um als Daemon/Dienst ausgeführt zu werden und Named Pipes oder einen anderen Server/Client-Transport zu verwenden. Für den Moment reicht es aus, die Arbeit zu erledigen, da die hi4Java-Bibliothek "einfach funktioniert".

0
Mike Lowery

Ich habe eine Schnittstelle zu einem Teensy bekommen, die ziemlich gut funktioniert, diesen Artikel

0
Brad Bruce

Die meisten USB-Chipsätze werden mit Treibern geliefert. Silicon Labs hat eine.

0
Nick