Необработанная печать напрямую на 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. Обратите внимание, что многие приложения, такие как блокнот, не способны печатать в сыром виде, поэтому ваше приложение также должно это поддерживать.
- Установите соответствующий драйвер для вашего USB-принтера. Проверьте свойства принтера, чтобы увидеть, какой порт USB он использует. Это может быть USB001 и т. Д.
- Используя устройства и принтеры, добавьте второй принтер. Локальный порт. Выберите только что созданный порт (например, USB001). Примечание. В некоторых версиях Windows установлен флажок для автоматического определения, если он есть, снимите этот флажок.
- Производитель: Общий, Принтер: Общий / Только текст
- Используйте драйвер, который установлен в данный момент
- Дайте принтеру имя, которое отличает его от уже созданного, то есть Zebra TTP8200 Raw.
- Не разделяют
- Не печатать тестовую страницу, Готово
Теперь, используя приложение для печати, используйте только что созданный принтер.
PS Эти инструкции также доступны здесь, со скриншотами, как часть учебника по печати с открытым исходным кодом Java. Проект предоставляет учебные пособия и для других платформ (Ubuntu, OS X).
http://qzindustries.com/TutorialRawWin
-Tres
Вы должны попробовать метод, изложенный в ссылке ниже. Это позволяет отправлять необработанные данные на принтер.