The program presented here allows you to draw different graphics and to embed graphics into an executable. When you run that executable, it will produce exactly the same drawing on the desktop. What's more, you will see the graphics being drawn. So, you can draw or write something using your mouse in different colors and different widths, build an executable file and send it to someone. When they run it, it will reproduce everything you have drawn.
- How to draw directly on the desktop
- How to deserialize an object into a different type than the one it was serialized into
- How to compile C# code during runtime
It would be nice if you were familiar with basic serialization concepts.
When you run the program, you can start drawing by pressing the mouse button and moving it while holding it down. You can choose different colors and widths to use while drawing. If you click Build, you will be prompted for the destination of the executable and it will be built. Running the newly built executable will draw the same graphic on the screen and you will be able to watch it being drawn.
In order to store information about the drawings, I have developed two classes:
Drawing. Here are their class diagrams:
Curve class carries information about all mouse movements that occur while the mouse button is pressed. The coordinates of all points are stored in a variable of
_duration stores the amount of time that the curve was being drawn.
_pause indicates the time that elapsed after the previous curve was drawn. Obviously, it will be equal to zero for the first curve.
Drawing stores the list of curves, as well as the width and height of the screen.
When the user presses the mouse button, a Boolean variable indicating whether the drawing is in progress or not is set to
true. After that, this variable is checked in the
MouseMove event and a line is drawn accordingly:
private void Form1_MouseMove(object sender, MouseEventArgs e)
Pen p = new Pen(color, width);
using (Graphics gr=this.CreateGraphics())
gr.DrawLine(p, prev, e.Location);
prev = e.Location;
cv is a variable of type
Curve. When the drawing of the current curve finishes,
cv is added to the list of curves in an instance of the
When the user ends drawing and clicks the Build button, an instance of the
Drawing class is serialized to file. Before serializing, all coordinates are transformed to screen coordinates using the
PointToScreen method. After serialization, an executable file is built using the
CompilerParameters classes. Here is the code snippet:
private bool Compile(string path)
using (CSharpCodeProvider prov = new CSharpCodeProvider())
CompilerParameters param = new CompilerParameters();
string pathtoicon = "";
if (File.Exists(Application.StartupPath + "\\icon.ico"))
pathtoicon = Application.StartupPath + "\\icon.ico";
"/target:winexe" + " " + "/win32icon:" +
"\"" + pathtoicon + "\"";
param.GenerateExecutable = true;
param.GenerateInMemory = false;
param.IncludeDebugInformation = false;
param.OutputAssembly = path;
param.TreatWarningsAsErrors = false;
CompilerResults compresults =
result = compresults.Errors.Count == 0;
When you launch the generated executable, an object of the
Drawing class is deserialized from the embedded stream and then it is drawn on the desktop. The steps are described below.
If you serialize an object in one assembly and try to deserialize it from another assembly, you will get an error saying that the assembly in which the serialized object was declared cannot be found. You will get the same error even if both assemblies contain the class definition for the specified object. There were two possible ways to solve this problem:
- Use XML Serialization instead of Binary
- Move the class definition to a separate DLL and reference the same DLL from both assemblies
Both of them have disadvantages. XML Serialization is slower than Binary and the output file is larger in size. Defining classes in a separate DLL means that you won't be able to create a stand-alone executable. So, I was stuck with this problem.
After Googling for several hours, I found this website that explains Binary Serialization very deeply: Binary Serialization. One of the concepts discussed was how to deserialize an object to a different type. So, I used this to deserialize my object to the same type, but defined in another assembly. To do that, you have to make your own class inherited from
System.Runtime.Serialization.SerializationBinder and then override the
BindToType method. Here is my implementation:
sealed class typeconvertor : SerializationBinder
public override Type BindToType(string assemblyName, string typeName)
Type returntype = null;
if (assemblyName ==
"draw on desktop, Version=220.127.116.11,
assemblyName = Assembly.GetExecutingAssembly().FullName;
if (typeName ==
draw on desktop, Version=18.104.22.168, Culture=neutral,
typeName.Replace("draw on desktop,
Version=22.214.171.124, Culture=neutral, PublicKeyToken=null",
After that, you have to set the formatter's
Binder property to this newly created type. The code snippet below will make it clearer:
private Drawing eserialize(Stream input)
Drawing temp = null;
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = new typeconvertor();
temp = formatter.Deserialize(input) as Drawing;
catch (SerializationException ex)
BindToType is called several times. When it encounters a type defined in the "draw on desktop" assembly, it substitutes it with the corresponding type from the calling assembly. So, the problem of deserialization is solved.
In order to draw on the screen, we need to retrieve a handle to the device context. To retrieve the handle of the device context, we call the function
GetDC and pass an empty handle.
GetDC is an unmanaged method, so we need to use P/Invoke to be able to call it from our application.
When we have the handle retrieved, we pass it to a
static method of the
Graphics class called
Graphics.FromHdc. This function creates a new
Graphics object that we can use for drawing. Here is a code snippet showing the declaration of the
GetDC function and the creation of a
static extern IntPtr GetDC(IntPtr hWnd);
Graphics gr = Graphics.FromHdc(GetDC(IntPtr.Zero))
Once we have created the
graphics object, we can use it for necessary drawings. So, that's it! I hope you found it interesting and learnt something new.
Deserializing an object into a different type than the one it was serialized into was the most interesting and tricky part in the whole application.
- August 24, 2007 - Initial Release