Необработанная печать напрямую на USB-принтер, минуя спулер Windows

Я экспериментирую с термопринтером Zebra TTP8200. Для моего приложения мне нужно непрерывно печатать трассировки типа плоттера, пока пользователь не нажмет кнопку остановки. У меня была игра с языком ZPL, и я могу успешно генерировать растровые данные и выгружать из моего растрового изображения строку (или несколько строк) за один раз, выводя ZPL в виде необработанных данных.

Я использую некоторый демонстрационный код Microsoft для вывода необработанных данных на принтер, и это прекрасно работает, за исключением одной проблемы: спулер. Оказывается, что каждый раз, когда я выводлю некоторые данные с использованием кода MS rawprn.exe, они на самом деле помещаются в очередь для печати и затем передаются на принтер. Это займет до 10 секунд, чтобы пройти через спулер, очевидно, слишком медленно. Отключение буферизации в драйвере не помогает, это просто означает, что программа зависает, когда задание проходит через спулер и печать завершается.

Есть ли способ обойти спулер и выводить данные прямо на этот USB-принтер? Мои исследования до сих пор не обнаружили ничего похожего на Windows API. В идеале я хотел бы иметь возможность использовать принтер, как если бы он был последовательным принтером - откройте порт и загрузите данные.

Заранее большое спасибо за любые подсказки!

4 ответа

Решение

Спасибо за комментарии.

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

В статье приведен окончательный код:

/* Code to find the device path for a usbprint.sys controlled
* usb printer and print to it
*/

#include <usb.h>
#include <usbiodef.h>
#include <usbioctl.h>
#include <usbprint.h>
#include <setupapi.h>
#include <devguid.h>
#include <wdmguid.h>

/* This define is required so that the GUID_DEVINTERFACE_USBPRINT variable is
 * declared an initialised as a static locally, since windows does not include it
 * in any of its libraries
 */

#define SS_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
static const GUID name \
= { l, w1, w2, { b1, b2,  b3,  b4,  b5,  b6,  b7,  b8 } }

SS_DEFINE_GUID(GUID_DEVINTERFACE_USBPRINT, 0x28d78fad, 0x5a12, 0x11D1, 0xae,
               0x5b, 0x00, 0x00, 0xf8, 0x03, 0xa8, 0xc2);

void SomeFunctionToWriteToUSB()
{
  HDEVINFO devs;
  DWORD devcount;
  SP_DEVINFO_DATA devinfo;
  SP_DEVICE_INTERFACE_DATA devinterface;
  DWORD size;
  GUID intfce;
  PSP_DEVICE_INTERFACE_DETAIL_DATA interface_detail;

  intfce = GUID_DEVINTERFACE_USBPRINT;
  devs = SetupDiGetClassDevs(&intfce, 0, 0, DIGCF_PRESENT |
                             DIGCF_DEVICEINTERFACE);
  if (devs == INVALID_HANDLE_VALUE) {
    return;
  }
  devcount = 0;
  devinterface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
  while (SetupDiEnumDeviceInterfaces(devs, 0, &intfce, devcount, &devinterface)) {
    /* The following buffers would normally be malloced to he correct size
     * but here we just declare them as large stack variables
     * to make the code more readable
     */
    char driverkey[2048];
    char interfacename[2048];
    char location[2048];
    char description[2048];

    /* If this is not the device we want, we would normally continue onto the
     * next one or so something like
     * if (!required_device) continue; would be added here
     */
    devcount++;
    size = 0;
    /* See how large a buffer we require for the device interface details */
    SetupDiGetDeviceInterfaceDetail(devs, &devinterface, 0, 0, &size, 0);
    devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
    interface_detail = calloc(1, size);
    if (interface_detail) {
      interface_detail->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
      devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
      if (!SetupDiGetDeviceInterfaceDetail(devs, &devinterface, interface_detail,
                                           size, 0, &devinfo)) {
    free(interface_detail);
    SetupDiDestroyDeviceInfoList(devs);
    return;
      }
      /* Make a copy of the device path for later use */
      strcpy(interfacename, interface_detail->DevicePath);
      free(interface_detail);
      /* And now fetch some useful registry entries */
      size = sizeof(driverkey);
      driverkey[0] = 0;
      if (!SetupDiGetDeviceRegistryProperty(devs, &devinfo, SPDRP_DRIVER, &dataType,
                                            (LPBYTE)driverkey, size, 0)) {
    SetupDiDestroyDeviceInfoList(devs);
    return;
      }
      size = sizeof(location);
      location[0] = 0;
      if (!SetupDiGetDeviceRegistryProperty(devs, &devinfo,
                                            SPDRP_LOCATION_INFORMATION, &dataType,
                                            (LPBYTE)location, size, 0)) {
    SetupDiDestroyDeviceInfoList(devs);
    return;
      }
      usbHandle = CreateFile(interfacename, GENERIC_WRITE, FILE_SHARE_READ,
                 NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL |
                 FILE_FLAG_SEQUENTIAL_SCAN, NULL);
      if (usbHandle != INVALID_HANDLE_VALUE) {
    /* Now perform all the writing to the device ie.
     * while (some condition) WriteFile(usbHandle, buf, size, &bytes_written);
     */
    CloseHandle(usbHandle);
      }
    }
  }
  SetupDiDestroyDeviceInfoList(devs);
}

Еще раз спасибо за предложения.

Если USB-принтер доступен в качестве COM-порта, вы можете просто записать в COM-порт. Вот так из командной строки DOS:

dir > com1

В первом примере будут выведены результаты dir Команда на принтер.

Или вот еще один пример:

copy file.txt com1

В первом примере будет выведено содержимое file.txt к принтеру.

Вывести правильно отформатированные данные ZPL будет сложнее, чем просто текст. Однако я получил это для работы с Linux, используя Ruby (и команды Epson/ESC).

Есть ли способ обойти спулер и выводить данные прямо на этот USB-принтер?

Да, конечно. Он встроен в большинство операционных систем, поэтому печать через USB не так очевидна, как по Ethernet и COM/LPT. Обратите внимание, что многие приложения, такие как блокнот, не способны печатать в сыром виде, поэтому ваше приложение также должно это поддерживать.

  1. Установите соответствующий драйвер для вашего USB-принтера. Проверьте свойства принтера, чтобы увидеть, какой порт USB он использует. Это может быть USB001 и т. Д.
  2. Используя устройства и принтеры, добавьте второй принтер. Локальный порт. Выберите только что созданный порт (например, USB001). Примечание. В некоторых версиях Windows установлен флажок для автоматического определения, если он есть, снимите этот флажок.
  3. Производитель: Общий, Принтер: Общий / Только текст
  4. Используйте драйвер, который установлен в данный момент
  5. Дайте принтеру имя, которое отличает его от уже созданного, то есть Zebra TTP8200 Raw.
  6. Не разделяют
  7. Не печатать тестовую страницу, Готово

Теперь, используя приложение для печати, используйте только что созданный принтер.

PS Эти инструкции также доступны здесь, со скриншотами, как часть учебника по печати с открытым исходным кодом Java. Проект предоставляет учебные пособия и для других платформ (Ubuntu, OS X).

http://qzindustries.com/TutorialRawWin

-Tres

Вы должны попробовать метод, изложенный в ссылке ниже. Это позволяет отправлять необработанные данные на принтер.

http://support.microsoft.com/kb/322091/en-us

Другие вопросы по тегам