Home   |  Articles   |  Resources   |  Humor   |  Feedback       

  Login   Register 

Ads Via DevMavens


Fun With C# and HP Laserjets

Posted by on Sunday, December 28, 2003

Using sockets and C#, you can customize the display of a networked HP Printer.
I wasn’t sure if I should put this article under “Code” or “Humor”, since it contains both. Ultimately it is much funnier than technical, but full source is included for you to use in your own environment.

I was studying the C# language one day and thought back to earlier in my career. Back then I was learning the assembly language for a little 8 bit Hitachi CPU (the 6303) in order to control a small thermal printer. With the right control codes you could get the printer to display a custom message on the LCD. Then I was walking by the HP Laser printer in the office and wondered if I could do the same here. Once I uncovered the Printer Job Language Reference from HP, I realized this could be fun. After all, who would not get a kick out of a printer with the message “TOUCH ME” on the LCD?

So I wrote some C# and had everything working. I put a list of messages together for the program to display at random and setup a scheduled task to execute the assembly every hour. It didn’t take long for people to notice and start commenting. Some people would check the printer for a new message on every trip. I remained completely silent as to my knowledge of the origin of these messages.

At one point I disabled my scheduled task because our chief network admin had made it his mission to stop the printer hacking. After locking down everything on the printer he possibly could, the messages still got through, but I figured the joke had run it’s course. Then, when everyone realized we were going out of business, I turned it back on for a little bit of amusement everyday.

After the company was sold and I did some consulting for the buyer, I setup the program in their office too. I had it hit an HP printer nearest the engineering cubicles, where at least one guy I know slept behind his high cubicle walls every afternoon for an hour. It never raised many eyebrows that I know of while I was there, but afterwards someone told me they knew one lady who stopped at the printer every day “because it says such nice things to me”. If I can brighten one person's day, every day, my mission is accomplished.

The following listing is the code to pull it off. I think the selection of random messages is quite funny to see on a printer, but feel free to modify these to match your own sense of humor. To use the program just add the IP address of the printer and a message in quotes, or type "random" for the message to select from the random message list.


namespace hphack
{
  using System;
  using System.Text;
  using System.Net;
  using System.Net.Sockets;

  public class PrnHack
  {
    public static int Main(string[] args)
    {
      if(!ParseArgs(args))
      {
        return -1;
      }
            
      Console.WriteLine("\nHP Display Hack");
      Console.WriteLine("Host: {0}", args[0]);
      Console.WriteLine("Message: {0}\n", message);
            
      IPEndPoint ipEndPoint;
      ipEndPoint = new IPEndPoint( Dns.Resolve(args[0]).AddressList[0], PJL_PORT);

      Console.WriteLine("Host is {0}", ipEndPoint.ToString());

      Socket socket;
      socket = new Socket(
                        AddressFamily.InterNetwork,  
                        SocketType.Stream, 
                        ProtocolType.Tcp
                     );

      socket.Connect(ipEndPoint);

      byte [] sendData;
      string sendString;

      sendString = String.Format(
                "\x1B%-12345X@PJL RDYMSG DISPLAY = \"{0}\"\r\n\x1B%-12345X\r\n", 
                message
           );

      sendData = Encoding.ASCII.GetBytes(sendString);
                
      int result;
      result = socket.Send(sendData, sendData.Length, 0);

      if(result == 0)
      {
        Console.WriteLine("Could not send on socket");
      }
        
      socket.Close();
        
      Console.WriteLine("Finished\n\n");
      return 0;
    }

 

    protected static bool ParseArgs(string[] args)
    {
      if(args.Length != 2)
      {
        Console.WriteLine(
                  "HP Display Hack: " + 
                  "hphack printername \"message\" "
            );
        return false;
      }

      if(args[1].Length > 16)
      {
        Console.WriteLine("Message must be <= 16 characters");
        return false;
      }
        
      if(args[1].CompareTo("random") == 0)
      {
        message = GetRandomMessage();
      }
      else
      {
        message = args[1];
      }

      return true;
    }


    public static string GetRandomMessage()
    {
      string [] Messages = { 
                             "BUZZ OFF", 
                             "TOUCH ME",
                             "STEP AWAY",
                             "SET TO STUN",
                             "SCORE = 3413",
                             "PAT EATS MICE",
                             "FEED ME",
                             "GO AWAY",
                             "NEED MORE SPACE",
                             "POUR ME A DRINK",
                             "IN DISTRESS",
                             "NICE SHIRT",
                             "GO AWAY",
                             "NO PRINT FOR YOU",
                             "RADIATION LEAK",
                             "HANDS UP",
                             "PRESS MY BUTTON",
                             "TAKE ME HOME",
                             "LOOKS LIKE RAIN",
                             "HELLO WORLD",
                             "NICE HAIR",
                             "NEED A MINT?",
                             "BE GENTLE",
                             "BE KIND",
                             "INSERT DISK",
                             "BUY ME LUNCH",
                             "DONT STOP",
                             "COME CLOSER",
                             "TAKE A BREAK",
                             "INSERT QUARTER",
                             "BLACK SABBATH"
      };


      Random r = new Random();
      return Messages[r.Next() % Messages.Length];
    }

    protected const int PJL_PORT = 9100;
    protected static string message = "NO MESSAGE";
        
  }
}



Comments:

Printer prints instead of displaying on LCD
By mortenharket on 3/10/2004
I am a VB.NET developer and was browsing some stuff on ASP.NET here and happened to read this article. We have a HP Color LaserJet 4600dn. I tried this code out and ran it as suggested. However, on this printer, it prints out the random messages on paper instead of displaying them on LCD. This is what it prints out:
@PJLRDYMSG = "INSERT QUARTER".

I wish I could do the display thing. Am I missing anything?

Thanks.

Morten
(ssc8e@umkc.edu)

Got it running
By mortenharket on 3/10/2004
Nevermind that. I got it running finally :-)

There's a small typo in the code. It should say:
...
"\x1B%-12345X@PJL RDYMSG DISPLAY = \"{0}\"\r\n\x1B%-12345X\r\n",
...

That is, PJL RDYMSG DISPLAY and not PJLRDYMSG DISPLAY. The latter makes it print the message.

This is quite fun :-), though most of my colleagues already know about it.

Oops!
By scott on 3/10/2004
Thanks for catching the typo (and debugging). I updated the code in the article.

slightly stupid question
By bb on 7/6/2004
how do you determine the 'hostname' for the printer when on a windows network?

re: hostname
By scott on 7/7/2004
The printer needs to be a TCP/IP printer. You should be able to find networked printers with windows explorer.

Works on one, not on another
By mattsmith321 on 7/23/2004
It works on a secondary printer near me, but not on my primary target printer. It is throwing the following exception:

Unhandled Exception: System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 size, SocketFlags socketFlags)
at hphack.PrnHack.Main(String[] args) in d:\tmhp\hphack\hphack.cs:line 46

Any thoughts on what would cause this?

Thanks,
Matt

PJL Problem
By wonie on 7/24/2004
Hi,

I am developing a Program that reads the Display messages from all HP Printers in the Network. I am having problems getting the right Display Message.

Can you give some assissatance ?

Thanks
wniefert@niefert.com

re: Works on one...
By scott on 7/24/2004
Matt, Wonie:

I'm no expert on the printer job language, so I can't offer too much insight for either of you. It could be that the commands have changed over the years, though I would think the codes remain backward compatible..

Having fun
By mattsmith321 on 7/26/2004
Actually hitting the secondary printer has been more fun since it is the main one that our boss uses. She immediately noticed it and of course pointed it out to everyone. Of course, I am a "person of interest" at the moment so we will see how long I can get away with it.

F#cking Hilarious!
By Andy on 8/9/2004
That's awesome! I just tested it on the printer behind me and it works like a charm!

Neet-o-keen
By Timberwolf0122 on 8/24/2004
I'm going to knock out aq scrolly-text edition to get round the infurating 16 char limmit. Also does anyone else know say Xerox/Epson printer codes?

monitoring a network printer
By Yohan229 on 9/13/2004
Has anyone been successful in reading data back from the printer through the socket? Stuff like printer status, PJL ECHO commands, etc...?

I could not run the program
By shoree on 9/27/2004
I made a new project in C# - console based application.

But when I run the program the args array as length = 0, so the program does not execute at all.

Where am I going wrong!

SS

Practical jokes
By ForToday on 9/29/2004
My coworkers and I where talking about practical jokes in the office. I told them that the best practical jokes are the ones you can enjoy a good laugh without too much embarassment and that will not disrupt the work day too much. In other words you should not loose work over the joke! They asked me for an example... and a about a month later I found this article. So Much Fun! I set up a schedule and just left it to run at diffrent hours of the day and just wait to see what happened. I didn't know that the day before the printer was repaired. Poor tech. was blamed for all the fun (I had already told my boss the real culprit so it didn't get out of hand)... finally my boss let it slip by accident that I was playing with their minds! Fun! Thanks for the perfect example of a good practical joke!

Code to get status
By raydog153 on 3/10/2005
In case anyone was interested, here is the code to get the status. New send string:
sendString = String.Format("\x1B%-12345X@PJL INFO STATUS\r\n\x1B%-12345X\r\n", message);

Then after sending read the data:
byte [] receiveData = new Byte[64];
string receiveString;
result = socket.Receive(receiveData);
if(result == 0)
{
Console.WriteLine("Could not receieve on socket");
}
else
{
receiveString = Encoding.ASCII.GetString(receiveData);
Console.WriteLine("Display: {0}", receiveString);
}

Output will be:
Display: @PJL INFO STATUS
CODE=10001
DISPLAY="RADIATION LEAK"
ONLINE=T
Finished

Can someone please help a newbie?
By anananomous on 3/27/2005
I don't know much about C# but I have a networked laserjet and I've compiled this code and made a .exe file. I believe that I am inserting the IP address of my printer in the wrong places in the code. Can someone please tell me where exactly I have to change the code, and how to compile it properly.

re: help
By scott on 3/27/2005
The way the program is written you can pass the host name as the first argument of the program.

Still Lost
By anananomous on 3/27/2005
I dont know any progamming, so could you simplify it to just telling me what to replace with what?

Thanks

Couple of questions...
By fiercekitty on 3/28/2005
I see a comment was posted yesterday so I don't feel so bad about asking...

1. Will this work for shared printers on the network or only printers that connect directly & have their own IP address, i.e. with a Jet card? Or can I do IP address of the host computer \ name of shared printer? (i.e. 192.168.1.250\hp)?

2. I briefly tried to compile under Visual Studio 6.0 but it seems the program tries to make it into a C++ file and thus doesn't compile properly. What compiler should I use?

Thanks in advance for any help offered!

Oops
By fiercekitty on 3/28/2005
I see now that it's a C# snippet - silly me. Got it compiled and now am just trying to work out some of the problems (I'm new to this whole code thing). Sorry to bother you!

any chance...
By amlitnod on 5/3/2005
On having the the .exe emailed. I haven't installed any dev software on my laptop yet, and the way it looks, it won't be soon.

hope you can help, thanks

amlitnod@yahoo.com

AddressFamily.Ieee12844
By plattpou on 6/6/2005
Hi everyone.. I have mine connected thru the Dot4_001 usb port, which i think its the AddressFamily.Ieee12844..

Anyway as far as i tried, i couldnt get connected to the device, did anyone knows how to do this?.. I really need to get the printer status.

exe with interface
By mk14 on 7/21/2005
Could anyone here compile an exe-program of the code with a little interface (asking for IP and MESSAGE and an OK-button)?
I don't have any experience with C#, nor do I have a compiler :( .

mk14

Also works with Lexmark printers
By mk14 on 7/29/2005
I just tried this on my Dell 1700n Laser Printer:
Telnet onto port 9100, type @PJL RDYMSG DISPLAY="MESSAGE".
The printer does not have a front panel display, but the message shows up in the "virtual front panel" on the printer's webpage.

Status in Parallel printer
By plattpou on 10/13/2005
Did anyone know how to retrieve the status data, from a Parallel port connected PCL printer.

I have successfully sent pcl commands, but i have no clue where can i find the status response.

GUI Program
By bullshitcrapbutt on 11/28/2005
Here's a program that will allow you to send a message to a HP JetDirect printer. It doesn't allow random messages, but it gets the job done.

http://www.irongeek.com/i.php?page=security/jetdirecthack

Retrieving display messages...
By drakaan on 11/29/2005
If you want to pull the messages back off of the printer, it's easiest to do it with SNMP (assuming it's enabled on the printer in question).

The OID (do some looking into SNMP if you don't know what that means) for HP printers active status message is:

1.3.6.1.4.11.2.3.9.1.1.3.0

I used this and a dll I found ages ago (pw_snmp.dll) to get the status messages from the printers so the receptionist could look and see when any of them needed toner, had paper jams, etc.

I found one issue......
By jielias on 11/29/2005
I had to change Dns.Resolve to Dns.GetHostEntry in line 22 and then it compiled.

I expanded the code a bit...
By pdavis on 11/29/2005
I found this pretty neat and decided to expand on it some. My app will also retrieve the status of the printer and scroll long text messages. Still lots that could be done with it though. Source code is available here...

http://tinyurl.com/cvwoz

Perl version
By singe on 11/30/2005
I was playing with this a few years ago and found this Perl version that does the same thing:

#!/usr/bin/perl
#
# Printer Fun :-)
# by Laurens (laurens@netric.org)
#
# little perl script to change the "ready message"
# on printers that support PJL commands.
#
# tested on a HP 4000/4100
#

use strict;
use IO::Socket;
use Getopt::Std;

my %opt;
my $data;
my $socket;

print "\nPrinter Fun :-)\n";
print "by Laurens (laurens\@netric.org)\n\n";

getopts("r:t:h", \%opt);
usage() if not %opt or $opt{h};

if ($opt{t} and $opt{r}) {
print "[+] Setting the printer ready message\n";

$data =
"\033%-12345X\@PJL $opt{r}\n".
"\033%-12345X\n";

$socket = IO::Socket::INET->new(
PeerAddr=>$opt{t},
PeerPort=>9100,
Proto =>'tcp')
or die "[-] Couldn't connect to $opt{t}:9100 : $!\n\n";

print $socket $data;
close ($socket);

print "[+] DONE!\n\n";
} else {
print "\n[-] Specify -r and -t!\n\n";
}

sub usage {
print "usage: $0 [-r <message>] [-t <hostname/ip>] [-h]\n";
print "-r : ready message display\n";
print "-t : target\n";
print "-h : help/usage\n";
print "example: $0 -r \"netric.org\" -t 192.168.1.123\n\n";
exit;
}

Password protected?
By fredmsun on 11/30/2005
I've had a number of problems getting this to work on our company network. I think the JetDirect cards are password protected, as I don't seem to be able to Telnet to any of the printers.
If I know the password does anyone know how to pass it to the printer port?
My printer name are in the following format:
\\hostname\printername

Many thanks,
Fred...

Network Printer
By ajai_np on 12/4/2005
Hi ,
I am using Network Printer "\\HostName\printer name"
I tried .. It gives exception that Host not found... while executing the line Dns.Resolve(string hostname)

Please help me......

Compiled and GUI interface
By MetalliMyers on 12/16/2005
Hey fellas,

I made a gui interface for this, so it takes an IP and a message. Not very sophisticated, but cool non-the-less.

http://home.comcast.net/~justinwmyers/files/PrinterHack

Just rename the file with the extension .exe

Works on my HP 4550
By phord on 12/21/2005
We have an HP 4550 Color Laserjet here. It is served by a Windows Print Server somewhere on the corporate LAN, so I normally wouldn't know its IP address. But I went to the printer itself, navigated through the menus to "Print Configuration", and got a printout of all its settings -- including its IP address!

Then, I went to my PC and did this:

X:\> telnet 172.18.159.15 9100
Trying 172.18.159.15...
Connected to 172.18.159.15.
Escape character is '^]'.
@PJL RDYMSG DISPLAY="YOU HAVE PRETTY EYES."
? < PRESSED Ctrl-] >
telnet> quit
Connection closed.

I checked the virtual front panel display on the web page (http://172.18.159.15) but it changed from "Ready" to blank. When I looked at the real printer, though, it showed my message in 16x2 glory!

Now to script this puppy. Thanks for the hack!

Perl bug
By phord on 12/21/2005
The $data line is wrong in the perl script as shown. Fix is below. I also modified it to show the text as it appears on the printer display (16x2):

print "[+] Setting the printer ready message:\n";
print " " . substr($opt{r}, 0,16) . "\n";
print " " . substr($opt{r}, 16,16) . "\n";

$data = "\033%-12345X" .
"\@PJL RDYMSG DISPLAY=\"" .
"$opt{r}\"\r\n\033%-12345X\r\n";


I tested it on a bank of HP printers:
C8500A
5si
8150
4550

Worked great on all of them!

Cool snippet...here's the VB
By stschatz on 12/29/2005
Imports System
Imports System.Text
Imports System.Net
Imports System.Net.Sockets

Namespace HPHack

Public Class PrnHack

Shared Sub Main(ByVal args() As String)

If ParseArgs(args) Then

Console.WriteLine("HP Display Hack")
Console.WriteLine("Host: {0}", args(0))
Console.WriteLine("Message: {0}", message)

Dim ipEndPoint As IPEndPoint
ipEndPoint = New IPEndPoint(Dns.Resolve(args(0)).AddressList(0), PJL_PORT)

Console.WriteLine("Host is {0}", ipEndPoint.ToString())

Dim socket As Socket
socket = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)

socket.Connect(ipEndPoint)

Dim sendData() As Byte
Dim sendString As String

sendString = [String].Format(ChrW(27) + "%-12345X@PJL RDYMSG DISPLAY = " + ChrW(34) + "{0}" + ChrW(34) + ControlChars.Cr + ControlChars.Lf + ChrW(27) + "%-12345X" + ControlChars.Cr + ControlChars.Lf, message)

sendData = Encoding.ASCII.GetBytes(sendString)

Dim result As Integer
result = socket.Send(sendData, sendData.Length, 0)

If result = 0 Then
Console.WriteLine("Could not send on socket")
End If

socket.Close()

Console.WriteLine("Finished")
End If
End Sub



Shared Function ParseArgs(ByVal args() As String) As Boolean

If args.Length <> 2 Then
Console.WriteLine("HP Display Hack: hphack printername ""message""")
Return False
End If

If args(1).Length > 16 Then
Console.WriteLine("Message must be <= 16 characters")
Return False
End If

If args(1).CompareTo("random") = 0 Then
message = GetRandomMessage()
Else
message = args(1)
End If

Return True
End Function


Shared Function GetRandomMessage() As String
Dim Messages() As String = { _
"BUZZ OFF", _
"TOUCH ME", _
"STEP AWAY", _
"SET TO STUN", _
"SCORE = 3413", _
"PAT EATS MICE", _
"FEED ME", _
"GO AWAY", _
"NEED MORE SPACE", _
"POUR ME A DRINK", _
"IN DISTRESS", _
"NICE SHIRT", _
"GO AWAY", _
"NO PRINT FOR YOU", _
"RADIATION LEAK", _
"HANDS UP", _
"PRESS MY BUTTON", _
"TAKE ME HOME", _
"LOOKS LIKE RAIN", _
"HELLO WORLD", _
"NICE HAIR", _
"NEED A MINT?", _
"BE GENTLE", _
"BE KIND", _
"INSERT DISK", _
"BUY ME LUNCH", _
"DONT STOP", _
"COME CLOSER", _
"TAKE A BREAK", _
"INSERT QUARTER", _
"BLACK SABBATH" _
}


Dim r As Random = New Random
Return Messages(Decimal.Remainder(r.Next(), Messages.Length))
End Function

Shared PJL_PORT As Integer = 9100
Shared message As String = "NO MESSAGE"

End Class
end namespace

.NET 2.0 GUI Printer Hack
By bigronaldo on 3/2/2006
Hey! Here's a .NET 2.0 GUI version of this HP Printer Hack tool from Tekdev Open Source Development. It comes with a predefined list of messages to send to the printer ;-).

http://www.tekdev.com/downloads/printerhack/

Works as a print job on non-networked printers
By Stijntje on 5/25/2006
If your printer is not networked, or is networked through a parallel port print server, you can compose the encoded text as a print job and send it to the printer. It worked on my venerable HP Laserjet 5si.

Now in Ruby
By ? on 6/9/2006
All you need is ruby, ruby's all ya need.

#!/usr/bin/env ruby
# Simple HP Printer Hacker
# Written in Ruby by Erik Gregg
# 6/9/06
# Status Usage: hp-message.rb <host> status
# Message Usage: hp-message.rb <host> message "<message>"
require 'net/telnet'
begin
if ARGV[0].nil?
puts "Read the script.
Status Usage: hp-message.rb <host> status
Message Usage: hp-message.rb <host> message \"<message>\""
exit
end
host = Net::Telnet::new("Host" => ARGV[0],
"Port" => 9100,
"Timeout" => 5,
"Prompt" => /\b/n)
if ARGV[1] == "status"
host.cmd("@PJL INFO STATUS") { |c| print c }
elsif ARGV[1] == "message"
host.cmd("@PJL RDYMSG DISPLAY=\""+ARGV[2]+"\"")
else
puts "Oh Cmon. Look at the syntax inside me."
exit
end
host.close

rescue Timeout::Error
puts "Time to go now!"
exit 1
end

More Info
By ? on 6/9/2006
http://ralree.info/articles/2006/06/09/hp-printer-messager-in-ruby
Here's the page in my blog dedicated to it.

printer fun
By chacha on 6/28/2006
thats really cool but how exactly do u impliment the code i've done programing but i dont know how to get it to run properly or were to run the program at
chacha.benny@gmail.com if u can point me in the right direction

test
By mistamaasta on 1/13/2007
love it!

Copyright 2004 OdeToCode.com 


The world is coming to an end. Please log off.
--Unknown
The Blogs
Subscribe to the OdeToCode blogs for the latest news, downloads, new articles, and quirky commentary.
Contribute Code
Privacy
Consultancy