从 Zebra 打印机读取状态

发布于 2024-08-13 22:12:58 字数 653 浏览 8 评论 0原文

我正在开展一个项目,我们需要使用 Zebra 打印机来打印条形码标签。 我们使用 C#,并且在打印方面做得很好,将原始 ZPL 字符串发送到打印机(使用 Winspool.drv)。

然而,我们还需要从打印机读取数据,但运气不佳。

我们需要从打印机获取状态,这是 ZPL 命令“~HS”的输出,这样我们就可以知道内存中有多少标签等待打印。 winspool.drv 中的 EnumJobs() 仅在 Windows 假脱机上有作业,一旦它们被发送到打印机,它们就会从该列表中消失。但这并不意味着标签已被打印,因为打印机具有剥离传感器并且一次仅打印一张标签,并且我们显然有兴趣将批量标签发送到打印机。

我尝试过类似的方法(使用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调用中什么也没得到。我什至不知道这是否是正确的做法。

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

谢谢。

I'm working on a project where we need to use a Zebra Printer for barcode labels.
We're using C#, and we're doing OK on the printing side of things, sending raw ZPL strings to the printer (using winspool.drv).

However, we also need to read from the printer, and no luck there.

We need to get the status from the printer, which is the output to the ZPL command "~HS", so we can tell how many labels are in memory waiting to be printed.
The EnumJobs() from winspool.drv only has jobs on the windows spool, and once they're sent to the printer, they're gone from that list. But that doesn't mean the label has been printed, since the printer has a peel sensor and only prints one label at a time, and we're obviously interested in sending batches of labels to the printer.

I've tried something like (using the winspool.drv calls):

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

But I get nothing on the ReadPrinter call. I don't even know if this is the right way of going at it.

Anyone out there tackled this before?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

厌倦 2024-08-20 22:12:58

我面临着同样的问题。您已经在这个主题上做过任何事情了吗?

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()); (从打印机手册中,命令<7> 应该获取固件版本)

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

I'm facing the same problem. Did you already manage anything on this subject?

Ax Perez Parra Castro, this is how I did it:

-get the RawPrinterHelper class from here http://support.microsoft.com/kb/322091

-my printer (zebra 2030) doesn't support ZPL, so as far as I know the only way is to send unicode to it

-I made a list of characters I need e.g.

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();

(get those int values from en.wikipedia.org/wiki/ASCII)

-compose the command - e.g. sb.Append(esc + enq + Convert.ToChar(7).ToString()); (from the printer manual, the command < ESC>< ENQ><7> should get the firmware version)

-send the command RawPrinterHelper.SendStringToPrinter(printerName, sb.ToString()); (printerName in my case is "Zebra TTP 2030")

暮年 2024-08-20 22:12:58

在这种情况下,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

ReadPrinter will not help in this situation. It will read back the print job you have submitted to the printer, not the printer's response. However, for the sake of completeness: In order to use ReadPrinter, you must open the printer again, using the combined "printer name - job id" syntax:

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

This will only work if the job 12345 has not been removed yet.


As for answering the question, you have to use WriteFile to send data and ReadFile to get the response. To use those functions, you need to open the printer with CreateFile. After you've done that, the rest is absolutely trivial.

The problem here is getting the device path that must be passed to CreateFile in order to open the printer. If your printer is an LPT one, that's as simple as "LPT:", but for a USB printer you have to obtain the device path, which looks like this:

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

I have found a way to obtain this path, but it only works if you have just one printer installed. If you have more, you will need a relation between the device path and the printer name you see in the control panel, and that relation is something I haven't figured yet. I've created a question for that: Figuring which printer name corresponds to which device ID.

素染倾城色 2024-08-20 22:12:58

大约 15 年前,我编写了通过 Zebra 打印机进行打印的软件。

当时我们通过 RS-232(?标准串行通信)与打印机进行通信,效果很好,所有信息都从打印机及时准确地返回。

最近我使用 Epson 理货打印机,发现 Windows 打印机驱动程序笨拙且低效。我降低了一个级别并通过 GDI 直接与打印机通信,一切都令我满意。

我说去掉中间人,如果你降低一个级别并直接与打印机通信,而不是通过Windows打印机驱动程序通信,你会取得更大的成功。

希望这有帮助,

About 15 years ago I wrote software to print throught Zebra printers.

At the time we communicated with the printer over RS-232 (? standard serial comms), which worked well, all information came back from the printer in a timely and accurate fashion.

Recently I'd to work with Epson tally printers, and found the windows printer drivers clumsy and inefficient. I dropped down a level and communicated directly with the printer through GDI, and everything worked to my satisification.

I say take out the middle man, if you drop down a level and communicate with the printer directly, rather than communicating through windows printer drivers, you'll have more success.

Hope this helps,

遇见了你 2024-08-20 22:12:58

如果您有机会使用 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/fingerprint/...-printers)使用 GetPrinterPRINTER_INFO_2
pinvoke.net 上还提供了翻译。

If you have the chance to use kernel32.dll and leaving out the usb-driver-bound winspool.srv you could use this vanilla approach:

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
            };
        }
    }
}

Otherwise there's a solution available (it's VB.NET though) (but I can't tell if this works with ZPL/EPL/fingerprint/...-printers) which uses GetPrinter with PRINTER_INFO_2.
There's also a translation at pinvoke.net available.

顾忌 2024-08-20 22:12:58

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

I have used TCP/IP communication with C++, and I was able to response from the print engine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文