Send ESC commands to a printer in C#
I've blogged about this before after having to do it myself. Not to toot my own horn too loudly, but I recommend that you read that entry, as I talk about a complex image command with the printer. If you can understand that, you can print anything!
(I talk about ESC/POS, used in retail receipt printers, but the semantics of the discussion applies to ESC/P, the older dot-matrix language, as well.)
In short, use a BinaryWriter
instead of a string
or you'll get hopelessly confused (as I did) as certain characters get "baked" in the wrong encoding on their way to the printer. (Trust me on this one! Do not use a StringBuilder
when trying to talk in ESC/POS.)
Be careful not to use the Write(string)
overload of the BinaryWriter
as it will prepend the length of the string as a byte in the stream, which is not what you want. (I also blogged about that after learning that the hard way, too, on the same project.)
Then I use some code to send the byte
array, as obtained from the BinaryWriter
, directly to the printer by P/Invoking:
private static void Print(string printerName, byte[] document)
{
NativeMethods.DOC_INFO_1 documentInfo;
IntPtr printerHandle;
documentInfo = new NativeMethods.DOC_INFO_1();
documentInfo.pDataType = "RAW";
documentInfo.pDocName = "Bit Image Test";
printerHandle = new IntPtr(0);
if (NativeMethods.OpenPrinter(printerName.Normalize(), out printerHandle, IntPtr.Zero))
{
if (NativeMethods.StartDocPrinter(printerHandle, 1, documentInfo))
{
int bytesWritten;
byte[] managedData;
IntPtr unmanagedData;
managedData = document;
unmanagedData = Marshal.AllocCoTaskMem(managedData.Length);
Marshal.Copy(managedData, 0, unmanagedData, managedData.Length);
if (NativeMethods.StartPagePrinter(printerHandle))
{
NativeMethods.WritePrinter(
printerHandle,
unmanagedData,
managedData.Length,
out bytesWritten);
NativeMethods.EndPagePrinter(printerHandle);
}
else
{
throw new Win32Exception();
}
Marshal.FreeCoTaskMem(unmanagedData);
NativeMethods.EndDocPrinter(printerHandle);
}
else
{
throw new Win32Exception();
}
NativeMethods.ClosePrinter(printerHandle);
}
else
{
throw new Win32Exception();
}
}
Good luck!
Bypassing the printer driver and controlling the printer directly, using its native command language, is a supported scenario. The P/Invoke is explained well in this KB article.
Sure you want to do this? It is uncommon, to put it mildly, usually only attempted with low-cost Point-Of-Sale thermal or matrix printers. If you don't like the mechanics of PrintDocument.PrintPage, nobody does, you could consider my code in this thread. Note the FormFeed method.