I\'m experimenting with a Zebra TTP8200 thermal printer. For my application I need to print plotter type traces continuously until the user hits a stop button. I\'ve had a p
If the USB printer is available as a COM port, you can just write to the COM port. Like this, from the DOS prompt:
dir > com1
The former example will output the results of the dir
command to the printer.
Or, here is another example:
copy file.txt com1
The former example will output the contents of file.txt
to the printer.
Outputting properly formatted ZPL data will be harder than just plain text. I have gotten this to work from Linux using Ruby (and Epson/ESC commands), however.
Is there a way to bypass the spooler and output data straight to this USB printer?
Yes, absolutely. It's built into most OSs, printing raw via USB is just a bit less obvious than Ethernet and COM/LPT. Note, many applications, such as notepad are incapable of printing raw, so your application needs to support this as well.
Now with your raw printing application, use the newly created printer.
P.S. These instructions are also available here, with screenshots, as part of a Java open source raw printing tutorial. The project provides tutorials for other platforms (Ubuntu, OS X) as well.
http://qzindustries.com/TutorialRawWin
-Tres
The below class in C# is something I've adapted from a Microsoft Knowledgebase article. There are methods in this class to send the print job as a string
and byte[]
. Please note there are some references to log4net in there that can be removed/replaced with the logging framework of your choice. :
/// <summary>
/// Class used to aid in sending raw printer data (PS, PRN, etc) directly to the printer.
/// This class was taken from http://support.microsoft.com/kb/322091
/// </summary>
public class PrintQueueUtility
{
private static ILog log = LogManager.GetLogger(typeof(PrintQueueUtility));
[DllImport("winspool.Drv", EntryPoint = "OpenPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
[DllImport("winspool.Drv", EntryPoint = "ClosePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool ClosePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartDocPrinterA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartDocPrinter(IntPtr hPrinter, Int32 level, [In, MarshalAs(UnmanagedType.LPStruct)] DOCINFOA di);
[DllImport("winspool.Drv", EntryPoint = "EndDocPrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndDocPrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "StartPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool StartPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "EndPagePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool EndPagePrinter(IntPtr hPrinter);
[DllImport("winspool.Drv", EntryPoint = "WritePrinter", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool WritePrinter(IntPtr hPrinter, IntPtr pBytes, Int32 dwCount, out Int32 dwWritten);
/// <summary>Method which sends a <see langword="byte"/> array to a printer queue with a specific document name.</summary>
/// <param name="bytes">Byte array to send to the printer.</param>
/// <param name="printerName">Name of the printer to send the <paramref name="bytes"/> to.</param>
/// <param name="documentName">The document Name.</param>
/// <returns><see cref="bool"/> indicating whether or not the method succeeded at adding something to the print queue.</returns>
public static bool SendBytesToPrinter(byte[] bytes, string printerName, string documentName)
{
bool success;
// Allocate some unmanaged memory for those bytes into an unmanaged pointer.
IntPtr unmanagedBytes = Marshal.AllocCoTaskMem(bytes.Length);
// Copy the managed byte array into the unmanaged array.
Marshal.Copy(bytes, 0, unmanagedBytes, bytes.Length);
// Send the unmanaged bytes to the printer.
success = SendUnmanagedBytesToPrinter(unmanagedBytes, printerName, documentName, bytes.Length);
// Free the unmanaged memory that you allocated earlier.
Marshal.FreeCoTaskMem(unmanagedBytes);
return success;
}
/// <summary>Method which sends a string to the printer queue with a specific document name.</summary>
/// <param name="data"><see cref="String"/> data to send to the printer.</param>
/// <param name="printerName">Name of the printer to send the data to.</param>
/// <param name="documentName">Name of the document in the printer queue.</param>
/// <returns><see cref="bool"/> indicating whether or not the method succeeded at adding something to the print queue.</returns>
public static bool SendStringToPrinter(string data, string printerName, string documentName)
{
bool success;
IntPtr unmanagedBytes;
// How many characters are in the string?
var characterCount = data.Length;
// Assume that the printer is expecting ANSI text, and then convert
// the string to ANSI text.
unmanagedBytes = Marshal.StringToCoTaskMemAnsi(data);
// Send the converted ANSI string to the printer.
success = SendUnmanagedBytesToPrinter(unmanagedBytes, printerName, documentName, characterCount);
Marshal.FreeCoTaskMem(unmanagedBytes);
return success;
}
private static bool SendUnmanagedBytesToPrinter(IntPtr unmanagedBytes, string printerName, string documentName, int count)
{
int error;
int written;
IntPtr printer;
var di = new DOCINFOA();
var success = false;
di.pDocName = documentName;
di.pDataType = "RAW";
// Open the printer.
if (OpenPrinter(printerName.Normalize(), out printer, IntPtr.Zero))
{
// Start a document.
if (StartDocPrinter(printer, 1, di))
{
// Start a page.
if (StartPagePrinter(printer))
{
// Write the bytes.
success = WritePrinter(printer, unmanagedBytes, count, out written);
EndPagePrinter(printer);
}
EndDocPrinter(printer);
}
ClosePrinter(printer);
}
// If you did not succeed, GetLastError may give more information
// about why not.
if (!success)
{
error = Marshal.GetLastWin32Error();
log.ErrorFormat("Sending bytes to printer {0} failed. Last Win32 error = {1}", printerName, error);
}
return success;
}
// Structure and API declarations:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class DOCINFOA
{
[MarshalAs(UnmanagedType.LPStr)]
public string pDocName;
[MarshalAs(UnmanagedType.LPStr)]
public string pOutputFile;
[MarshalAs(UnmanagedType.LPStr)]
public string pDataType;
}
Thanks for the comments.
After some more digging around, I found this interesting article on using Windows printer functions provided by usbprint.sys. With a bit of hacking the sample code there seemed to work. I think I'll take this route.
There is the final code given in the article:
/* 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);
}
Thanks again for the suggestions.