【问题标题】:FileStream.BeginRead doesn't seem to return to callbackFileStream.BeginRead 似乎没有返回回调
【发布时间】:2013-02-08 15:27:23
【问题描述】:

我正在使用 Async 通过 c# 读取 USB 设备。我正在使用文件流来做到这一点。当我执行 FileStream.BeginRead 时。它似乎没有做回调。这是什么问题?

这是我的代码

 /// <summary>
    /// Initialises the device
    /// </summary>
    /// <param name="strPath">Path to the device</param>
    private void Initialise(String strPath)
    {
        // Create the file from the device path
        SafeHandle = CreateFile(strPath, FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, 
            EFileAttributes.Overlapped, IntPtr.Zero);
        if (SafeHandle != null) // if the open worked...
        {
            IntPtr lpData;
            if (HidD_GetPreparsedData(SafeHandle, out lpData))
                // get windows to read the device data into an internal buffer
            {
                try
                {
                    HidCaps hidCaps;
                    HidP_GetCaps(lpData, out hidCaps); // extract the device capabilities from the internal buffer
                    InputReportLength = hidCaps.InputReportByteLength; // get the input...
                    OutputReportLength = hidCaps.OutputReportByteLength; // ... and output report lengths
                    FileStream = new FileStream((SafeFileHandle) SafeHandle, FileAccess.ReadWrite, InputReportLength,
                                                true); // wrap the file handle in a .Net file stream
                    BeginAsyncRead(); // kick off the first asynchronous read
                }
                finally
                {
                    HidD_FreePreparsedData(ref lpData);
                        // before we quit the function, we must free the internal buffer reserved in GetPreparsedData
                }
            }
            else // GetPreparsedData failed? Chuck an exception
                throw HidDeviceException.GenerateWithWinError("GetPreparsedData failed");
        }
        else    // File open failed? Chuck an exception
        {
            SafeHandle = null;
            throw HidDeviceException.GenerateWithWinError("Failed to create device file");
        }
    }

这是回调

       /// <summary>
    /// Kicks off an asynchronous read which completes when data is read or when the device
    /// is disconnected. Uses a callback.
    /// </summary>
    private void BeginAsyncRead()
    {
        Byte[] buffer = new Byte[InputReportLength];
        // put the buff we used to receive the stuff as the async state then we can get at it when the read completes
        FileStream.BeginRead(buffer, 0, InputReportLength, ReadCompleted, buffer);
    }

这是我的回调

        /// <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>
    private void ReadCompleted(IAsyncResult iResult)
    {
        Byte[] buffer = (Byte[])iResult.AsyncState; // retrieve the read buffer
        try
        {
            FileStream.EndRead(iResult);    // call end read : this throws any exceptions that happened during the read
            try
            {
                HandleDataReceived(buffer); // pass the new input report on to the higher level handler
            }
            finally
            {
                BeginAsyncRead();   // when all that is done, kick off another read for the next report
            }
        }
        catch (IOException) // if we got an IO exception, the device was removed
        {
            HandleDeviceRemoved();
            if (OnDeviceRemoved != null)
                OnDeviceRemoved(this, new EventArgs());
            Dispose();
        }
    }

更新我想知道我在 devicePath 中是否做错了

        /// <summary>
    /// Helper method to return the device path given a DeviceInterfaceData structure and an InfoSet handle.
    /// Used in 'FindDevice' so check that method out to see how to get an InfoSet handle and a DeviceInterfaceData.
    /// </summary>
    /// <param name="hInfoSet">Handle to the InfoSet</param>
    /// <param name="oInterface">DeviceInterfaceData structure</param>
    /// <returns>The device path or null if there was some problem</returns>
    private static string GetDevicePath(IntPtr hInfoSet, ref DeviceInterfaceData oInterface)
    {
        uint nRequiredSize = 0;
        // Get the device interface details
        if (!SetupDiGetDeviceInterfaceDetail(hInfoSet, ref oInterface, IntPtr.Zero, 0, ref nRequiredSize, IntPtr.Zero))
        {
            DeviceInterfaceDetailData oDetail = new DeviceInterfaceDetailData();

            oDetail.Size = Marshal.SizeOf(typeof(IntPtr)) == 4 ? 8 : 5; // check if is a 64 bit

            if (SetupDiGetDeviceInterfaceDetail(hInfoSet, ref oInterface, ref oDetail, nRequiredSize, ref nRequiredSize, IntPtr.Zero))
            {
                return oDetail.DevicePath;
            }
        }
        return null;
    }

【问题讨论】:

  • 显示您的代码..然后其他人可以帮助您。
  • ReadCompleted 在哪里?这才是真正的关键功能。此外,不要将缓冲区作为最后一个参数发送,发送流是因为您需要关闭它(让文件无限期打开并不好)。真的,我们需要并排查看 BeginAsyncRead 和 ReadCompleted。
  • @DanielKelley 我只是我的代码的副本
  • 到目前为止看起来不错,如果你在 BeginAsyncRead 和 ReadCompleted 中设置断点,你会同时击中它们吗?
  • @sircodesalot:它只隐藏 BeginAsyncRead 从不 ReadComplete

标签: c# usb hid


【解决方案1】:

您是否将回调作为参数添加到方法调用中,例如以下示例:

public void LoadFile(string fileName)
{  
    currentFileStream = new FileStream("Foo.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024 * 8, true);
    buffer = new byte[currentFileStream.Length];
    currentFileStream.BeginRead(buffer, 0, buffer.Length, FileReadComplete, currentFileStream);
}

private void FileReadComplete(IAsyncResult result)
{
    // do your code
}

【讨论】:

  • 所以在为此编写示例时,我遇到了一些事情。 IAsyncResult.AsyncState 不能只保存一项。那么如何在不创建另一个结构的情况下将文件流(为了关闭它)和缓冲区(为了捕获存储的数据)获取到回调?在我的示例中,我只是让它全球化,但我认为这很糟糕。
  • @sircodesalot 只需使用闭包来捕获您需要的所有变量。
【解决方案2】:
// Create a synchronization object that gets  
// signaled when verification is complete.
ManualResetEvent manualEvent = new ManualResetEvent(false);

// Create random data to write to the file. 
byte[] writeArray = new byte[100000];
new Random().NextBytes(writeArray);

FileStream fStream = 
    new FileStream("Test#@@#.dat", FileMode.Create, 
    FileAccess.ReadWrite, FileShare.None, 4096, true);

// Check that the FileStream was opened asynchronously.
Console.WriteLine("fStream was {0}opened asynchronously.",
    fStream.IsAsync ? "" : "not ");

// Asynchronously write to the file.
IAsyncResult asyncResult = fStream.BeginWrite(
    writeArray, 0, writeArray.Length, 
    new AsyncCallback(EndWriteCallback), 
    new State(fStream, writeArray, manualEvent));

// Concurrently do other work and then wait  
// for the data to be written and verified.
manualEvent.WaitOne(5000, false);

【讨论】:

    【解决方案3】:

    所以你可以看一下我写的一个快速示例。

    class Program
    {
        static Byte[] buffer = new Byte[1024];
    
        public static void Main()
        {
            FileStream fileStream = File.OpenRead("data.txt");
            fileStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(FileReadCallback), fileStream);
    
            Console.ReadLine();
        }
    
        public static void FileReadCallback(IAsyncResult result)
        {
            FileStream fileStream = (FileStream)result.AsyncState;
            fileStream.EndRead(result);
    
            foreach (Byte b in buffer)
                Console.Write((Char)b);
    
        }
    }
    

    虽然这种方法有效,但它是非常古老的 c++ 风格。写这样的东西要容易得多:

        public static void Main()
        {
            using (FileStream fileStream = File.OpenRead("data.txt"))
            using (StreamReader reader = new StreamReader(fileStream))
            {
                // This will read a single line of text
                String line = reader.ReadLine();
            }
        }
    

    然后使用System.Threading.Tasks.Task 类在另一个线程上执行它,如下所示:

        public static void Main()
        {
            Task.Run(ThisWillExecuteOnAnotherThread);
        }
    
        public static void ThisWillExecuteOnAnotherThread()
        {
            using (FileStream fileStream = File.OpenRead("data.txt"))
            using (StreamReader reader = new StreamReader(fileStream))
            {
                // This will read a single line of text
                String line = reader.ReadLine();
            }
        }
    

    我个人认为这种风格要干净得多。

    更新

    所以这个设置在我的机器上运行良好:

    class Program
    {
        static byte[] buffer;
        static int InputReportLength = 1024;
        static FileStream fileStream;
        public static void Main()
        {
            BeginAsyncRead();
        }
    
        private static void BeginAsyncRead()
        {
            buffer = new Byte[InputReportLength];
            fileStream = File.OpenRead("Data.txt");
    
            // put the buff we used to receive the stuff as the async state then we can get at it when the read completes
            fileStream.BeginRead(buffer, 0, InputReportLength, ReadCompleted, buffer);
        }
    
        private static void ReadCompleted(IAsyncResult iResult)
        {
            Byte[] buffer = (Byte[])iResult.AsyncState; // retrieve the read buffer
    
        }
    
    }
    

    我建议你不要这样做:

    FileStream FileStream = ....
    

    通常你应该'PascalCase'你的类名和'camelCase'你的变量。此外,作为一般建议,代码的结构方式要求您在页面上进行多次跳转,如果您以后需要再次查看代码,这将使您难以维护。尽量让事物尽可能靠近。

    【讨论】:

    • 您想使用Task.Factory.StartNewTask.Run 而不是new Task。部分原因是您需要调用 Start() 才能真正启动它,并且因为只有在您不打算立即启动时才能构建这样的任务。
    • @sircodesalot 正如我所说,你不应该使用 Task 构造函数,你应该使用 RunStartNew
    • 哦,我现在明白了。谢谢你,先生。不知道 System.Threading.Thread 是否有类似的语义?
    猜你喜欢
    • 1970-01-01
    • 2022-11-30
    • 2017-04-25
    • 1970-01-01
    • 1970-01-01
    • 2017-03-20
    • 1970-01-01
    • 2018-05-16
    • 2014-03-24
    相关资源
    最近更新 更多