C# 使用C从USB标签打印机读取数据#

C# 使用C从USB标签打印机读取数据#,c#,printing,usb,C#,Printing,Usb,我有一台USB热敏收据打印机(它有命令文档,几乎所有的ESC/POS都有一些原始字节命令)。我使用著名的WinSpool.drv绑定将原始字节发送到打印机,它可以正常工作。但现在我想从打印机读取状态。这是记录在案的: >Status Query Command: [ESC]!? (0x1B 0x21 0x3F) Response: 1 byte (bitwise status) Bit 0 = Cover Open Bit 1 = Paper

我有一台USB热敏收据打印机(它有命令文档,几乎所有的ESC/POS都有一些原始字节命令)。我使用著名的WinSpool.drv绑定将原始字节发送到打印机,它可以正常工作。但现在我想从打印机读取状态。这是记录在案的:

>Status Query
    Command: [ESC]!? (0x1B 0x21 0x3F)
    Response: 1 byte (bitwise status)
        Bit 0 = Cover Open
        Bit 1 = Paper Stuck
        Bit 2 = No Paper
        ...
我还尝试在WinSpool.drv()中使用
ReadPrinter
。我确信它已经启用了双向驱动程序支持,并且设备确实收到了命令,但我根本无法从
ReadPrinter
接收任何信息

然后我发现并尝试了一种包括查找打印机路径的方法(看起来像“\?\USB\VID#u 6868&PID#u 0500&MI#u 00#6&1a69986f&0&0000{28d78fad-5a12-11d1-ae5b-0000f803a8c2}”,查找起来非常痛苦),然后直接用
CreateFile
与打印机通信。我将在上面的帖子中发布关键代码:

'GUID_DEVINTERFACE_USB_DEVICE ...
        '{A5DCBF10-6530-11D2-901F-00C04FB951ED}
        PRNTPORT = "\\?\usb#vid_0a5f&pid_0088#32j125200336#{a5dcbf10-6530-11d2-901f-00c04fb951ed}"
        hPort = NativeMethods.CreateFile(PRNTPORT, NativeMethods.EFileAccess.GENERIC_READ Or NativeMethods.EFileAccess.GENERIC_WRITE, _
                                         NativeMethods.EFileShare.FILE_SHARE_READ Or NativeMethods.EFileShare.FILE_SHARE_WRITE, _
                                         Nothing, _
                                         NativeMethods.ECreationDisposition.OPEN_ALWAYS, _
                                         NativeMethods.EFileAttributes.FILE_FLAG_SEQUENTIAL_SCAN Or _
                                         NativeMethods.EFileAttributes.FILE_ATTRIBUTE_NORMAL, Nothing)
       
        outFile = New FileStream(hPort, FileAccess.ReadWrite) ' create FileStream using Handle
        outFile2 = New FileStream(hPort, FileAccess.Read)
        Dim sWriter As New StreamWriter(outFile)
        Dim sr As New StreamReader(outFile2)
        sWriter.WriteLine("JR")
        sWriter.WriteLine("N")
        sWriter.WriteLine("UG" & vbCrLf)
        sWriter.Flush()
        Dim strRead(0) As String

        While sr.EndOfStream = False

            strRead(strRead.Length - 1) = sr.ReadLine()
            ReDim Preserve strRead(strRead.Length)
            'Debug.Print(sr.ReadLine())
        End While
        outFile.Close()
        outFile2.Close()
        If strRead.Length > 1 Then
            MessageBox.Show("Done " & vbCr & String.Join(vbCr, strRead))
        End If
正如post所述,您首先将字节写入打印机,然后从打印机读取,直到EOF。因此我提出了代码:

[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern SafeFileHandle CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename, [MarshalAs(UnmanagedType.U4)] FileAccess access, [MarshalAs(UnmanagedType.U4)] FileShare share, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] FileAttributes flagsAndAttributes, IntPtr templateFile);

var ptr = CreateFile(@"\\?\USB#VID_6868&PID_0500&MI_00#6&1a69986f&0&0000#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}", FileAccess.ReadWrite, FileShare.ReadWrite, IntPtr.Zero, FileMode.Open, FileAttributes.Normal, IntPtr.Zero);

Console.WriteLine($"HANDLE={ptr.DangerousGetHandle()}");
FileStream stream = new FileStream(ptr, FileAccess.ReadWrite);

/* Write */
byte[] data = { 0x1B, 0x21, 0x3F };
stream.Write(data, 0, data.Length);

/* Read */
List<byte> read = new List<byte>();
byte[] buf = new byte[256];
int readBytes = 0;
while(true)
{
    readBytes = stream.Read(buf, 0, buf.Length);
    if(readBytes <= 0)
        break;
    read.AddRange(buf.Take(readBytes));
}
Console.WriteLine(read[0]);
现在有两个问题

首先,每次我运行上面的代码时,第一次尝试的输出都是交替的:运行一次,它成功地返回了正确的字符串;运行两次,它什么也不返回(我假设它超过了250ms超时);第三次跑,成功;跑第四次,没什么

其次,即使使用async,挂起读取仍然存在。当它挂起时,我按pause debugging,VisualStudio说代码停在
Task readTask=stream.ReadAsync(buf,0,buf.Length)行。据我所知,阅读过程甚至不是从这一行开始的

所以直到现在我完全迷路了。有人能指出问题所在吗?我相信我可以使用像libWinUSB32这样的东西在低级别上和USB通信,但我真的想把它作为最后的手段保存起来,并在没有库的情况下实现它

public static void PrinterWrite(this Stream stream, byte[] data)
{
    stream.Write(data, 0, data.Length);
}

public static byte[] PrinterRead(this Stream stream, int timeOut = 250)
{
    List<byte> read = new List<byte>();
    byte[] buf = new byte[256];
    int readBytes = 0;
    while (true)
    {
        Task<int> readTask = stream.ReadAsync(buf, 0, buf.Length);
        if (Task.WaitAny(new Task[] { readTask }, timeOut) != -1 && (readBytes = readTask.Result) > 0)
            read.AddRange(buf.Take(readBytes));
        else
            break;
    }
    return read.ToArray();
}

/* In Main() */
for (int i = 0; i < 10; ++i)
{
    // Here I used another command which is to query the model string of the printer, it works the same as the query state command.
    stream.PrinterWrite(Encoding.ASCII.GetBytes("~!T\r\n"));
    byte[] ret = stream.PrinterRead();
    Console.WriteLine(Encoding.ASCII.GetString(ret));
    Console.WriteLine("===");
}