Requirement
Recently I got a requirement for printing using a Windows Service. The printing should be silent without any pop-up. Also I should be able to change the printer name and settings on the fly. I searched the internet and found some examples and finally two of them worked for me, so I am posting the working ones here.
There are two things that should be noted before we start:
- The User account should be used to execute a Windows Service. There are other accounts like Local System and Local service. I tried Local System but it doesn't work because the System Account doesn't obey our command to select a default printer. For system default printer is another then for a user (xps in my case).
- To allow the service to interact with the Desktop, if you are using a local system account, if your team wants you to use the Local System Account then in Windows XP, right-click on the service (after opening services.msc), open properties and choose this option "Allow to interact with Desktop". For the user account this option is not required.
First method
The print document class provided by .NET is capable of printing inside a Windows Service. The benefit of using the print document class is that it gives some control of printing and settings. It exposes an event which can be consumed to change printing settings.
Here's the code for this:
Namespace WindowsFormsApplication1
{
public static class myPrinters
{
[DllImport("winspool.drv", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetDefaultPrinter(string Name);
}
public class PrintDocumentMethod
{
private Font printFont;
private Stream IOStream;
private void pd_PrintPage(object sender, PrintPageEventArgs ev)
{
ev.Graphics.DrawImage(Image.FromStream(IOStream,true,false),ev.Graphics.VisibleClipBounds);
ev.HasMorePages = false;
}
public void ChangeDefaultPrinter(String pname)
{
myPrinters.SetDefaultPrinter(pname);
}
public void Printing(string pname)
{
try
{
IOStream = new FileStream("C:\\scanned.jpeg", FileMode.Open, FileAccess.Read);
//streamToPrint = new StreamReader(IOStream);
try
{
printFont = new Font("Arial", 10);
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(pd_PrintPage);
// Specify the printer to use.
pd.PrinterSettings.PrinterName = pname;
pd.Print();
}
finally
{
IOStream.Close();
}
}
catch (Exception ex)
{
EventLog e = new EventLog("Print Error");
e.WriteEntry("Failed in Printing, Reason:" + ex.Message);
}
}
}
}
Second Method
While searching I came across a second method for printing. This method uses creation of a process using System.Diagnostics.Process and start this process for printing. This code is quite small and can be used where the page settings are not required to be done, as this doesn't provide much control of printer settings.
class ProcessSpawnMethod
{
public void Print()
{
//PrintDialog printDialog1 = new PrintDialog();
//printDialog1.PrinterSettings.PrinterName = "EasyCoder 91 DT (203 dpi)";
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.Refresh();
//process.StartInfo.Arguments = "EasyCoder 91 DT (203 dpi)";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.Verb = "print";
process.StartInfo.FileName = @"C:\\MyFile.txt";
process.StartInfo.UseShellExecute = true;
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.Start();
}
}
Windows Service
And here is the code for Windows Service, to test the above methods.
protectedoverride void OnStart(string[] args)
{
//PrintDocumentMethod way----
WindowsFormsApplication1.PrintDocumentMethod printDoc =new WindowsFormsApplication1.PrintDocumentMethod();
//printDoc.ChangeDefaultPrinter("SATO CG212"); // to change default printer.
printDoc.Printing("SATO CG212");
//ProcessSpawnMethod---
//ProcessSpawnMethod pspawn = new ProcessSpawnMethod();
//pspawn.Print();
}
I have written the above code, just to test the printing when the Windows Service starts. I am thinking to use a timer here which will execute the code again and again. For now this code will run only once.