本文介绍了USB设备的异步I/O的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,有一个我想以异步方式使用的BeginRead调用. (警告,使用在Internet上发现的各种代码示例将代码拼凑在一起!)使用该示例,我希望BeginRead立即返回,然后在数据最终显示出来时再调用回调. />
相反,我看到的是它被卡住"在BeginRead调用中.我可以中断,并且程序计数器在BeginRead行上,但是如果我继续,它将不会走到任何地方. CreateFile调用和FileStream构造函数调用都设置为异步,因此我看不到它不会继续的任何原因.
如果我添加另一个创建另一个文件句柄和文件流的部分,然后首先将某些内容写到我的设备中,然后调用BeginRead,则BeginRead将立即成功完成,并且回调将被命中,并且将从设备接收数据.

我尝试仅使用.net调用并避免使用CreateFile,但是发现了.net异步处理文件的困难方式,但不适用于USB设备路径.只是(错误地)声称设备路径中存在无效字符.

除了托管代码调用原始Win32 API的混杂因素之外,还有另一个潜在的混杂因素.我正在尝试在64位系统上编译和运行.我已经发现必须将结构字段从int更改为Int32.这些路线可能还有更多.

有什么想法吗?

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




嗨SAKryukov,

对不起,代码转储.这是一个复杂的问题,因此需要更多代码来演示它.我确实做了很多工作,将其归结为最低限度的再现情况. (是的,我意识到400行仍然是一个很大的问题,但是Windows API通常不利于紧凑的代码)如果将这些代码插入一个简单的Visual Studio命令行项目中,它将编译并重现上述问题. .当然,您必须将VID和PID替换为您自己的USB设备的VID和PID,才能进行BeginRead尝试.

问题在于BeginRead正在阻塞,就好像它是一次同步尝试一样,但它应该是一个异步调用.

请特别查看CreateFile调用,相关参数,FileStream构造函数和相关参数,看看是否可以看到任何错误.

-Dan




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


这篇关于USB设备的异步I/O的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 08:29
查看更多