如何使用Flutter Dart的Win32包读取Boca打印机状态
打印机与应用双向通信读取问题排查
问题背景
需要实现打印机与应用的双向通信,包含写入、读取两个功能。目前已通过Winspool API的WritePrinter实现向打印机写入数据,但调用ReadPrinter读取打印机状态时出现异常:函数既未执行,也未抛出错误。
现有代码
打开打印机
final alloc = Arena(); final pPrinterName = printerName.toNativeUtf16(allocator: alloc); final pWritePrinterHandle = alloc<HANDLE>(); final pDefault = alloc<PRINTER_DEFAULTS>() ..ref.pDatatype = nullptr ..ref.pDevMode = nullptr ..ref.DesiredAccess = PRINTER_ALL_ACCESS; // Open Printer var fSuccess = OpenPrinter(pPrinterName, pWritePrinterHandle, pDefault); if (fSuccess == 0) { final error = GetLastError(); throw Exception('OpenPrint error, status: $fSuccess, error: $error'); }
向打印机发送命令
final pDocInfo = alloc<DOC_INFO_1>() ..ref.pDocName = 'Boca Systems Communicator'.toNativeUtf16(allocator: alloc) ..ref.pDatatype = 'RAW'.toNativeUtf16(allocator: alloc) // RAW, TEXT or XPS_PASS ..ref.pOutputFile = nullptr; // 启动打印作业 var fStartDocJobId = StartDocPrinter(pWritePrinterHandle.value, 1, pDocInfo); if (fStartDocJobId == 0) { // 原代码错误:误用了打开打印机的fSuccess变量,应判断作业ID是否为0 final error = GetLastError(); throw Exception( 'StartDocPrinter error, status: $fStartDocJobId, error: $error'); } var fStartPrintSuccess = StartPagePrinter(pWritePrinterHandle.value); // 发送打印命令 String dataToPrint = '<S92>'; final cWritten = alloc<DWORD>(); final data = dataToPrint.toNativeUtf8(allocator: alloc); final writeSuccess = WritePrinter( pWritePrinterHandle.value, data, dataToPrint.length, cWritten); if (dataToPrint.length != cWritten.value) { final error = GetLastError(); throw Exception( 'WritePrinter error, status: $writeSuccess, error: $error'); }
读取打印机的代码(存在问题)
final printerJobName = '$printerName, Job $fStartDocJobId'; final Completer<void> completer = Completer<void>(); final pReadPrinterHandle = alloc<HANDLE>(); final pReadDefault = alloc<PRINTER_DEFAULTS>() ..ref.pDatatype = nullptr ..ref.pDevMode = nullptr ..ref.DesiredAccess = PRINTER_USE_ACCESS; final openPrinterDetailName = printerJobName.toNativeUtf16(allocator: alloc); // 尝试打开作业句柄读取 var fReadOpenSuccess = OpenPrinter(openPrinterDetailName, pReadPrinterHandle, pReadDefault); if (fReadOpenSuccess == 0) { final error = GetLastError(); throw Exception( 'Read OpenPrint error, status: $fReadOpenSuccess, error: $error, openPrinterDetailName $printerJobName'); } final readBufferSize = 1024; final pReadData = alloc<Uint8>(readBufferSize); final bytesRead = alloc<Uint32>(); final readSuccess = ReadPrinter( pReadPrinterHandle.value, pReadData, readBufferSize, bytesRead); if (readSuccess == 0) { final error = GetLastError(); throw Exception('ReadPrinter error, status: $readSuccess, error: $error'); } // 处理读取数据 final readData = pReadData.cast<Utf8>().toDartString();
问题分析与修正方案
核心问题点
- 错误的句柄打开参数:
OpenPrinter的第一个参数应为打印机名称,而非打印机名, Job 作业ID格式的作业标识,作业名称无法用于打开可读取的打印机句柄。 - 权限不足:读取打印机响应需要
PRINTER_READ_ACCESS或更高权限(如已有的PRINTER_ALL_ACCESS),PRINTER_USE_ACCESS权限不足以读取数据。 - 未完成打印作业:原写入代码缺少
EndPagePrinter和EndDocPrinter调用,打印机可能未处理完命令就被尝试读取,导致无响应。 - ReadPrinter使用限制:该函数仅适用于支持双向通信的打印机(如部分POS热敏打印机),且需确保打印机收到命令后会返回响应数据。
修正后的读取代码
复用已打开的打印机句柄(无需重新打开),确保作业完成后再读取:
// 必须先结束打印作业,让打印机处理命令并生成响应 EndPagePrinter(pWritePrinterHandle.value); EndDocPrinter(pWritePrinterHandle.value); final readBufferSize = 1024; final pReadData = alloc<Uint8>(readBufferSize); final bytesRead = alloc<Uint32>(); // 使用已有的高权限打印机句柄读取 final readSuccess = ReadPrinter( pWritePrinterHandle.value, pReadData, readBufferSize, bytesRead); if (readSuccess == 0) { final error = GetLastError(); // ERROR_NO_DATA表示打印机无响应数据返回,属于正常情况 if (error == ERROR_NO_DATA) { print('打印机无返回数据'); } else { throw Exception('ReadPrinter error, status: $readSuccess, error: $error'); } } else { // 处理读取到的数据 final readData = pReadData.cast<Utf8>().toDartString(); print('读取到打印机数据:$readData'); }
额外注意事项
- 确认打印机硬件支持双向通信,且驱动已启用双向支持。
- 需确保发送的命令(如示例中的
<S92>)是打印机支持的查询类命令,能触发响应。 - 若读取仍无响应,可尝试增加读取前的延迟,或通过
GetPrinter函数先确认打印机处于就绪状态。
内容的提问来源于stack exchange,提问作者Savin Sharma
相关产品推荐
相关产品推荐

