Search code examples
c#usbprinters

How to Await kernel32 ReadFile() or Set a Timeout in C#?


The code is generally functional, but there are occasional issues where it fails to capture the printer's response. Consequently, when attempting to send additional bytes using the 'writefile' method to the printer, it receives the previous response, and the new data remains in the printer's queue. My idea is to first read all the pending printer responses and then proceed to send the next set of bytes, ensuring that I receive the expected response. This is particularly important when multiple commands are being sent to the printer concurrently, and I want to ensure I receive the specific response I'm interested in.

public Dictionary<string, string> ReadMany(List<string> settings)
{
    try
    {
        var Data = CreateReadManySettingXml(settings);

        // Check if the handle was successfully created
        if (Handle.IsInvalid)
        {
            throw new Exception("Failed to open handle to the device.");
        }

        // Write data to the device using the handle
        uint bytesWritten = 0;
        bool success = WriteFile(Handle, Data, (uint)Data.Length, out bytesWritten, IntPtr.Zero);

        // Check if the write was successful
        if (!success || bytesWritten != Data.Length)
        {
            throw new Exception("Failed to write to the device.");
        }

        // Read the response from the USB device
        byte[] readBuffer = new byte[10000];
        uint bytesRead = 0;

        success = ReadFile(Handle, readBuffer, 10000, out bytesRead, IntPtr.Zero);

        if (!success)
        {
            throw new Exception("Failed to read data from USB device.");
        }

        // Resize the buffer to the actual number of bytes read
        Array.Resize(ref readBuffer, (int)bytesRead);

        return ReadManyValues(Encoding.UTF8.GetString(readBuffer));

    }
    catch (Exception e)
    {
        throw new Exception(e.Message);
    }
}

I considered using 'await' with the 'readfile' method to handle this, but it seems that this approach leads to the code becoming stuck at the 'readfile' method and not progressing further.


Solution

  • You should just use a normal FileStream to read USB, as noted in Figuring which printer name corresponds to which device ID

    // Write data to the device using the handle
    uint bytesWritten = 0;
    using var stream = new FileStream(Handle, FileAccess.ReadWrite, 0, true);
    await stream.WriteAsync(Data, 0, Data.Length, CancellationToken.None);
    // write will throw automatically
    
    // Read the response from the USB device
    byte[] readBuffer = new byte[10000];
    uint bytesRead = 0;
    
    var bytesRead = stream.ReadAsync(readBuffer, 0, 10000, CancellationToken.None);
    // Do not resize buffer, just pass the correct offsets
    var str = Encoding.UTF8.GetString(readBuffer, 0, bytesRead);
    return ReadManyValues(str);