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