从斑马打印机读取状态

13

我正在一个项目中使用斑马打印机来打印条形码标签。我们使用 C#,在发送原始的 ZPL 字符串到打印机上(使用 winspool.drv)的打印方面已经做得不错。

但是,我们还需要从打印机读取信息,这方面一直没有进展。

我们需要获取打印机的状态,即 ZPL 命令“~HS”的输出,以便我们可以知道有多少标签在内存中等待打印。Winspool.drv 中的 EnumJobs() 只列出了在 Windows spool 上的作业,一旦它们被发送到打印机上,它们就从列表中消失了。但这并不意味着标签已经被打印了,因为打印机有一个剥离传感器,并且一次只打印一个标签,而我们显然有兴趣向打印机发送批量标签。

我已经尝试了类似以下的代码(使用 winspool.drv 调用):

OpenPrinter(szPrinterName, out hPrinter, IntPtr.Zero);
WritePrinter(hPrinter, pBytes, dwCount, out dwWritten); // send the string "~HS"
ReadPrinter(hPrinter, data, buff, out pcRead);

但是在ReadPrinter调用中我什么也没有得到。我甚至不知道这是否是正确的方法。

有人之前解决过这个问题吗?

谢谢。

5个回答

3

我也遇到了同样的问题。你已经在这个问题上做出了任何进展吗?

Ax Perez Parra Castro,我是这样做的:

-从这里获取RawPrinterHelper类http://support.microsoft.com/kb/322091

-我的打印机(zebra 2030)不支持ZPL,所以据我所知唯一的方法是向其发送Unicode

-我列出了我需要的字符列表,例如:

string enq = Convert.ToChar(5).ToString();
string esc = Convert.ToChar(27).ToString();
string nul = Convert.ToChar(0).ToString();
string rs = Convert.ToChar(30).ToString();
string lf = Convert.ToChar(10).ToString();
string cr = Convert.ToChar(13).ToString();

从en.wikipedia.org/wiki/ASCII获取这些int值

-组合命令 - 例如sb.Append(esc + enq + Convert.ToChar(7).ToString());(来自打印机手册,《ESC> <7>》命令应该可以获取固件版本)

-发送命令RawPrinterHelper.SendStringToPrinter(printerName, sb.ToString());(在我这里,打印机名称是“Zebra TTP 2030”)


@AndreasNiedermair,那是几年前的事情了,所以我记不清细节了。请看看这个实验性项目是否有帮助:https://www.dropbox.com/s/2h6gj0o08eksbxu/PrintLabel.zip?dl=0 - bfi

1

ReadPrinter 在这种情况下无法帮助。它将读取您提交给打印机的打印作业,而不是打印机的响应。但是,为了完整起见:要使用 ReadPrinter,您必须再次打开打印机,使用组合的“打印机名称 - 作业 ID”语法:

OpenPrinter("Zebra,Job 12345", ...);
ReadPrinter(hPrinter, ...);

只有在作业12345尚未被删除的情况下,此操作才能成功。


回答这个问题,您需要使用WriteFile发送数据和ReadFile获取响应。要使用这些函数,您需要使用CreateFile打开打印机。完成后,其余部分绝对是微不足道的。

问题在于获取必须传递给CreateFile以打开打印机的设备路径。如果您的打印机是LPT型号,则只需使用"LPT:"即可,但对于USB打印机,您需要获取设备路径,该路径如下:

\\?\usb#vid_0a5f&pid_0027#46a072900549#{28d78fad-5a12-11d1-ae5b-0000f803a8c2}

我已经找到了一种获取此路径的方法,但仅适用于您只安装了一个打印机的情况。如果您有多个打印机,则需要在设备路径和控制面板中看到的打印机名称之间建立关系,而这种关系是我尚未解决的问题。我已经为此创建了一个问题:确定哪个打印机名称对应哪个设备ID


WriteFileReadFile在哪里?我在winspool.drv中找不到它们(请参见http://msdn.microsoft.com/en-us/library/windows/desktop/dd162861(v=vs.85).aspx) - user57508
你确定你不能使用.ReadPrinter读取数据吗?http://msdn.microsoft.com/en-us/library/windows/desktop/dd162895(v=vs.85).aspx说:“ReadPrinter函数从指定的打印机中检索数据。” - user57508
1
@AndreasNiedermair 是的,它可以从打印机中检索数据,但这些数据是您自己放入打印机中的数据(即您的打印作业),而不是打印机可能生成的响应打印作业的数据。WriteFileReadFile是通用的Windows函数,可用于许多不同的对象,包括打印机,它们位于kernel32.dll中。 - GSerg
感谢您的输入。所以您基本上是在说,使用winspool.drv没有机会读取响应,但应该回退到基本的kernel32.dll(即没有任何驱动程序的普通USB)? - user57508
据我所知,是这样的(您也可以查看我的关联问题)。虽然需要一个驱动程序,但其中涉及到一个驱动程序,因为要使用它,您需要正确安装打印机,这需要一个驱动程序。 - GSerg

0
大约15年前,我编写了一款用于通过Zebra打印机打印的软件。
当时我们使用RS-232(标准串行通信)与打印机通信,效果很好,打印机返回的所有信息都及时而准确。
最近,我需要使用Epson Tally打印机,但发现Windows打印机驱动程序笨拙而低效。因此,我降低了通信级别,直接通过GDI与打印机通信,这样一切都得到了满足。
我认为应该去掉中间人,如果你降低通信级别,直接与打印机通信,而不是通过Windows打印机驱动程序通信,你将更加成功。
希望这有所帮助,

0
如果你有机会使用 kernel32.dll,并且不需要使用与 USB 驱动程序绑定的 winspool.srv,那么你可以使用这种基本方法:
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Microsoft.Win32.SafeHandles;

{
    public class USB
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern Int32 CancelIo(SafeFileHandle hFile);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern IntPtr CreateEvent(IntPtr SecurityAttributes,
                                                  Boolean bManualReset,
                                                  Boolean bInitialState,
                                                  String lpName);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern Boolean GetOverlappedResult(SafeFileHandle hFile,
                                                           IntPtr lpOverlapped,
                                                           ref Int32 lpNumberOfBytesTransferred,
                                                           Boolean bWait);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        internal static extern Boolean ReadFile(SafeFileHandle hFile,
                                                IntPtr lpBuffer,
                                                Int32 nNumberOfBytesToRead,
                                                ref Int32 lpNumberOfBytesRead,
                                                IntPtr lpOverlapped);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern Int32 WaitForSingleObject(IntPtr hHandle,
                                                         Int32 dwMilliseconds);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern SafeFileHandle CreateFile(String lpFileName,
                                                         UInt32 dwDesiredAccess,
                                                         Int32 dwShareMode,
                                                         IntPtr lpSecurityAttributes,
                                                         Int32 dwCreationDisposition,
                                                         Int32 dwFlagsAndAttributes,
                                                         Int32 hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        internal static extern Boolean WriteFile(SafeFileHandle hFile,
                                                 ref byte lpBuffer,
                                                 Int32 nNumberOfBytesToWrite,
                                                 ref Int32 lpNumberOfBytesWritten,
                                                 IntPtr lpOverlapped);

        [DllImport("kernel32.dll", SetLastError = true)]
        internal static extern int GetLastError();

        private const Int32 FILE_FLAG_OVERLAPPED = 0X40000000;
        private const Int32 FILE_SHARE_READ = 1;
        private const Int32 FILE_SHARE_WRITE = 2;
        private const UInt32 GENERIC_READ = 0X80000000;
        private const UInt32 GENERIC_WRITE = 0X40000000;
        private const Int32 OPEN_EXISTING = 3;
        private const Int32 WAIT_OBJECT_0 = 0;
        private const Int32 WAIT_TIMEOUT = 0x102;
        private const Int32 ReadBufferSize = 200;

        private readonly string _devicePathName;

        public USB(string devicePathName)
        {
            this._devicePathName = devicePathName;
        }

        public void Send(string data)
        {
            var bData = this.Encoding.GetBytes(data);
            this.Send(bData);
        }

        public void Send(byte[] data)
        {
            try
            {
                var eventObject = CreateEvent(IntPtr.Zero,
                                              false,
                                              false,
                                              String.Empty);
                var hidOverlapped = GetHidOverlapped(eventObject);

                var unManagedBuffer = Marshal.AllocHGlobal(data.Length);
                var unManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(hidOverlapped));
                Marshal.StructureToPtr(hidOverlapped,
                                       unManagedOverlapped,
                                       false);

                using (var writeHandle = this.GetWriteFileHandle())
                {
                    var numberOfBytesWritten = 0;
                    var success = WriteFile(writeHandle,
                                            ref data[0],
                                            data.Length,
                                            ref numberOfBytesWritten,
                                            unManagedOverlapped);
                    if (!success)
                    {
                        var result = WaitForSingleObject(eventObject,
                                                         100);
                        switch (result)
                        {
                            case WAIT_OBJECT_0:
                                success = true;
                                break;
                            case WAIT_TIMEOUT:
                                CancelIo(writeHandle);
                                break;
                        }
                    }
                }

                Marshal.FreeHGlobal(unManagedOverlapped);
                Marshal.FreeHGlobal(unManagedBuffer);
            }
            catch (Exception ex)
            {
                // TODO add logging and enhance the try/catch-closure to a smaller one
            }
        }

        private Encoding Encoding
        {
            get
            {
                return Encoding.ASCII;
            }
        }

        public string Read()
        {
            var receivedBytes = 0;
            var receiveBuffer = new byte[ReadBufferSize];

            string data;

            try
            {
                var eventObject = CreateEvent(IntPtr.Zero,
                                              false,
                                              false,
                                              String.Empty);
                var hidOverlapped = GetHidOverlapped(eventObject);

                var unManagedBuffer = Marshal.AllocHGlobal(ReadBufferSize);
                var unManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(hidOverlapped));

                Marshal.StructureToPtr(hidOverlapped,
                                       unManagedOverlapped,
                                       false);

                using (var readHandle = CreateFile(this._devicePathName,
                                                   GENERIC_READ,
                                                   FILE_SHARE_READ /* | FILE_SHARE_WRITE*/,
                                                   IntPtr.Zero,
                                                   OPEN_EXISTING,
                                                   FILE_FLAG_OVERLAPPED,
                                                   0))
                {
                    var success = ReadFile(readHandle,
                                           unManagedBuffer,
                                           receiveBuffer.Length,
                                           ref receivedBytes,
                                           unManagedOverlapped);
                    if (!success)
                    {
                        var result1 = WaitForSingleObject(eventObject,
                                                          300);
                        switch (result1)
                        {
                            case WAIT_OBJECT_0:
                                GetOverlappedResult(readHandle,
                                                    unManagedOverlapped,
                                                    ref receivedBytes,
                                                    false);
                                break;
                            case WAIT_TIMEOUT:
                            default:
                                //CancelIo(_readHandle);
                                break;
                        }
                    }
                }

                if (receivedBytes > 0)
                {
                    Array.Resize(ref receiveBuffer,
                                 receivedBytes);
                    Marshal.Copy(unManagedBuffer,
                                 receiveBuffer,
                                 0,
                                 receivedBytes);
                    data = this.Encoding.GetString(receiveBuffer);
                }
                else
                {
                    data = null;
                }

                Marshal.FreeHGlobal(unManagedOverlapped);
                Marshal.FreeHGlobal(unManagedBuffer);
            }
            catch (Exception ex)
            {
                // TODO add logging and enhance the try/catch-closure to a smaller one
                data = null;
            }

            return data;
        }

        private SafeFileHandle GetWriteFileHandle()
        {
            var writeHandle = CreateFile(this._devicePathName,
                                         GENERIC_WRITE | GENERIC_READ,
                                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                                         IntPtr.Zero,
                                         OPEN_EXISTING,
                                         0,
                                         0);

            return writeHandle;
        }

        private static NativeOverlapped GetHidOverlapped(IntPtr eventObject)
        {
            return new NativeOverlapped
            {
                OffsetLow = 0,
                OffsetHigh = 0,
                EventHandle = eventObject
            };
        }
    }
}

否则有一个可用的解决方案(它是VB.NET)(但我不能确定它是否适用于ZPL/EPL/指纹/...打印机),它使用PRINTER_INFO_2的{{link2:GetPrinter}}。
还有一个在pinvoke.net上可用的翻译。


哼!这是微软典型的解决方案不再可用了。 - AaA

-1

我已经使用了C++的TCP/IP通信,并且能够从打印引擎获得响应。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接