Click here to Skip to main content
Email Password   helpLost your password?

screenshot of the demo application

Introduction

This article presents a printer class that supports the LPR print protocol in C#. With this class, it is possible to send a print file in ASCII, PostScript, PCL, et cetera, directly to a network printer or print server that communicates by means of the LPR (LPD) protocol. The printer class also implements the LPQ, the LPRM, and the Restart request.

Background

The LPR/LPD protocol is a 15 year old print protocol from the TCP/IP suite that is still important in the area of network printers and print servers. It is described in detail in RFC 1179. Augmented variations of the protocol exist, like LPRNG also known as LPR Next Generation.

In an application, I needed to send PostScript files to an LPD enabled printer. It was possible to use the command line lpr.exe which is included in Windows but I didn't want to be dependant on lpr.exe, so I searched for a free C# implementation of LPR. As I could not find one, it was time to build such a class myself.

How to use the code

The printer class is straightforward. The constructor is called with three parameters, the hostname, the queue name, and the username. As these parameters are reused again and again with every LPR and LPQ request, I decided to place them in the constructor. Just a choice.

To print a file, one only needs to call LPR with the filename as argument, and to get the content of the spool queue, just call LPQ. The boolean parameter of LPQ indicates a long or small listing. The output format of LPQ depends on the implementation of the LPD daemon in the printer so there might be no difference.

So the core code to print a postscript file could look like:

if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
    LPD.Printer printer1 = 
         new LPD.Printer("saturnus","queue","rob");
    string fname = openFileDialog1.FileName;
    if (fname.EndsWith(".ps"))
    {
        printer1.LPR(fname);
        textBox1.Text = printer1.LPQ(false);
    }
    else
    {
        // display appropiate error message

        // ...

    }
}

Points of interest

To use the printer class, one should have a network printer with an LPD daemon, or start the TCP/IP Print Service on a computer (print server). Then, you can send files to any Windows printer defined on the computer. Use the PC name as hostname and the name of the printer as queue name. Be aware that the name of the printers may not contain spaces as the LPR/LPD protocol uses spaces as separators.

LPR does not wait until the file is printed, it starts a thread in the background for every file. LPQ and LPRM do not use a background thread.

The Restart method seems not to be supported in the Windows LPD daemon as I get no acknowledge. Nevertheless, I kept it in the code (use at own risk :).

Some things to improve the class include: (no deadline)

History

Usage rights

Everybody is granted to use this code as long as you refer to the original work, and I would appreciate that enhancements are published at CodeProject too.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralPerformance tweaks
Rowland Shaw
5:15 14 Dec '09  
I was finding that in a high-performance scenario that there were a couple of bottlenecks (that in reality probably wouldn't be a problem). To work around these, I'd suggest people protect the NetworkStream in SendFile() with a using {...} block.

In the interests of requesting the minimum access on a file, I'd also suggest changing to use a FileStream constructor, and explicitly requesting only read access, for example:
	FileStream fstream = new FileStream(fname, FileMode.Open, FileAccess.Read, FileShare.Read);

GeneralLess naive implementation of GetJobId()
Rowland Shaw
5:06 14 Dec '09  
I've reimplemented GetJobId() to give separate incrementing counts based on the user name, which some people may find useful, should they need to work in a service type environment:

        private static Dictionary<string, int> LastJobId = new Dictionary<string, int>();
/// <summary> /// GetCounter returns the next jobid for the LPR protocol
/// which must be between 0 and 999.
/// The jobid is incremented every call but will be wrapped to 0 when
/// larger than 999.
/// </summary> /// <returns>next number</returns> private int GetJobId()
{
int cnt = 0;

lock (LastJobId)
{
if( LastJobId.ContainsKey(puser) )
{
cnt = LastJobId[puser];
}

cnt++; // next number but
cnt %= 1000; // keep cnt between 0 and 999
if( LastJobId.ContainsKey(puser) )
{
LastJobId[puser] = cnt;
}
else {
LastJobId.Add(puser, cnt);
}
}
return cnt;
}

GeneralLPR failed after 10 jobs [modified]
balu12345
9:46 10 Oct '09  
Hi,


I am great Fan you, you are great ....

many & many thanks to you giving this code...

When I tried to print 100 jobs at a time,Iam getting the following error:

"A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond 192.168.40.50:515 "

here 192.168.40.50 is my print server & port is 515.

Please help me to come out of this problem

PLease..please help me...& guide to solve this problem


Thanks

modified on Sunday, October 11, 2009 9:08 AM

GeneralRe: LPR failed after 10 jobs
rob tillaart
8:02 11 Oct '09  
Hi Balu,

Depending on the operating system you use (especially at the receiving end) there is a different maximum of the number of open sockets. WinXP differs from XP Pro, etc. Google for the details.

1. What OS are you using to send the jobs?
2. What OS (device) is receiving the jobs?
3. Is the error 100% repeatable?
4. Did you try other "batch" sizes e.g. 10 25 50 instead of 100?
5. Does it give the same error?

regards, rob
GeneralRe: LPR failed after 10 jobs
balu12345
8:53 11 Oct '09  
Hi Rob,

Once Again I am very & very thankfull to you for replying towards my question...

Thanks sir....
Please find below answers ..,

1. What OS are you using to send the jobs?
ans) windows XP professional

2. What OS (device) is receiving the jobs?
ans) printserver is windows 2003 server
Printer is Fujixerox MFD(apoes model)

3. Is the error 100% repeatable?
ans)yes it is 100% repeatable

4. Did you try other "batch" sizes e.g. 10 25 50 instead of 100?
ans)yes for everything greater than 10 jobs has the same problem

5. Does it give the same error?
ans) yes

To my knowledge LPR will use 515 port...,so once 10 jobs were sent to LPR,then 515 is not ready to accept the new job...so it failed by giving the above error.

One way to solve this problem is,if there more than 10 jobs then i have to wait until one of them in first ten to be completed and then accomidate next job to lpr..like wise for all of them in batch...

but Iam not able to do this & Iam exactly using your code,no changes were made to it....please help me & throw me out of this problem


Thanks & regards
Balu
GeneralRe: LPR failed after 10 jobs
rob tillaart
7:48 13 Oct '09  
balu12345 wrote:
Hi Rob,

To my knowledge LPR will use 515 port...,so once 10 jobs were sent to LPR,then 515 is not ready to accept the new job...so it failed by giving the above error.

One way to solve this problem is,if there more than 10 jobs then i have to wait until one of them in first ten to be completed and then accomidate next job to lpr..like wise for all of them in batch...

but Iam not able to do this & Iam exactly using your code,no changes were made to it....please help me & throw me out of this problem


Thanks & regards
Balu

Hi Balu,

Indeed LPR uses port 515 by default, the LPD service of win2003 seems not to be able to have more than 10 listening sockets open, so the problem is not caused by the LPR code. To confirm this please try the following:

1. send more than 10 jobs directly to the printer
Does it accept all jobs? or same error?
My expectation: it will accept all jobs.


2. send more than 10 jobs directly to the another (LINUX, no windows) printserver.
Does it accept all jobs? or same error?
My expectation: it will accept all jobs.


I recall there are differences between OS-es in the area of nr of open sockets. The behaviour of the printserver can be (partly) configured in the registry. Please check the following link with plenty information:

http://technet.microsoft.com/en-us/library/cc778201(WS.10).aspx[^]

(note there are many more sites with printserver info)

I don't have the time to investigate which setting to change but carefull reading might help you. Please note that fiddling in the registry is at your own risk and not for the faint of heart. Please backup your registry before changing anything.

Regards,
Rob
GeneralRe: LPR failed after 10 jobs
balu12345
8:36 23 Oct '09  
Hi Rob,

I just recovered from fever so today only i have seen your reply
very & very thanks for your help....

you are right,when i send more than 10 prints drictly..then all of them were printed successfully.

why the same(batch printting) was not happening from code?plz share ur thoughts with me..

can plz help me for the following...

1.How to know/get acknowledgement that after LPR the job is printed successfully(i.e.,all its pages in that particular job is physically printed successfully)?if u have any code snippet please share it to me.

if I get above information..,then i will use thread mechanism to resolve the my actual LPR issue(failed after 10 jobs).

Thanks
GeneralRe: LPR failed after 10 jobs
rob tillaart
22:42 23 Oct '09  
Hi,

"Remote diagnostics is a wonderful game".

My conclusion so far is that there is a restriction in the printserver as told earlier. It is not simple to ask if physical pages are printed, you need a printer that supports e.g. SNMP and the printerMIB. But than again you need to know how many pages the jobs have to make an ideal solution.

An easier although a bit unreliable is to use an LPQ call and see if a particular job is still in the queue at the server. If theserver says that the job is not in the queue you may assume it is sent to the printer. That does not mean it is printed but probably it is busy printing.

The solution above is called "active polling" and is resource intensive (CPU, network, client & server) but it will not generate a large load if you put a sleep() in it.

You can implement this in several ways. Straightforward:
1. Send a job,
2. sleep some time and lpq the server until queue is empty
3. goto 1.

A more intelligent solution uses some system knowledge. Just send jobs and do an lpq to see #jobs in the queue at the server. If this is 10 (make that configurable) sleep for one second (also configurable). When the queuesize is less than 10 start sending jobs again. IN short use the fact that the server can buffer X jobs. The sleep time should be configurable as you only need to check after a page is printed, this minimizes the polling overhead. If the printer is 10PPM you only need to check every 6 seconds in theory. In practice I would check once a second.

Please note the above solutions are not foolproof, especially if there are multiple clients the straightforward solution can create starvation in theory. If this happens you need at least a faster printer Smile

Hopes this helps enough to solve your problem

Regards, Rob
GeneralRe: LPR failed after 10 jobs
balu12345
4:02 4 Nov '09  
Hi Rob,

Iam very much thankfull to you...

I have implemented LPQ as per your suggestion and now iam able to print many number of jobs at a time without fail...

Thanks alot ....

thanks alot...

i need one more help from you... suppose my printer queuename is "Hp sample Queue" and Printer server IP "173.xxx.xx.xx"

LPQ is failed because my Queuename "Hp sample Queue" has white space in between it. If it doesn't have white space(like "HpsampleQueue123")then it returned successfully

Please let me know how to make LPQ suuccessfull even if Printer Queue name("Hp sample Queue") has white spaces in between it? and even how to handle it in c# code?
Please help me sir...

Iam very & very much thankfull for you

Thanks
GeneralRe: LPR failed after 10 jobs
rob tillaart
11:43 4 Nov '09  
Hi,
The problem comes from the RFC1179 () [^]specification (par 5.3) in which a LPQ command is defined as

| 03 | Queue | SP | List | LF |

So in the command a space is a separator and your quenames has spaces in it. So the behavior of the class is according to the spec. But that doesn't solve your problem.

Straightforward solutions:
1. Rename your printers,
2. Provide spaceless nicknames for your printers. [man printcap under linux]

The trick is needed to patch the request of the LPQ call (around line 600) in such a way that the server responds as intended. Some solution thoughts: Note these are out of the spec and may cause other problems.

3. patch the code so that the queue name will be embedded in double / single quotes.
4. patch the code so that in the queue name spaces will be escaped.
Trial/error but I would try at least the backslash \
5. patch the code so that in the queuename spaces are replaced by the wildcards ? or *
6. read your server LPQ specification if it contain other tips.

Let me hear your results

Succes,
Rob
QuestionNot able to print PDF files with demo exe
NEERBADA3
11:16 20 Apr '09  
I am able to print ps file with demo exe but when i am giving the pdf it prints it in garbage lot of numbers and even lot of pages.
AnswerRe: Not able to print PDF files with demo exe
rob tillaart
21:27 20 Apr '09  
Hi Neerbada,

LPR is a transport protocol for sending printjobs from a client to a printer or printserver. It takes care that a file is sent to a printer with the (elementary) metadata needed to handle a printjob as one entity. Most important it separates two printjobs so they do not overlap on paper, a problem that occured when jobs were sent to fast after each other to old lineprinters.

LPR and LPD do not interpret PostScript or PDF data, so if the printer does not support PDF, LPR will sent it but the interpreter will assume it is plain text resulting in lots of text and numbers. If you need an interpreter for PDF you might google for Ghostscript or contact Adobe for their (commercial) PDF interpreter.

I hope this explains the results you see.

Regards,
Rob
GeneralRe: Not able to print PDF files with demo exe
NEERBADA3
1:25 21 Apr '09  
Hi Rob,

Thanks for the quick reply.

I am able ot print the pdf documents on the same printer from the PDF file i.e if I print after opening the pdf file and use the print button given on the PDF file.

It means printer is supporting the pdf files.

But if I use the demo.exe and give the pdf file than it produces the plain text resulting in lots of numbers and text.

Please help.

Thanks,
Neeraj
GeneralRe: Not able to print PDF files with demo exe
rob tillaart
3:32 21 Apr '09  
Hi Neeraj,

I understand your experiment and I will try to explain your observations.

When you open the PDF file with Acrobat Reader this application interprets the PDF code and translates them to GDI calls that are displayed by your video card. When you press the print button, similar GDI calls are sent to the printerdriver that translates them to PostScript or PCL code which is sent to the printer.

To see the difference print to file from Acrobat Reader (checkbox in the driver dialog) and open the resulting file with Wordpad. Assuming you have a PostScript driver the resulting file is Postscript. Also open the original PDF file with Wordpad.

The PDF file starts like: %PDF-1.4
The Postscript starts like: %!PS-Adobe-3.0

The PDF file will look familiar just like the "garbage" printed earlier. Note that 1.4 and 3.0 are version numbers and that they might differ.

So in conclusion when you print from Acrobat Reader to the printer no PDF is sent to the printer but a printer language that depends on the printerdriver chosen.

What you now can do is use the demo application to sent the Postscript file to the printer.

Hopes this explains the difference to you. More detailed information about the printing proces can be found at http://technet.microsoft.com/en-us/library/cc783789.aspx[^]

Regards,
Rob
GeneralRe: Not able to print PDF files with demo exe
NEERBADA3
4:00 21 Apr '09  
Thanks Rob again for the quick reply and excellent explanation.

I use the print to file command and create the PS file from pdf and when I am giving to the printer with Demo exe it is printing the whole contents of ps file in the text format.

Is there is anything missing from my side ?


Given below are the top 30 statements from the PS file:


%-12345X@PJL JOB NAME="FORM.pdf"
@PJL SET STRINGCODESET=UTF8
@PJL SET HOLD=OFF
@PJL SET SEPARATORPAGE=OFF
@PJL SET ECONOMODE=OFF
@PJL SET RESOLUTION=600
@PJL ENTER LANGUAGE=POSTSCRIPT
%!PS-Adobe-3.0
%%Title: FORM.pdf
%%Creator: PScript5.dll Version 5.2.2
%%CreationDate: 4/21/2009 8:44:19
%%BoundingBox: (atend)
%%Pages: (atend)

%%PageOrder: (atend)
%%DocumentNeededResources: (atend)
%%DocumentSuppliedResources: (atend)
%%DocumentData: Clean7Bit
%%TargetDevice: (HP Universal Printing PS) (3010.107) 0
%%LanguageLevel: 2
%%HiResBoundingBox: 12 17.3299 597.9600 775.6302
%%CropBox: 12 17.3299 597.9600 775.6302
%ADO_BeginApplicationHeaderComments
%%Creator: Adobe Acrobat 8.0
%%For: A378737
%%LanguageLevel: 3
%ADO_EndApplicationHeaderComments
%%DocumentProcessColors: (atend)
%%DocumentCustomColors: (atend)
%%EndComments
GeneralRe: Not able to print PDF files with demo exe
NEERBADA3
8:30 21 Apr '09  
Great Rob!!! After removing the first 7 PJL commands I am able to print the pdf from the demo.exe.

I am using the following printer for printing:
HP laserjet 4350dtn

Now the problem is how can we make whole thing automated for printing pdf files through demo.exe.

Currently I am following the steps below to print pdf file through demo.exe:

1) Save the PDF file with print to file option.
2) Remove the first 7 PJL commands
3) Use the file after above changes to print through demo.exe


Thanks,
Neeraj
GeneralRe: Not able to print PDF files with demo exe
rob tillaart
21:18 21 Apr '09  
OK,

Hypothesis 2: The cause is in the first character [001B].

test 1: Could you remove these and give it a try? Easier to remove two bytes than seven lines Smile

Test 2: Windows has a commandline LPR client. Could you try it with the original printfile (including the PJL comments and the escape character)? type "LPR /?" for explanation of parameters. [reference test]

In case you want to dive into the code: The datafile is read and sent into the network socket around line 420-440. There is a change but the older code is available as comment.

regards,
rob
GeneralRe: Not able to print PDF files with demo exe
NEERBADA3
9:01 22 Apr '09  
Hi Rob,

Thanks again for your quick response.

test1 and test 2 failed and it is printing as text. Now the only option left is to dive into the code.

Reviewed the code also for changes but not able to understand where to make the change. Can you help me with the code change for our scenario ?

Thanks,
Neeraj
GeneralRe: Not able to print PDF files with demo exe
NEERBADA3
10:56 22 Apr '09  
Hi Rob,

Thanks again for your reponse. Just replied to the mail. Please confirm you got the mail or not ?

Neeraj
GeneralIs it possible use as a WebService ?
malukinho
17:41 12 Nov '08  
I love this article because it is very close as I'm looking for.
I'd like to know if is it possible to use as a webservice, where an application will get the response of one webpage, and then, will print a page without user commands ?

Thanks in advance,
Best regards
GeneralRe: Is it possible use as a WebService ?
rob tillaart
23:16 12 Nov '08  
Hi Malukinho,

Don't know your requirements in detail but to keep things nicely separated I propose to make one class that generates the file to be printed. When the file is generated it can be offered to the LPR class. Similar to the thread "Printing contents of a textbox to a Network printer using ASP.net" just below.

If you don't have high performance requirements you can store the file as a temporary file and use the LPR class to print it. Note that most of the time the speed of the printer will be the performance bottleneck. This way you can use the LPR client class just as is.
If you need more performance the class should be rewritten so it exposes lower level methods so you can open the printer, fill a buffer, send, ...., and close printer yourself.

Hopes this helps,

regards,
Rob Tillaart
QuestionPrinting contents of a textbox to a Netwrok printer using ASP.net
Member 4088318
5:52 15 May '08  
Hi Rob,
The class was excellent it works great for a windows application. Can you please help me implement it for a web application. The Description for the Project i'm doing is as below:

I have to print a label to a Datamax printer by a ASP.Net web application (C#). The contents to be printed will be given in a Text box.

The Datamax label printer is a Network shared printer whose IP address is known to the web application Developer.

The Web application should use this IP address to communicate to the printer, so that later when the printer is changed we need to change only the IP address in the Web app.

Any one in the network who opens this web application and clicks Print should be able to print a label to that particular printer without any change in printer settings or any installations.

It is a single web application no extra forms can be added.

Kindly Help me in this issue.
Thanks in Advance
AnswerRe: Printing contents of a textbox to a Netwrok printer using ASP.net
rob tillaart
4:40 1 Jun '08  
Hi Member4088318,

Excuses for the long delay for the answer. Many threads ask for high priority Smile

The simplest way to solve the problem is to store the text to print in a temporary file and print this file. This could also be part of the printer class. I have made a first implementation which stores the text in a file with a random name to minimize the chance of collisions. Then this temporary file is printed as usual. Because it is a temporary file I give the flag deleteafterprint the value true. Setting this flag to false may help to debug it.

Just include the code snippet below in the printer class. Change the value of temppath to fit your need & system. Temppath should be a property of the printer class ...

        
public void LPRtext(string text)
{
Random r = new Random();
string temppath = @"C:\";
string fn = temppath + "lpr" + r.Next(999999999).ToString();
StreamWriter sw = new StreamWriter(fn);
sw.Write(text);
sw.Close();
// print and delete afterwards..
LPR(fn, true);
}

Hopes this solves your need.

regards,
rob
QuestionStatus
jmshearer
7:18 30 May '07  
First off, thank you for the code--it's an excellent LPR implementation.

Any idea how to implement a "real" status request (i.e. query the printer for current state, errors, etc.) Perhaps I missed this in RFC 1179.

Thanks.

Michael Shearer
AnswerRe: Status
rob tillaart
2:37 3 Jun '07  

Hi Michael,

In RFC 1179 there is no printer state defined, only the queue state by means of the LPQ (short or long) command. The data and its formatting LPQ returns is not specified. This makes it hard to parse output from different printers or devices behaving like printers e.g. print server (windows/linux/..), fax server etc.

My preferred way to interrogate printers about their state is SNMP (Simple Network Management Protocol) and to use the printer Mib RFC 3805 (replaces 1759). With this one can see if the device is idle or running or in error, also the number of papertrays and the amount of paper in them can be requested. But be aware that this only works for printers that support the printer MIB. Note that some printers support the PM partially.

There are several articles about using SNMP in C# e.g. http://www.codeproject.com/useritems/SNMPDLL.asp[^].

Additional to SNMP one can use the PING command to see if the printer is reachable in the first place.

Succes,

Rob Tillaart


Last Updated 27 Dec 2006 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010