문제
.Net(C#)을 사용하여 USB 장치로 어떻게 작업할 수 있나요?
USB 이벤트(연결/연결 끊김)를 어떻게 감지할 수 있으며 장치와 통신(읽기/쓰기)하는 방법은 무엇입니까?
이를 수행할 수 있는 기본 .Net 솔루션이 있습니까?
해결책
없다 토종의 (예: 시스템 라이브러리) 솔루션입니다.그렇기 때문에 SharpUSBLib 언급한 바와 같이 존재한다 무바.
USB 장치에 대한 자체 핸들러를 롤링하려면 다음을 확인하세요. System.IO.Ports의 SerialPort 클래스.
다른 팁
SharpUSBLib를 사용해 보았더니 컴퓨터가 망가졌습니다(시스템 복원 필요).같은 프로젝트에 참여한 동료에게도 이런 일이 일어났습니다.
LibUSBDotNet에서 대안을 찾았습니다. http://sourceforge.net/projects/libusbdotnet아직 많이 사용하지는 않았지만 좋아 보이고 최근에 업데이트되었습니다(Sharp와는 달리).
편집하다:2017년 2월 중순 기준으로 LibUSBDotNet은 약 2주 전에 업데이트되었습니다.한편 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 개체 오류가 발생합니다.
나는 추천하고 싶다 LibUSBDotNet, 제가 2년째 사용하고 있는 도서관입니다.USB 장치로 작업해야 하는 경우(요청 보내기, 응답 처리) 이 라이브러리가 제가 찾을 수 있는 최고의 솔루션이었습니다.
장점:
- 동기화 또는 비동기 모드에서 작업하는 데 필요한 모든 방법이 있습니다.
- 소스코드 제공
- 바로 사용을 시작할 수 있을 만큼 충분한 샘플입니다.
단점:
- 문서화 수준이 낮습니다(오픈 소스 프로젝트에서 흔히 발생하는 문제입니다).기본적으로 CHM 도움말 파일에서 메소드에 대한 일반적인 설명을 찾을 수 있습니다.하지만 여전히 제공된 샘플과 소스 코드만으로도 코딩에 충분하다고 생각합니다.가끔 이상한 동작을 보고 왜 이런 식으로 구현되었는지 알고 싶은데 힌트조차 얻을 수 없습니다...
- 더 이상 지원되지 않는 것 같습니다.마지막 버전은 2010년 10월에 발행되었습니다.그리고 때로는 답변을 얻기가 어렵습니다.
여기에는 C#과 함께 작동하는 SharpUSBLib 라이브러리 및 HID 드라이버를 가져오는 방법에 대한 자습서가 있습니다.
http://www.developerfusion.com/article/84338/making-usb-c-friend/
일반 툴킷이 있습니다 윈드라이버 #.NET도 지원하는 사용자 모드에서 USB 드라이버 작성용
당신이 가지고 있다면 내쇼날인스트루먼트 PC의 소프트웨어를 사용하여 USB 드라이버를 만들 수 있습니다. "NI-VISA 드라이버 마법사".
USB 드라이버를 생성하는 단계: http://www.ni.com/tutorial/4478/en/
드라이버를 생성하면 모든 USB 장치에 바이트를 쓰고 읽을 수 있습니다.
장치 관리자 아래의 창에 드라이버가 표시되는지 확인하십시오.
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();
이것이 누군가에게 도움이 되기를 바랍니다.
USB 장치는 일반적으로 두 가지 범주로 분류됩니다.숨겨져 있고 USB.USB 장치는 Hid 장치일 수도 있고 아닐 수도 있으며 그 반대의 경우도 마찬가지입니다.Hid는 일반적으로 직접 USB보다 작업하기가 조금 더 쉽습니다.플랫폼마다 USB와 Hid를 모두 처리하기 위한 API가 다릅니다.
UWP에 대한 설명서는 다음과 같습니다.
숨김: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
}
대부분의 USB 칩셋에는 드라이버가 함께 제공됩니다. 실리콘랩스 하나 있습니다.
나는 운이 좋지 않은 몇 가지 제안을 시도했습니다.나는 결국 Java를 사용하여 작동하는 솔루션을 작성했고 hid4java 도서관.콘솔 앱으로서 다음을 사용하여 C#에서 쉘 아웃할 수 있습니다. Process.Start()
, 매개변수 전달 및 응답 읽기.이는 기본 HID I/O를 제공하지만 연결/연결 해제 이벤트는 없습니다.이를 위해서는 데몬/서비스로 실행하고 명명된 파이프나 다른 서버/클라이언트 전송을 사용하도록 다시 작성해야 합니다.지금은 hi4java 라이브러리가 "그냥 작동"하므로 작업을 완료하는 데 충분합니다.