You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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();

问题分析与修正方案

核心问题点

  1. 错误的句柄打开参数:OpenPrinter的第一个参数应为打印机名称,而非打印机名, Job 作业ID格式的作业标识,作业名称无法用于打开可读取的打印机句柄。
  2. 权限不足:读取打印机响应需要PRINTER_READ_ACCESS或更高权限(如已有的PRINTER_ALL_ACCESS),PRINTER_USE_ACCESS权限不足以读取数据。
  3. 未完成打印作业:原写入代码缺少EndPagePrinter和EndDocPrinter调用,打印机可能未处理完命令就被尝试读取,导致无响应。
  4. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 23:48:20