Search code examples
c#kernel32overlapped-io

Overlapped.AsyncResult doesn't change even after operation completed successfully


To communicate with a HID device, I use some functions from kernel32. Codes are borrowed from Microchip MLA custom HID device project. It uses blocking methods.

I found I can make these methods async. Here is what I tried for an async write:

//...

internal const uint FILE_FLAG_OVERLAPPED = 0x40000000;
//...

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool ReadFile(
    SafeFileHandle hFile,
    IntPtr lpBuffer,
    uint nNumberOfBytesToRead,
    ref uint lpNumberOfBytesRead,
    Overlapped lpOverlapped);        // Formerly: IntPtr lpOverlapped);
//...

WriteHandleToUSBDevice = CreateFile(DevicePath, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero,
    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, IntPtr.Zero);    // Formerly: 0 instead of FILE_FLAG_OVERLAPPED
//...

Overlapped OL = new Overlapped();
WriteFile(WriteHandleToUSBDevice, OUTBuffer, 65, ref BytesWritten, OL);    // Formerly: IntPtr.Zero instead of OL
//Some code to run while write operation is in progress asynchronously...
while (OL.AsyncResult == null) ;    // Wait until write is completed; waits forever.

You can find complete code in Microchip MLA custom HID device project.
OL.AsyncResult remains null although write is completed successfully; I'm sure because device receives data and responses correctly. What is wrong in my code?


Solution

  • Thanks to Gserg comment, I found a better solutions than using kernel32 C++ style functions:

    SafeFileHandle HandleToUSBDevice = CreateFile(DevicePath, GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ | FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
            IntPtr.Zero);
    
    FileStream HID = new FileStream(HandleToUSBDevice, FileAccess.ReadWrite, (int)(PacketSize + 1), true);
    
    
    Task WriteTask = Task.Factory.StartNew(() => HID.Write(OUTBuffer, 0, 65));
    //Some Code
    if (!WriteTask.Wait(2500)) throw new TimeoutException("Failed to send data within 2.5s timeout.");
    

    You can use async/await and WriteAsync if your .Net framework target is more than 4.5.

    I could use ReadFile instead of HID.Write but closing handles would be harder. Also using managed C# methods is better than importing and using unmanaged C++ functions.

    EDIT: I also add a code for BeginRead:

    IAsyncResult result = HID.BeginRead(INBuffer, 0, 65, null, null);
    while (FavoriteCondition)
    {
        MethodWhichShouldBeCalledRepeatedly();
        if (result.IsCompleted)
        {
            ProcessData(INBuffer);
        }
    }