I have a PCL file that I generated using "print to file".
What's the best way to programmatically print this file in C#?
(Given of course that the printer I am printing to supports PCL.)
I know that I can print by invoking from a prompt:
copy filename.pcl //location/printername
So I would imagine that I could also do the same thing programmatically (using copy)... I wonder if there is a cleaner way to do this, say using PrintDocument.
Note that when I use PrintDocument:
var pd = new PrintDocument
{
DocumentName = @"filename.pcl";
PrinterSettings = {PrinterName = @"\\location\printername"}
};
pd.Print();
I always get a blank page printed.
This article applies to Visual Basic, but it is easy enough to follow to be able to apply it to C#, I think. Otherwise, I'll gladly assist you with any problematic parts.
If the printer is on the network, here's a little funny example of how to talk directly to it. Not sure if it also works if you simply send the bytes of the PCL to the printer though.
I'm sorry to have come so late to this question, but I have some code that will do the job. It was not written by me originally. I received the code from another programmer's help site, but I can't remember which one. It works beautifully. Here is the project, which creates a dll. To do it yourself, select Create:/Project/Class Library (select Visual C# under project types).
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.IO;
namespace PrintRaw
{
public class RawFilePrint
{
// Structure and API declarions:
[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;
}
[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);
// SendBytesToPrinter()
// When the function is given a printer name and an unmanaged array
// of bytes, the function sends those bytes to the print queue.
// Returns true on success, false on failure.
public static bool SendBytesToPrinter(string szPrinterName, IntPtr pBytes, Int32 dwCount)
{
Int32 dwError = 0, dwWritten = 0;
IntPtr hPrinter = new IntPtr(0);
DOCINFOA di = new DOCINFOA();
bool bSuccess = false; // Assume failure unless you specifically succeed.
di.pDocName = "RAW Document";
di.pDataType = "RAW";
if (OpenPrinter(szPrinterName.Normalize(), out hPrinter, IntPtr.Zero))
{
if (StartDocPrinter(hPrinter, 1, di))
{
if (StartPagePrinter(hPrinter))
{
bSuccess = WritePrinter(hPrinter, pBytes, dwCount, out dwWritten);
EndPagePrinter(hPrinter);
}
EndDocPrinter(hPrinter);
}
ClosePrinter(hPrinter);
}
if (!bSuccess)
{
dwError = Marshal.GetLastWin32Error();
}
return bSuccess;
}
public static bool SendFileToPrinter(string szPrinterName, string szFileName)
{
FileStream fs = new FileStream(szFileName, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
Byte[] bytes = new Byte[fs.Length];
bool bSuccess = false;
IntPtr pUnmanagedBytes = new IntPtr(0);
int nLength;
nLength = Convert.ToInt32(fs.Length);
bytes = br.ReadBytes(nLength);
pUnmanagedBytes = Marshal.AllocCoTaskMem(nLength);
Marshal.Copy(bytes, 0, pUnmanagedBytes, nLength);
bSuccess = SendBytesToPrinter(szPrinterName, pUnmanagedBytes, nLength);
Marshal.FreeCoTaskMem(pUnmanagedBytes);
return bSuccess;
}
public static bool SendStringToPrinter(string szPrinterName, string szString)
{
IntPtr pBytes;
Int32 dwCount;
dwCount = szString.Length;
// Assume that the printer is expecting ANSI text, and then convert
// the string to ANSI text.
pBytes = Marshal.StringToCoTaskMemAnsi(szString);
SendBytesToPrinter(szPrinterName, pBytes, dwCount);
Marshal.FreeCoTaskMem(pBytes);
return true;
}
}
}
Now, to use this code, add the resulting dll as a reference to your project, then call the functions as you need them. Here is some code where I used it, just today:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace PclFontTest
{
class Program
{
static void Main(string[] args)
{
string szPrinterName = @"\\printserver\LaserJet 2420";
StreamReader sr = new StreamReader(@"C:\Fonts\US20HP.FNT");
string line = (char)27 + "*c32545D";
line += sr.ReadToEnd();
line += (char)27 + "*c5F";
PrintRaw.RawFilePrint.SendStringToPrinter(szPrinterName, line);
}
}
}
This program reads a PCL Font from a file. It wraps the font with code to assign a font id of 32545 to it, then calls the dll function SendStringToPrinter.
We use the following methods per DLL Import:
[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)] RawPrinter 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);