0

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?

AmirSina Mashayekh
  • 498
  • 1
  • 5
  • 21
  • 1
    `You can find complete code in Microchip MLA custom HID device project` - the question must contain all relevant code in itself. – GSerg Aug 08 '20 at 08:14
  • @GSerg "All relevant code" will become really large! Also, someone experienced should be able to detect my error as I included most important parts and other parts are same in most applications. – AmirSina Mashayekh Aug 08 '20 at 08:21
  • @GSerg I added `ref` but it got worse: `Managed Debugging Assistant 'FatalExecutionEngineError' : 'The runtime has encountered a fatal error...'` – AmirSina Mashayekh Aug 08 '20 at 08:32
  • I didn't realise you were using the managed `System.Threading.Overlapped`. But I did say "if it's a struct", which it isn't. – GSerg Aug 08 '20 at 08:33
  • 1
    Can you not simply [do this](https://stackoverflow.com/a/24552771/11683)? – GSerg Aug 08 '20 at 08:34
  • 1
    The Overlapped class has constructors that take an IAsyncResult parameter. Meant to make asynchronous usage practical, the intention of overlapped I/O, a single callback method can handle multiple async reads. You are not using such a constructor, nor do anything obvious to handle the completion async, so AsyncResult must be null. There is no point in using Overlapped or async code at all if you write code that waits for the completion. Make it async, if intended, with FileStream.BeginRead() or ReadAsync(). – Hans Passant Aug 08 '20 at 12:37

1 Answers1

0

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);
    }
}
AmirSina Mashayekh
  • 498
  • 1
  • 5
  • 21
  • That is a very wrong thing you did there. Please see https://stackoverflow.com/a/13140963/11683 and https://blog.stephencleary.com/2013/08/startnew-is-dangerous.html, and simply use `await` like in the [example](https://stackoverflow.com/a/24552771/11683). – GSerg Aug 08 '20 at 11:52
  • @GSerg Sorry! You're right. I forgot to mention `//Some Code` part. Code will start write, execute some other code and then wait for write result. I can't use `async` because there is no `Stream.ReadAsync` method in .Net 4.0 (target of my app) and `Stream.BeginWrite` doesn't support `async`. – AmirSina Mashayekh Aug 08 '20 at 13:27