Чтение статуса с Zebra Printer

Я работаю над проектом, в котором нам нужно использовать принтер Zebra для этикеток со штрих-кодом. Мы используем C#, и с печатью у нас все в порядке, отправляя необработанные строки ZPL на принтер (используя winspool.drv).

Однако нам также нужно читать с принтера, и здесь не повезло.

Нам нужно получить статус от принтера, который является выводом команды ZPL «~HS», чтобы мы могли сказать, сколько этикеток находится в памяти, ожидающих печати. EnumJobs() из winspool.drv имеет задания только в очереди 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. Я даже не знаю, правильный ли это путь.

Кто-нибудь там занимался этим раньше?

Спасибо.


person pmoreira    schedule 04.12.2009    source источник


Ответы (5)


Я столкнулся с той же проблемой. Вам уже что-то удалось на эту тему?

Топор Перес Парра Кастро, вот как я это сделал:

- получить класс RawPrinterHelper отсюда http://support.microsoft.com/kb/322091

-мой принтер (zebra 2030) не поддерживает ZPL, поэтому, насколько я знаю, единственный способ - отправить на него юникод

-Я составил список персонажей, которые мне нужны, например.

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

(получите эти значения int из en.wikipedia.org/wiki/ASCII)

-составить команду - например. sb.Append(esc + enq + Convert.ToChar(7).ToString()); (из инструкции к принтеру команда ‹ ESC>‹ ENQ>‹7> должна получить версию прошивки)

-отправить команду RawPrinterHelper.SendStringToPrinter(printerName, sb.ToString()); (printerName в моем случае "Zebra TTP 2030")

person bfi    schedule 01.06.2010
comment
@AndreasNiedermair это было несколько лет назад, поэтому я не помню подробностей. Посмотрите, поможет ли этот экспериментальный проект dropbox.com/s/2h6gj0o08eksbxu/PrintLabel .zip?dl=0 - person bfi; 02.10.2014

ReadPrinter в этой ситуации не поможет. Он будет считывать задание печати, которое вы отправили на принтер, а не ответ принтера. Однако для полноты картины: чтобы использовать ReadPrinter, вы должны открыть принтер снова, используя комбинированный синтаксис "имя принтера - идентификатор задания":

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}

Я нашел способ получить этот путь , но это работает, только если у вас установлен только один принтер. Если у вас их больше, вам понадобится связь между путем к устройству и именем принтера, которое вы видите на панели управления, и эту связь я еще не понял. Я создал для этого вопрос: Определение того, какое имя принтера соответствует на какой идентификатор устройства.

person GSerg    schedule 18.05.2012
comment
где находятся WriteFile и ReadFile - я не смог найти их в winspool.drv (см. msdn.microsoft.com/en-us/library/windows/desktop/) - person ; 01.10.2014
comment
вы уверены, что не можете прочитать данные с помощью .ReadPrinter?? msdn.microsoft.com/en -us/library/windows/desktop/ говорит: Функция ReadPrinter извлекает данные с указанного принтера. - person ; 01.10.2014
comment
@AndreasNiedermair Да, он извлекает данные с принтера, но это данные, которые вы сами помещаете в этот принтер (ваше задание на печать), а не данные, которые принтер может генерировать в ответ на задание на печать. WriteFile и ReadFile — это функции Windows общего назначения, которые работают со многими различными объектами, включая принтеры, они находятся в kernel32.dll. - person GSerg; 01.10.2014
comment
Спасибо за ваш вклад. так что вы в основном говорите, что с winspool.drv нет возможности прочитать ответ, но следует вернуться к основному kernel32.dll (который представляет собой ванильный USB без каких-либо Водитель)? - person ; 01.10.2014
comment
@AndreasNiedermair Насколько я знаю, это так (вы также можете проверить мой связанный вопрос). Однако здесь задействован драйвер, потому что для его использования вам необходимо правильно установить принтер, для чего требуется драйвер. - person GSerg; 01.10.2014

Около 15 лет назад я написал программное обеспечение для печати на принтерах Zebra.

В то время мы связывались с принтером через RS-232 (стандартный последовательный порт), который работал хорошо, вся информация возвращалась с принтера своевременно и точно.

Недавно мне приходилось работать с принтерами Epson tally, и я обнаружил, что драйверы принтеров для Windows неуклюжи и неэффективны. Я опустился на уровень ниже и связался напрямую с принтером через GDI, и все работало, как мне хотелось.

Я говорю, уберите посредника, если вы спуститесь на уровень ниже и будете общаться с принтером напрямую, а не через драйверы принтера Windows, вы добьетесь большего успеха.

Надеюсь это поможет,

person Binary Worrier    schedule 04.12.2009

Если у вас есть возможность использовать 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), который использует GetPrinter с PRINTER_INFO_2.
Также есть перевод на pinvoke.net доступен.

person Community    schedule 01.10.2014
comment
Хм! как решение, которое больше не доступно. типичный майкрософт! - person AaA; 09.03.2018

Я использовал связь TCP/IP с C++, и мне удалось получить ответ от механизма печати.

person Vivek    schedule 10.01.2018