سؤال

باستخدام .Net (C#)، كيف يمكنك العمل مع أجهزة USB؟

كيف يمكنك اكتشاف أحداث USB (الاتصالات/قطع الاتصال) وكيف تتواصل مع الأجهزة (القراءة/الكتابة).

هل هناك حل .Net أصلي للقيام بذلك؟

هل كانت مفيدة؟

المحلول

لا يوجد محلي (على سبيل المثال، مكتبات النظام) الحل لهذا.لهذا السبب SharpUSBLib موجود كما ذكر moobaa.

إذا كنت ترغب في إنشاء المعالج الخاص بك لأجهزة USB، فيمكنك الاطلاع على فئة SerialPort من System.IO.Ports.

نصائح أخرى

لقد حاولت استخدام SharpUSBLib وأفسد جهاز الكمبيوتر الخاص بي (بحاجة إلى استعادة النظام).حدث لزميل في نفس المشروع أيضًا.

لقد وجدت بديلاً في LibUSBDotNet: http://sourceforge.net/projects/libusbdotnetلم أستخدمه كثيرًا حتى الآن ولكنه يبدو جيدًا وتم تحديثه مؤخرًا (على عكس Sharp).

يحرر:اعتبارًا من منتصف فبراير 2017، تم تحديث LibUSBDotNet منذ حوالي أسبوعين.وفي الوقت نفسه، لم يتم تحديث SharpUSBLib منذ عام 2004.

لقد استخدمت الكود التالي لاكتشاف متى تم توصيل أجهزة 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 في وقت التشغيل عند الإغلاق.

أنا أوصي ليبUSBDotNet, المكتبة التي أستخدمها منذ عامين.إذا كان عليك العمل مع جهاز USB (إرسال الطلبات، معالجة الاستجابات)، كانت هذه المكتبة هي أفضل حل يمكن أن أجده.

الايجابيات:

  • يحتوي على جميع الطرق التي تحتاجها للعمل في وضع المزامنة أو غير المتزامن.
  • كود المصدر المقدمة
  • عينات كافية لبدء استخدامه على الفور.

سلبيات:

  • ضعف التوثيق (إنها مشكلة شائعة بالنسبة للمشاريع مفتوحة المصدر).في الأساس، يمكنك العثور على وصف شائع للطرق في ملف تعليمات آلية تبادل المعلومات (CHM) وهذا كل شيء.ولكن ما زلت أجد أن العينات المقدمة وكود المصدر كافية للتشفير.فقط في بعض الأحيان أرى سلوكًا غريبًا وأريد أن أعرف سبب تنفيذه بهذه الطريقة ولا أستطيع حتى الحصول على تلميح...
  • يبدو غير مدعوم بعد الآن.تم إصدار الإصدار الأخير في أكتوبر 2010.ومن الصعب الحصول على إجابات في بعض الأحيان.

يوجد برنامج تعليمي حول الحصول على مكتبة SharpUSBLib وبرامج تشغيل HID التي تعمل مع C# هنا:

http://www.developerfusion.com/article/84338/make-usb-c-Friendly/

هناك مجموعة أدوات عامة WinDriver لكتابة برامج تشغيل USB في وضع المستخدم الذي يدعم #.NET أيضًا

اذا كنت تمتلك الصكوك الوطنية البرنامج الموجود على جهاز الكمبيوتر الخاص بك، يمكنك إنشاء برنامج تشغيل USB باستخدامه "معالج برنامج تشغيل NI-VISA".

خطوات إنشاء برنامج تشغيل USB: http://www.ni.com/tutorial/4478/en/

بمجرد إنشاء برنامج التشغيل، ستتمكن من كتابة البايتات وقراءتها على أي جهاز USB.

تأكد من رؤية برنامج التشغيل بواسطة windows ضمن "إدارة الأجهزة":

enter image description here

كود سي#:

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

أمل أن هذا يساعد شخصاما.

تنقسم أجهزة USB عادة إلى فئتين:اختبأ، وUSB.قد يكون جهاز USB جهاز Hid أو لا يكون، والعكس صحيح.عادةً ما يكون التعامل مع Hid أسهل قليلًا من التعامل مع USB المباشر.تحتوي الأنظمة الأساسية المختلفة على واجهات برمجة تطبيقات مختلفة للتعامل مع كل من 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/

فيما يلي فئتان للتعامل مع USB/Hid على مستوى 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
}

مع أي من هذه الحلول، ستحتاج إما إلى استطلاع رأي الجهاز على فترات زمنية، أو استخدام إحدى فئات الاستماع للجهاز الأصلي لواجهة برمجة التطبيقات (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
}

تأتي معظم شرائح USB مع برامج تشغيل. مختبرات السيليكون لديه واحد.

لقد حصلت على واجهة ل مراهق تعمل بشكل جيد، وذلك باستخدام هذا المقال

لقد جربت العديد من هذه الاقتراحات دون جدوى.انتهى بي الأمر بكتابة حل عملي باستخدام Java و hid4java مكتبة.باعتباري تطبيقًا لوحدة التحكم، يمكنني الحصول عليه من C# باستخدام Process.Start(), وتمرير المعلمات وكذلك قراءة الردود.يوفر هذا إدخال/إخراج HID أساسيًا ولكن بدون أحداث الاتصال/قطع الاتصال.لذلك سأحتاج إلى إعادة كتابته ليتم تشغيله كبرنامج خفي/خدمة واستخدام الأنابيب المسماة أو أي وسيلة نقل أخرى للخادم/العميل.في الوقت الحالي، يكفي إنجاز المهمة نظرًا لأن مكتبة hi4java "تعمل فقط".

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top