Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 8

Fun With C# and HP Laserjets 

Posted by scott 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";

}
}

#include <iostream>
#include <string>

void main()
{

    std::string str;
    std::cin>>str;

    if(str == "TheCorrectSerialNumber")
        std::cout<<"Hello world!!!"<<std::endl;
}

Unhooking a leaking C# object from


Microsoft.Win32.SystemEvents.UserPreferenceChanged
Before I get started I'd like to clarify what the tem "leak" means for the purposes of this
discussion. Any object that your application is no longer using but cannot be garbage
collected is "leaking" for the purposes of this discussion. I know that technically the memory
is still accessible and it is possible to release it but the fact of the matter is that your
application is not using this memory and it will not ever be released. Given enough time this
will eventually lead to an out of memory exception.

For anyone that has used a memory profiler it's possible that you've come across leaks
related to the System.Windows.Form.ToolStripTextBoxcontrol. If you haven't, here's
a link to a blog post that can shed some light on this issue. It's a very good explanation of
the problem but in summary, the ToolStripTextBox control is hooking
theMicrosoft.Win32.SystemEvents.UserPreferenceChanged repeatedly and unhooking only
once. This is causing leaks because theUserPreferenceChanged event is static and does
not go out of scope for the life of your application. This creates a root reference for any
listeners of that event. Unfortunately, the workaround posted on the linked page did not
seem to work very well for me. After modifying the workaround until it sufficiently suited my
needs I found that the ToolStripTextBox control is not the only class that fails to unhook
from this event causing leaks. For the project I'm working on this was causing a chain
reaction of leaking objects that was fairly significant.

There are three ways to solve this problem that I can see. The first option is that you can try
to predict the nature of the bug in the class that is leaking. Using this information you can
code a very specific solution to this problem. The problem with this is that it requires that
you are correct in your analysis of the problem which is buried in a class that you don't have
the source code for and your targeted solution also works correctly. The second option I see
here is the brute force method. Just unhook the method a bunch of times and hopefully the
problem goes away. If I need to explain what's wrong with that don't waste your time
reading further. The third option I see for solving this problem is to create a generic method
that will look at the listeners of this event, find all references to the leaking object, and
unhook them. This is the approach I have used to solve this problem.

To implement this solution I created a


classUnhookSystemEventUserPreferenceChangedEvent. In this class there is a static
public method UnhookObject(object pObjectToUnhook). This will take any object that is
directly hooked (ToolStripTextBox andToolStripItemOverflow are the two I've had problems
with) to theMicrosoft.Win32.SystemEvents.UserPreferenceChanged event and will unhook
them. It's preferable to call this method in response to thedisposing event of the leaking
object. Rather than an abstract explanation of the code I've tried to comment the code itself
as thoroughly as possible so that I could just post the code and let you walk through it. If
there's another solution out there I didn't see it so for anyone who's been agonizing over this
I hope this helps.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;

namespace HelperClasses
{
     public class UnhookSystemEventUserPreferenceChangedEvent
     {
          //we'll use a static List to cache a reference to the internal list
of UserPreferenceChangedEvent listeners so that 
          // we do not need to search for it every time.
          static System.Collections.IList _UserPreferenceChangedList = null;

          static public void UnhookObject(object pObjectToUnhook)


          {
               //First check for null and get a ref to the
UserPreferenceChangedEvent's internal list of listeners if needed.
               if (_UserPreferenceChangedList == null)
GetUserPreferenceChangedList();
               //then, scan that list for any delegates that point to
pObjectToUnhook.
               SearchListAndRemoveEventHandlers(pObjectToUnhook);
          }

          static private void GetUserPreferenceChangedList()


          {
               Type oSystemEventsType = typeof(SystemEvents);

               //Using reflection, get the FieldInfo object for the internal


collection of handlers
               // we will use this collection to find the handler we want to
unhook and remove it.
               // as you can guess by the naming convention it is a private
member.
               System.Reflection.FieldInfo oFieldInfo =
oSystemEventsType.GetField("_handlers",
                                   System.Reflection.BindingFlags.Static |
                                   System.Reflection.BindingFlags.GetField |
                                   System.Reflection.BindingFlags.FlattenHiera
rchy |
                                   System.Reflection.BindingFlags.NonPublic);

               //now, get a reference to the value of this field so that you


can manipulate it.
               //pass null to GetValue() because we are working with a static
member.
               object oFieldInfoValue = oFieldInfo.GetValue(null);

               //the returned object is of type Dictionary<object,


List<SystemEventInvokeInfo>>
               //each of the Lists<> in the Dictionary<> is used to maintain a
different event implementation.
               //It may be more efficient to figure out how the
UserPreferenceChanged event is keyed here but a quick-and-dirty
               // method is to just scan them all the first time and then
cache the List<> object once it's found.

               System.Collections.IDictionary dictFieldInfoValue =
oFieldInfoValue as System.Collections.IDictionary;
               foreach (object oEvent in dictFieldInfoValue)
               {
                    System.Collections.DictionaryEntry deEvent =
(System.Collections.DictionaryEntry)oEvent;
                    System.Collections.IList listEventListeners =
deEvent.Value as System.Collections.IList;

                    //unfortunately, SystemEventInvokeInfo is a private class


so we can't declare a reference of that type.
                    //we will use object and then use reflection to get what
we need...
                    List<Delegate> listDelegatesToRemove = new
List<Delegate>();

                    //we need to take the first item in the list, get it's
delegate and check the type...
                    if (listEventListeners.Count > 0 &&
listEventListeners[0] != null)
                    {
                         Delegate oDelegate =
GetDelegateFromSystemEventInvokeInfo(listEventListeners[0]);
                         if (oDelegate is UserPreferenceChangedEventHandler)
                         { _UserPreferenceChangedList = listEventListeners; }
                    }
                    //if we've found the list, no need to continue searching
                    if (_UserPreferenceChangedList != null) break;
               }
          }

          static private void SearchListAndRemoveEventHandlers(object


pObjectToUnhook)
          {
               if (_UserPreferenceChangedList == null) return; //Do not run if
we somehow haven't found the list.

               //unfortunately, SystemEventInvokeInfo is a private class so we


can't declare a reference of that type.
               //we will use object and then use reflection to get what we
need...
               List<UserPreferenceChangedEventHandler> listDelegatesToRemove =
new List<UserPreferenceChangedEventHandler>();

               //this is NOT threadsafe. Unfortunately, if the collection is


modified an exception will be thrown during iteration.
               // This will happen any time another thread hooks or unhooks
the UserPreferenceChanged event while we iterate.
               // Modify this to be threadsafe somehow if that is required.
               foreach (object oSystemEventInvokeInfo in
_UserPreferenceChangedList)
               {
                    UserPreferenceChangedEventHandler oDelegate =
                         GetDelegateFromSystemEventInvokeInfo(oSystemEventInvo
keInfo) as UserPreferenceChangedEventHandler;

                    if (oDelegate != null && oDelegate.Target ==


pObjectToUnhook)
                    {
                         //at this point we have found an event handler that
must be unhooked.
                         listDelegatesToRemove.Add(oDelegate);
                    }
               }

               //We should unhook using the public method because the internal
implementation of this event is unknown.
               // iterating the private internal list is already shady enough
without manipulating it directly...
               foreach (UserPreferenceChangedEventHandler itemToRemove in
listDelegatesToRemove)
               { SystemEvents.UserPreferenceChanged -= itemToRemove; }
          }

          static private Delegate GetDelegateFromSystemEventInvokeInfo(object


pSystemEventInvokeInfo)
          {
               Type typeSystemEventInvokeInfo =
pSystemEventInvokeInfo.GetType();
               System.Reflection.FieldInfo oTmpFieldInfo =
typeSystemEventInvokeInfo.GetField("_delegate",
                                   System.Reflection.BindingFlags.Instance |
                                   System.Reflection.BindingFlags.GetField |
                                   System.Reflection.BindingFlags.FlattenHiera
rchy |
                                   System.Reflection.BindingFlags.NonPublic);

               //Here we are NOT working with a static field so we will supply


the SystemEventInvokeInfo
               // object that we found in the List<> object to the GetValue()
function.
               Delegate oReturn =
oTmpFieldInfo.GetValue(pSystemEventInvokeInfo) as Delegate;

               return oReturn;
          }
     }
}
Pos

You might also like