【问题标题】:Cannot get the correct printer status无法获得正确的打印机状态
【发布时间】:2021-03-09 11:04:13
【问题描述】:

我正在使用 Delphi 10.3 创建一个打印带有条形码的标签的程序。我不希望打印机将多个作业排队,而是我想在前一个作业完成后开始打印下一个作业并从打印机接收就绪状态。打印机状态始终为就绪,即使处于离线状态!

我使用这个函数来获取打印机的状态:

function TPrintQRForm.GetCurrentPrinterHandle() : THandle;
var
  Device, Driver, Port : Array[0..255] of Char;
  hDeviceMode : THandle;

begin
  Printer.GetPrinter(Device, Driver, Port, hDeviceMode);

  if not Winspool.OpenPrinter(@Device, Result, nil) then
  begin
    RaiseLastOSError();
  end;
end;

function TPrintQRForm.GetCurrentPrinterInformation() : TPrinterInfo;
var
  hPrinter : THandle;
  pInfo : PPrinterInfo2;
  bytesNeeded : DWORD;

begin
  hPrinter := GetCurrentPrinterHandle();
  try
    Winspool.GetPrinter(hPrinter, 2, Nil, 0, @bytesNeeded);
    pInfo := AllocMem(bytesNeeded);

    try
      Winspool.GetPrinter(hPrinter, 2, pInfo, bytesNeeded, @bytesNeeded);
      Result.ServerName := pInfo^.pServerName;
      Result.PrinterName := pInfo^.pPrinterName;
      Result.ShareName := pInfo^.pShareName;
      Result.PortName := pInfo^.pPortName;
      Result.DriverName := pInfo^.pDriverName;
      Result.Comment := pInfo^.pComment;
      Result.Location := pInfo^.pLocation;
      Result.DeviceMode := pInfo^.pDevMode;
      Result.SepFile := pInfo^.pSepFile;
      Result.PrintProcessor := pInfo^.pPrintProcessor;
      Result.DataType := pInfo^.pDatatype;
      Result.Parameters := pInfo^.pParameters;
      Result.SecurityDescriptor := pInfo^.pSecurityDescriptor;
      Result.Attributes := pInfo^.Attributes;
      Result.DefaultPriority := pInfo^.DefaultPriority;
      Result.StartTime := pInfo^.StartTime;
      Result.UntilTime := pInfo^.UntilTime;
      Result.Status := pInfo^.Status;
      Result.Jobs := pInfo^.cJobs;
      Result.AveragePPM := pInfo^.AveragePPM;
    finally
      FreeMem(pInfo);
    end;
  finally
    ClosePrinter(hPrinter);
  end;
end;

然后在某个事件中,我使用此代码检查打印机状态:

  PrinterInfo := GetCurrentPrinterInformation();

  while PrinterInfo.Status <> 0 do
  begin
    if PrinterInfo.Status = PRINTER_STATUS_PRINTING then
    begin
      // Get information about the selected printer
      PrinterInfo := GetCurrentPrinterInformation();
      
      // Process messages from the OS and do not freeze the UI
      Application.ProcessMessages;

      Continue;
    end;

    Case PrinterInfo.Status of
      PRINTER_STATUS_PAUSED             : ShowMessage('The printer is paused.');
      PRINTER_STATUS_ERROR              : ShowMessage('The printer is in an error state.');
      PRINTER_STATUS_PENDING_DELETION   : ShowMessage('The printer is being deleted.');
      PRINTER_STATUS_PAPER_JAM          : ShowMessage('Paper is jammed in the printer.');
      PRINTER_STATUS_PAPER_OUT          : ShowMessage('The printer is out of paper.');
      PRINTER_STATUS_MANUAL_FEED        : ShowMessage('The printer is in a manual feed state.');
      PRINTER_STATUS_PAPER_PROBLEM      : ShowMessage('The printer has a paper problem.');
      PRINTER_STATUS_OFFLINE            : ShowMessage('The printer is offline.');
      PRINTER_STATUS_IO_ACTIVE          : ShowMessage('The printer is in an active input/output state.');
      PRINTER_STATUS_BUSY               : ShowMessage('The printer is busy.');
      PRINTER_STATUS_OUTPUT_BIN_FULL    : ShowMessage('The printer''s output bin is full.');
      PRINTER_STATUS_NOT_AVAILABLE      : ShowMessage('The printer is not available for printing.');
      PRINTER_STATUS_WAITING            : ShowMessage('The printer is waiting.');
      PRINTER_STATUS_PROCESSING         : ShowMessage('The printer is processing a print job.');
      PRINTER_STATUS_INITIALIZING       : ShowMessage('The printer is initializing.');
      PRINTER_STATUS_WARMING_UP         : ShowMessage('The printer is warming up.');
      PRINTER_STATUS_TONER_LOW          : ShowMessage('The printer is low on toner.');
      PRINTER_STATUS_NO_TONER           : ShowMessage('The printer is out of toner.');
      PRINTER_STATUS_PAGE_PUNT          : ShowMessage('The printer cannot print the current page.');
      PRINTER_STATUS_USER_INTERVENTION  : ShowMessage('The printer has an error that requires the user to do something.');
      PRINTER_STATUS_OUT_OF_MEMORY      : ShowMessage('The printer has run out of memory.');
      PRINTER_STATUS_DOOR_OPEN          : ShowMessage('The printer door is open.');
      PRINTER_STATUS_POWER_SAVE         : ShowMessage('The printer is in power save mode.');
     else
       ShowMessage('The error  is unknown');
     end;

     Exit;
  end;

在这段代码之后,我开始打印过程,所有我需要打印的副本都在循环中。

我使用的是 TSC-TPP 2410M 打印机,尽管它对作业进行排队,但它从不提供打印状态。所以代码永远不会进入 while,因为我总是得到 0 打印机状态。

有什么方法可以禁用队列或让应用程序等待打印机停止打印然后执行下一个打印作业?有什么想法吗?

编辑:在@BlurrySterk 提出一些建议后,我发现当队列中的作业数达到 10 时,打印机会发出 PRINTER_STATUS_PRINTING。打印完所有作业后,它会再次准备好。

【问题讨论】:

  • 我没有打印机的状态总是可用的。看看docs.microsoft.com/en-us/troubleshoot/windows/win32/…
  • 感谢您的评论。我已经阅读了这篇文章,它说:“确定物理打印机的状态必须满足一个基本前提:假脱机程序必须尝试将打印作业发送到物理打印机。这是只有当端口监视器报告打印机的状态时。”。但我无法理解如何获取物理打印机状态... spooler 何时尝试打印?
  • 打印作业到达打印队列后,后台打印程序将尝试打印。显然,指定打印作业必须立即打印还是仅在最后一页之后打印的 Windows 设置会影响这一点。但我怀疑作业通过队列的速度太快以至于状态没有改变。
  • 我没有看到任何指定要获取信息的打印机的代码。我可以假设您正在测试的这台打印机实际上是 Windows 默认打印机吗?
  • Windows 管理打印机。用户决定他们是否使用打印后台处理程序。这不取决于你。为用户改变这一点是不友好的行为。您的应用程序需要管理权限才能在用户计算机上进行此配置更改。您不应更改用户的后台打印程序设置。如果这是针对您可以控制系统的嵌入式应用程序,则禁用打印后台处理程序似乎是您想要做的。

标签: windows delphi printing


【解决方案1】:

如 cmets 中所述,WinAPI 似乎存在限制。

因为之前的打印机现在已经报废了,所以我们买了一台 TSC ME240。这个link里面有TSPL/2命令的编程手册。

为了解决这个问题,我将打印机连接到网络并通过 tcp 套接字发送 TSPL/2 命令。

我需要这个代码来获取状态

  // Create TCP Client
  TCPClient := TIdTCPClient.Create();

  // Connect to server
  TCPClient.Host := PrinterIP;
  TCPClient.Port := PrinterPort;
  TCPClient.Connect();

  // Set status string
  StatusString := #27 + '!?';

  // Send command
  TCPClient.Socket.WriteLn(StatusString);

  // Check status
  PrinterStatus := TCPClient.Socket.ReadByte;

打印机正在响应并且作业已完成。感谢所有努力的男孩/女孩。

【讨论】:

  • 不错的解决方案。我有同样的问题。但我正在尝试使用 c# 进行连接。请问#27在Delphi中代表什么,因为我可以看到您没有将消息转换为字节数组。如果您可以提供任何建议,这是我的问题。 stackoverflow.com/questions/68260077/…
  • 表示ASCII表中的字符。在帖子中有一个指向 TSPL/2 命令手册的链接。在第 79 页的“状态轮询和立即命令”部分,它说如果您发送 !?序列到打印机它响应它的状态。
猜你喜欢
  • 2020-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多