本文是小编为大家收集整理的关于带有USB设备的异步I/O的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。
问题描述
在下面的代码中,有一个 BeginRead 调用,我想以异步方式使用它.(警告,代码是使用在互联网上发现的各种代码示例拼凑而成的!)使用此示例,我希望 BeginRead 立即返回,然后在数据最终显示时稍后触发回调.
相反,我看到的是它在 BeginRead 调用中"卡住"了.我可以中断,程序计数器在 BeginRead 行,但如果我继续,它不会去任何地方.CreateFile 调用和 FileStream 构造函数调用都设置为异步的,所以我看不出它不会继续的任何原因.
如果我添加另一个部分来创建另一个文件句柄和文件流,然后先将一些内容写入我的设备,然后调用 BeginRead,BeginRead 将立即成功完成,并且回调被命中,并且接收到来自设备的数据.
我尝试仅使用 .net 调用并避免使用 CreateFile,但发现 .net async 很难处理文件,但不适用于 USB 设备路径.只是声称(错误地)设备路径中有无效字符.
除了调用原始 Win32 API 的托管代码的混淆因素外,还有另一个潜在的混淆因素.我正在尝试在 64 位系统上编译和运行.我已经发现我必须将结构字段从 int 更改为 Int32.可能还有更多类似的东西.
有什么想法吗?
使用系统;使用 System.Collections.Generic;使用 System.ComponentModel;使用 System.Diagnostics;使用 System.IO;使用 System.Runtime.InteropServices;使用 Microsoft.Win32.SafeHandles;命名空间 Async_Multi_USB{class 程序{内部 结构 SP_DEVICE_INTERFACE_DATA{internal Int32 cbSize;internal System.Guid InterfaceClassGuid;internal Int32 标志;internal IntPtr 保留;}[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]internal 静态 extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr 枚举器, IntPtr hwndParent, Int32 标志);//来自 setupapi.hinternal const Int32 DIGCF_PRESENT = 2;internal const Int32 DIGCF_DEVICEINTERFACE = 0X10;[DllImport("hid.dll", SetLastError = true)]public 静态 extern void HidD_GetHidGuid(ref System.Guid HidGuid);[DllImport("setupapi.dll", SetLastError = true)]internal 静态 extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]internal 静态 extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData);[DllImport("setupapi.dll", SetLastError = true)]internal 静态 extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);internal const Int32 FILE_SHARE_READ = 1;internal const Int32 FILE_SHARE_WRITE = 2;internal const UInt32 GENERIC_READ = 0x80000000U;internal const UInt32 GENERIC_WRITE = 0x40000000U;internal const Int32 INVALID_HANDLE_VALUE = -1;internal const Int32 OPEN_EXISTING = 3;[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]internal static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes,Int32 hTemplateFile);//在互联网上发现的神奇数字......// 据说它们来自某处的windows头文件.public const Int32 DUPLEX = (0x00000003);public const Int32 FILE_FLAG_OVERLAPPED = (0x40000000);[DllImport("hid.dll", SetLastError = true)]internal 静态 extern Boolean HidD_GetPreparsedData(SafeFileHandle HidDeviceObject, ref IntPtr PreparsedData);内部 结构 HIDP_CAPS{internal Int16 用法;internal Int16 UsagePage;internal Int16 InputReportByteLength;internal Int16 OutputReportByteLength;internal Int16 FeatureReportByteLength;[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]internal Int16[] 保留;internal Int16 NumberLinkCollectionNodes;internal Int16 NumberInputButtonCaps;internal Int16 NumberInputValueCaps;internal Int16 NumberInputDataIndices;internal Int16 NumberOutputButtonCaps;internal Int16 NumberOutputValueCaps;internal Int16 NumberOutputDataIndices;internal Int16 NumberFeatureButtonCaps;internal Int16 NumberFeatureValueCaps;internal Int16 NumberFeatureDataIndices;}[DllImport("hid.dll", SetLastError = true)]internal 静态 extern Int32 HidP_GetCaps(IntPtr PreparsedData, ref HIDP_CAPS Capabilities);static public void OnReadComplete(IAsyncResult ar){试试{//m_fsDeviceRead.EndRead(ar);if ((ar.IsCompleted)){// 将数据传输回异步层.}}catch (IOException ex){// 如果设备已断开连接,则忽略该错误.Debug.WriteLine(ex.Message);}}public sealed class Hresults{public const int NOERROR = 0;public const int S_OK = 0;public const int S_FALSE = 1;public const int E_PENDING = 未选中((int)0x8000000A);public const int E_HANDLE = 未选中((int)0x80070006);public const int E_NOTIMPL = 未选中((int)0x80004001);public const int E_NOINTERFACE = 未选中((int)0x80004002);//ArgumentNullException.NullReferenceException 使用 COR_E_NULLREFERENCEpublic const int E_POINTER = 未选中((int)0x80004003);public const int E_ABORT = 未选中((int)0x80004004);public const int E_FAIL = 未选中((int)0x80004005);public const int E_OUTOFMEMORY = 未选中((int)0x8007000E);public const int E_ACCESSDENIED = 未选中((int)0x80070005);public const int E_UNEXPECTED = 未选中((int)0x8000FFFF);public const int E_FLAGS = 未选中((int)0x1000);public const int E_INVALIDARG = 未选中((int)0x80070057);//Wininetpublic const int ERROR_SUCCESS = 0;public const int ERROR_FILE_NOT_FOUND = 2;public const int ERROR_ACCESS_DENIED = 5;public const int ERROR_INSUFFICIENT_BUFFER = 122;}static void Main(string[] args){Guid guidHID = new Guid();HidD_GetHidGuid(ref guidHID);IntPtr hDeviceInfoSet = SetupDiGetClassDevs(ref guidHID, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);// MyDeviceInterfaceData结构的cbSize元素必须设置为// 结构的大小(以字节为单位).// 32位代码大小为28字节,64位代码大小为32字节.SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = 新 SP_DEVICE_INTERFACE_DATA();DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData);// 循环通过操作系统找到的设备集,创建我们的设备列表.列表<字符串>listHidDevices = new List<String>();Int32 i32MemberIndex = 0;while (true){//从0开始,通过设备信息集递增直到//没有更多设备可用.布尔 bSuccess = SetupDiEnumDeviceInterfaces(hDeviceInfoSet,IntPtr.Zero, ref guidHID, i32MemberIndex,ref DeviceInterfaceData);if (!bSuccess){// 如果失败,则意味着我们已经到了列表的末尾.break;}//设备存在.//找出需要多大的缓冲区.Int32 i32BufferSize = 0;bSuccess = SetupDiGetDeviceInterfaceDetail(hDeviceInfoSet,ref DeviceInterfaceData, IntPtr.Zero, 0,ref i32BufferSize, IntPtr.Zero);String sErrorMessage;if (bSuccess){//这个成功出乎意料!我们想得到一个错误,服务员// 成功调用缓冲区大小的信息.sErrorMessage = "无法获取 USB 设备详细信息的缓冲区大小.";Console.WriteLine(sErrorMessage);Debug.Assert(bSuccess);}sErrorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;if (Marshal.GetLastWin32Error() != Hresults.ERROR_INSUFFICIENT_BUFFER){sErrorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;Console.WriteLine(sErrorMessage);Debug.Assert(bSuccess);}// 使用返回的缓冲区大小为 SP_DEVICE_INTERFACE_DETAIL_DATA 结构分配内存.IntPtr pDetailDataBuffer = Marshal.AllocHGlobal(i32BufferSize);// 将 cbSize 存储在数组的第一个字节中.字节数因 32 位和 64 位系统而异.Marshal.WriteInt32(pDetailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);//再次调用SetupDiGetDeviceInterfaceDetail.//这一次,传递一个指向DetailDataBuffer的指针// 以及返回的所需缓冲区大小.bSuccess = SetupDiGetDeviceInterfaceDetail(hDeviceInfoSet,ref DeviceInterfaceData, pDetailDataBuffer,i32BufferSize, ref i32BufferSize, IntPtr.Zero);if (!bSuccess){sErrorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;Console.WriteLine(sErrorMessage);Debug.Assert(bSuccess);}//跳过cbsize(4字节)得到devicePathName的地址.IntPtr pDevicePathName = new IntPtr(pDetailDataBuffer.ToInt32() + 4);// 获取包含 devicePathName 的字符串.String sDevicePathName = Marshal.PtrToStringAuto(pDevicePathName).ToLower();//查看设备路径是否包含我们的VID和PID.UInt16 u16Vid = 0x148a;UInt16 u16Pid = 0x0004;String sSearch = String.Format("vid_{0:x4}&pid_{1:x4}", u16Vid, u16Pid);if (sDevicePathName.Contains(sSearch)){listHidDevices.Add(sDevicePathName);Console.WriteLine("找到设备:" + sDevicePathName);}if (pDetailDataBuffer != IntPtr.Zero){//释放AllocHGlobal之前分配的内存.Marshal.FreeHGlobal(pDetailDataBuffer);}i32会员索引++;}if (hDeviceInfoSet != IntPtr.Zero){SetupDiDestroyDeviceInfoList(hDeviceInfoSet);}if (listHidDevices.Count == 0){//未找到设备.返回;}SafeFileHandle hHidRead = CreateFile(listHidDevices[0],GENERIC_READ |GENERIC_WRITE,FILE_SHARE_READ |FILE_SHARE_WRITE,IntPtr.Zero, // securityAttributesOPEN_EXISTING, // creationDispositionFILE_FLAG_OVERLAPPED, //|DUPLEX,//标志0);//模板if (hHidRead.IsInvalid){string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;Console.WriteLine(errorMessage);//不行,得保释.返回;}IntPtr pPreparsedData = new System.IntPtr();if (!HidD_GetPreparsedData(hHidRead, ref pPreparsedData)){SystemException se = new SystemException("调用 HidD_GetPreparsedData 失败.");抛出 se;}HIDP_CAPS 能力 = 新 HIDP_CAPS();Int32 i32Result = HidP_GetCaps(pPreparsedData, ref Capabilities);if (i32Result == 0){SystemException se = new SystemException("调用 HidD_GetCaps 失败.");抛出 se;}Int16 i16OutputReportLen = Capabilities.OutputReportByteLength;Int16 i16InputReportLen = Capabilities.InputReportByteLength;const Int32 i32MaxPPCPLen = 0x1000;FileStream fsDeviceRead = new FileStream(hHidRead, FileAccess.Read,i32MaxPPCPLen, true);// 让异步读取悬空.BeginAsyncRead(ref fsDeviceRead, i16InputReportLen);hHidRead.Close();}public struct SyncObj_t{public FileStream fs;public 字节[] buf;};/// <摘要> /// 启动异步读取,在读取数据或设备时完成 /// 已断开连接.使用回调. /// </summary> static public void BeginAsyncRead(ref FileStream fs, int iBufLen){SyncObj_t syncObj = new SyncObj_t();同步对象.fs = fs;syncObj.buf = new 字节[iBufLen];//把我们用来接收东西的buff设置为异步状态,然后我们可以在读取完成时获取它跨度>fs.BeginRead(syncObj.buf, 0, iBufLen, new AsyncCallback(ReadCompleted), syncObj);}/// <摘要> /// 上述回调.请注意这一点,因为它将在异步读取的后台线程上调用 /// </summary> /// <参数 name="iResult">异步结果参数</param> static protected void ReadCompleted(IAsyncResult iResult){// 检索流并读取缓冲区.SyncObj_t syncObj = (SyncObj_t)iResult.AsyncState;试试{// call end read : 这会抛出读取过程中发生的任何异常syncObj.fs.EndRead(iResult);试试{//InputReport oInRep = CreateInputReport();//为设备创建输入报告//oInRep.SetData(arrBuff);//并设置数据部分——这会将接收到的数据处理成更容易理解的格式,具体取决于报告类型// 将新的输入报告传递给更高级别的处理程序.//HandleDataReceived(oInRep);}终于{// 完成所有操作后,开始阅读下一份报告BeginAsyncRead(ref syncObj.fs, syncObj.buf.Length);}}catch (IOException ex) // 如果我们有 IO异常,设备已被移除{Console.WriteLine(ex.ToString());/*HandleDeviceRemoved();如果(OnDeviceRemoved != null){OnDeviceRemoved(this, new EventArgs());}处置();*/}}}}
嗨 SAKryukov,
对不起代码转储.这是一个复杂的问题,因此需要更多的代码来演示它.我确实做了很多工作来将其归结为最小的复制案例.(是的,我意识到 400 行仍然是一个巨大的重现案例,但 Windows API 通常不利于紧凑的代码)如果您将该代码插入一个简单的 Visual Studio 命令行项目,它将编译并重现上述问题.当然,您必须将 VID 和 PID 替换为您自己的 USB 设备,才能开始尝试 BeginRead.
问题是 BeginRead 像同步尝试一样被阻塞,但它应该是异步调用.
请特别查看 CreateFile 调用和相关参数,以及 FileStream 构造函数和相关参数,看看是否有任何错误.
-Dan
推荐答案
static public void BeginAsyncRead
应该是static public IAsyncResult BeginAsyncRead
问题描述
In the code below, there is a BeginRead call which I want to use in an async manner. (warning, code is cobbled together using a variety of code samples found lying around on the internet!) With this sample, I expect the BeginRead to return immediately, and then the callback to be hit later on when data finally shows up.
Instead what I''m seeing, is that it gets "stuck" inside the BeginRead call. I can break, and the program counter is on the BeginRead line, but if I continue, it won''t go anywhere. Both the CreateFile call and the FileStream constructor call are set up to be asynchronous, so I can''t see any reason it wouldn''t continuing.
If I add another section which creates another file handle and file stream, and then write something out to my device first, THEN call BeginRead, the BeginRead will successfully complete immediately, and the callback gets hit, and the data from the device is received.
I tried using .net calls only and avoiding CreateFile, but discovered the hard way that .net async works with files, but doesn''t work with a USB device path. Just claims (erroneously) that there are invalid characters in the device path.
Besides the confounding factor of managed code calling an original Win32 API, there is another potential confounder. I am trying to compile and run on a 64-bit system. I already discovered I had to change struct fields from int to Int32. There may be something more along those lines.
Any ideas?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace Async_Multi_USB
{
class Program
{
internal struct SP_DEVICE_INTERFACE_DATA
{
internal Int32 cbSize;
internal System.Guid InterfaceClassGuid;
internal Int32 Flags;
internal IntPtr Reserved;
}
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern IntPtr SetupDiGetClassDevs(ref System.Guid ClassGuid, IntPtr Enumerator, IntPtr hwndParent, Int32 Flags);
// From setupapi.h
internal const Int32 DIGCF_PRESENT = 2;
internal const Int32 DIGCF_DEVICEINTERFACE = 0X10;
[DllImport("hid.dll", SetLastError = true)]
public static extern void HidD_GetHidGuid(ref System.Guid HidGuid);
[DllImport("setupapi.dll", SetLastError = true)]
internal static extern Boolean SetupDiEnumDeviceInterfaces(IntPtr DeviceInfoSet, IntPtr DeviceInfoData, ref System.Guid InterfaceClassGuid, Int32 MemberIndex, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData);
[DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern Boolean SetupDiGetDeviceInterfaceDetail(IntPtr DeviceInfoSet, ref SP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IntPtr DeviceInterfaceDetailData, Int32 DeviceInterfaceDetailDataSize, ref Int32 RequiredSize, IntPtr DeviceInfoData);
[DllImport("setupapi.dll", SetLastError = true)]
internal static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);
internal const Int32 FILE_SHARE_READ = 1;
internal const Int32 FILE_SHARE_WRITE = 2;
internal const UInt32 GENERIC_READ = 0x80000000U;
internal const UInt32 GENERIC_WRITE = 0x40000000U;
internal const Int32 INVALID_HANDLE_VALUE = -1;
internal const Int32 OPEN_EXISTING = 3;
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern SafeFileHandle CreateFile(String lpFileName, UInt32 dwDesiredAccess, Int32 dwShareMode, IntPtr lpSecurityAttributes, Int32 dwCreationDisposition, Int32 dwFlagsAndAttributes, Int32 hTemplateFile);
// Magic numbers found lying around on the internet...
// Supposedly they come from a windows header file somewhere.
public const Int32 DUPLEX = (0x00000003);
public const Int32 FILE_FLAG_OVERLAPPED = (0x40000000);
[DllImport("hid.dll", SetLastError = true)]
internal static extern Boolean HidD_GetPreparsedData(SafeFileHandle HidDeviceObject, ref IntPtr PreparsedData);
internal struct HIDP_CAPS
{
internal Int16 Usage;
internal Int16 UsagePage;
internal Int16 InputReportByteLength;
internal Int16 OutputReportByteLength;
internal Int16 FeatureReportByteLength;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)]
internal Int16[] Reserved;
internal Int16 NumberLinkCollectionNodes;
internal Int16 NumberInputButtonCaps;
internal Int16 NumberInputValueCaps;
internal Int16 NumberInputDataIndices;
internal Int16 NumberOutputButtonCaps;
internal Int16 NumberOutputValueCaps;
internal Int16 NumberOutputDataIndices;
internal Int16 NumberFeatureButtonCaps;
internal Int16 NumberFeatureValueCaps;
internal Int16 NumberFeatureDataIndices;
}
[DllImport("hid.dll", SetLastError = true)]
internal static extern Int32 HidP_GetCaps(IntPtr PreparsedData, ref HIDP_CAPS Capabilities);
static public void OnReadComplete(IAsyncResult ar)
{
try
{
//m_fsDeviceRead.EndRead(ar);
if ((ar.IsCompleted))
{
// Transmit the data back up to the Async layer.
}
}
catch (IOException ex)
{
// Just ignore the error if the device has been disconnected.
Debug.WriteLine(ex.Message);
}
}
public sealed class Hresults
{
public const int NOERROR = 0;
public const int S_OK = 0;
public const int S_FALSE = 1;
public const int E_PENDING = unchecked((int)0x8000000A);
public const int E_HANDLE = unchecked((int)0x80070006);
public const int E_NOTIMPL = unchecked((int)0x80004001);
public const int E_NOINTERFACE = unchecked((int)0x80004002);
//ArgumentNullException. NullReferenceException uses COR_E_NULLREFERENCE
public const int E_POINTER = unchecked((int)0x80004003);
public const int E_ABORT = unchecked((int)0x80004004);
public const int E_FAIL = unchecked((int)0x80004005);
public const int E_OUTOFMEMORY = unchecked((int)0x8007000E);
public const int E_ACCESSDENIED = unchecked((int)0x80070005);
public const int E_UNEXPECTED = unchecked((int)0x8000FFFF);
public const int E_FLAGS = unchecked((int)0x1000);
public const int E_INVALIDARG = unchecked((int)0x80070057);
//Wininet
public const int ERROR_SUCCESS = 0;
public const int ERROR_FILE_NOT_FOUND = 2;
public const int ERROR_ACCESS_DENIED = 5;
public const int ERROR_INSUFFICIENT_BUFFER = 122;
}
static void Main(string[] args)
{
Guid guidHID = new Guid();
HidD_GetHidGuid(ref guidHID);
IntPtr hDeviceInfoSet = SetupDiGetClassDevs(ref guidHID, IntPtr.Zero, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
// The cbSize element of the MyDeviceInterfaceData structure must be set to
// the structure's size in bytes.
// The size is 28 bytes for 32-bit code and 32 bytes for 64-bit code.
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
DeviceInterfaceData.cbSize = Marshal.SizeOf(DeviceInterfaceData);
// Loop through the set of devices found by the OS, creating a list of our devices.
List<String> listHidDevices = new List<String>();
Int32 i32MemberIndex = 0;
while (true)
{
// Begin with 0 and increment through the device information set until
// no more devices are available.
Boolean bSuccess = SetupDiEnumDeviceInterfaces(hDeviceInfoSet,
IntPtr.Zero, ref guidHID, i32MemberIndex,
ref DeviceInterfaceData);
if ( ! bSuccess)
{
// If it fails, that means we've reached the end of the list.
break;
}
// A device is present.
// Find out how big of a buffer is needed.
Int32 i32BufferSize = 0;
bSuccess = SetupDiGetDeviceInterfaceDetail(hDeviceInfoSet,
ref DeviceInterfaceData, IntPtr.Zero, 0,
ref i32BufferSize, IntPtr.Zero);
String sErrorMessage;
if (bSuccess)
{
// This success is unexpected! We wanted to get an error, with the attendant
// information of how big to make the buffer for a successful call.
sErrorMessage = "Unable to get the buffer size for USB device detail.";
Console.WriteLine(sErrorMessage);
Debug.Assert(bSuccess);
}
sErrorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
if (Marshal.GetLastWin32Error() != Hresults.ERROR_INSUFFICIENT_BUFFER)
{
sErrorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Console.WriteLine(sErrorMessage);
Debug.Assert(bSuccess);
}
// Allocate memory for the SP_DEVICE_INTERFACE_DETAIL_DATA structure using the returned buffer size.
IntPtr pDetailDataBuffer = Marshal.AllocHGlobal(i32BufferSize);
// Store cbSize in the first bytes of the array. The number of bytes varies with 32- and 64-bit systems.
Marshal.WriteInt32(pDetailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
// Call SetupDiGetDeviceInterfaceDetail again.
// This time, pass a pointer to DetailDataBuffer
// and the returned required buffer size.
bSuccess = SetupDiGetDeviceInterfaceDetail(hDeviceInfoSet,
ref DeviceInterfaceData, pDetailDataBuffer,
i32BufferSize, ref i32BufferSize, IntPtr.Zero);
if ( ! bSuccess)
{
sErrorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Console.WriteLine(sErrorMessage);
Debug.Assert(bSuccess);
}
// Skip over cbsize (4 bytes) to get the address of the devicePathName.
IntPtr pDevicePathName = new IntPtr(pDetailDataBuffer.ToInt32() + 4);
// Get the String containing the devicePathName.
String sDevicePathName = Marshal.PtrToStringAuto(pDevicePathName).ToLower();
// See if the device path includes our VID and PID.
UInt16 u16Vid = 0x148a;
UInt16 u16Pid = 0x0004;
String sSearch = String.Format("vid_{0:x4}&pid_{1:x4}", u16Vid, u16Pid);
if (sDevicePathName.Contains(sSearch))
{
listHidDevices.Add(sDevicePathName);
Console.WriteLine("Found device: " + sDevicePathName);
}
if (pDetailDataBuffer != IntPtr.Zero)
{
// Free the memory allocated previously by AllocHGlobal.
Marshal.FreeHGlobal(pDetailDataBuffer);
}
i32MemberIndex++;
}
if (hDeviceInfoSet != IntPtr.Zero)
{
SetupDiDestroyDeviceInfoList(hDeviceInfoSet);
}
if (listHidDevices.Count == 0)
{
// No devices found.
return;
}
SafeFileHandle hHidRead = CreateFile(listHidDevices[0],
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero, // securityAttributes
OPEN_EXISTING, // creationDisposition
FILE_FLAG_OVERLAPPED, //| DUPLEX, // flags
0); // template
if (hHidRead.IsInvalid)
{
string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
Console.WriteLine(errorMessage);
// Can't do it, have to bail.
return;
}
IntPtr pPreparsedData = new System.IntPtr();
if ( ! HidD_GetPreparsedData(hHidRead, ref pPreparsedData))
{
SystemException se = new SystemException("Call to HidD_GetPreparsedData failed.");
throw se;
}
HIDP_CAPS Capabilities = new HIDP_CAPS();
Int32 i32Result = HidP_GetCaps(pPreparsedData, ref Capabilities);
if (i32Result == 0)
{
SystemException se = new SystemException("Call to HidD_GetCaps failed.");
throw se;
}
Int16 i16OutputReportLen = Capabilities.OutputReportByteLength;
Int16 i16InputReportLen = Capabilities.InputReportByteLength;
const Int32 i32MaxPPCPLen = 0x1000;
FileStream fsDeviceRead = new FileStream(hHidRead, FileAccess.Read,
i32MaxPPCPLen, true);
// Leave an async read dangling.
BeginAsyncRead(ref fsDeviceRead, i16InputReportLen);
hHidRead.Close();
}
public struct SyncObj_t
{
public FileStream fs;
public Byte[] buf;
};
/// <summary>
/// Kicks off an asynchronous read which completes when data is read or when the device
/// is disconnected. Uses a callback.
/// </summary>
static public void BeginAsyncRead(ref FileStream fs, int iBufLen)
{
SyncObj_t syncObj = new SyncObj_t();
syncObj.fs = fs;
syncObj.buf = new Byte[iBufLen];
// Put the buff we used to receive the stuff as the async state then we can get at it when the read completes
fs.BeginRead(syncObj.buf, 0, iBufLen, new AsyncCallback(ReadCompleted), syncObj);
}
/// <summary>
/// Callback for above. Care with this as it will be called on the background thread from the async read
/// </summary>
/// <param name="iResult">Async result parameter</param>
static protected void ReadCompleted(IAsyncResult iResult)
{
// Retrieve the stream and read buffer.
SyncObj_t syncObj = (SyncObj_t)iResult.AsyncState;
try
{
// call end read : this throws any exceptions that happened during the read
syncObj.fs.EndRead(iResult);
try
{
//InputReport oInRep = CreateInputReport(); // Create the input report for the device
//oInRep.SetData(arrBuff); // and set the data portion - this processes the data received into a more easily understood format depending upon the report type
// Pass the new input report on to the higher level handler.
//HandleDataReceived(oInRep);
}
finally
{
// when all that is done, kick off another read for the next report
BeginAsyncRead(ref syncObj.fs, syncObj.buf.Length);
}
}
catch (IOException ex) // if we got an IO exception, the device was removed
{
Console.WriteLine(ex.ToString());
/*
HandleDeviceRemoved();
if (OnDeviceRemoved != null)
{
OnDeviceRemoved(this, new EventArgs());
}
Dispose();
*/
}
}
}
}
Hi SAKryukov,
Sorry about the code dump. It is a complex problem, so more code is needed to demonstrate it. I really did a lot of work to boil it down to a minimum repro case. (yes, I realize 400 lines is still a huge repro case, but the Windows APIs are often not conducive to tight code) If you plug that code into a simple Visual Studio command line project, it will compile and reproduce the problem as stated above. You would have to replace the VID and PID with that of your own USB device of course, to get down to the BeginRead attempt.
The problem is that BeginRead is blocking as if it were a synchronous attempt, but it should be an Asynchronous call.
Please look particularly at the CreateFile call, and related parameters, and the FileStream constructor, and related params, and see if you can see any thing wrong.
-Dan
推荐答案
static public void BeginAsyncRead
should be
static public IAsyncResult BeginAsyncRead