|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Contents
IntroductionThe 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. What You Will Learn from This Article
BackgroundIt would be nice if you were familiar with basic serialization concepts. How the Program WorksWhen 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. Code Behind the ApplicationClasses Used for DrawingIn order to store information about the drawings, I have developed two classes:
The Managing the Drawing ProcessWhen the user presses the mouse button, a Boolean variable indicating whether the drawing is in progress or not is set to private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Pen p = new Pen(color, width);
if (isdrawing)
{
using (Graphics gr=this.CreateGraphics())
{
gr.DrawLine(p, prev, e.Location);
cv.Coordinates.Add(prev);
prev = e.Location;
}
}
p.Dispose();
}
Serializing and Building the ExecutableWhen the user ends drawing and clicks the Build button, an instance of the private bool Compile(string path)
{
bool result;
using (CSharpCodeProvider prov = new CSharpCodeProvider())
{
CompilerParameters param = new CompilerParameters();
string pathtoicon = "";
//Set executable icon
if (File.Exists(Application.StartupPath + "\\icon.ico"))
{
pathtoicon = Application.StartupPath + "\\icon.ico";
}
param.CompilerOptions =
"/target:winexe" + " " + "/win32icon:" +
"\"" + pathtoicon + "\"";
param.GenerateExecutable = true;
param.GenerateInMemory = false;
param.IncludeDebugInformation = false;
//Add the file to which data was serialized as embedded resource
param.EmbeddedResources.Add(
Environment.GetEnvironmentVariable("TEMP")+"\\points.dat");
param.OutputAssembly = path;
//Add references
param.ReferencedAssemblies.Add("System.dll");
param.ReferencedAssemblies.Add("System.Data.dll");
param.ReferencedAssemblies.Add("System.Deployment.dll");
param.ReferencedAssemblies.Add("System.Drawing.dll");
param.ReferencedAssemblies.Add("System.Windows.Forms.dll");
param.ReferencedAssemblies.Add("System.Xml.dll");
param.TreatWarningsAsErrors = false;
//Compile it
CompilerResults compresults =
prov.CompileAssemblyFromSource(param,
Properties.Resources.Program);
result = compresults.Errors.Count == 0;
File.Delete(Environment.GetEnvironmentVariable("TEMP") +
"\\points.dat");
}
return result;
}
Recreating the Drawing processWhen you launch the generated executable, an object of the Advanced Binary Serialization: Deserializing an Object Into a Different Type Than the One It was Serialized IntoIf 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:
Or
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. sealed class typeconvertor : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type returntype = null;
if (assemblyName ==
"draw on desktop, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null")
{
assemblyName = Assembly.GetExecutingAssembly().FullName;
returntype =
Type.GetType(String.Format("{0}, {1}",
typeName, assemblyName));
return returntype;
}
if (typeName ==
"System.Collections.Generic.List`1[[Mousemove.Curve,
draw on desktop, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=null]]")
{
typeName =
typeName.Replace("draw on desktop,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null",
Assembly.GetExecutingAssembly().FullName);
returntype =
Type.GetType(String.Format("{0}, {1}",
typeName, assemblyName));
return returntype;
}
return returntype;
}
}
After that, you have to set the formatter's private Drawing eserialize(Stream input)
{
Drawing temp = null;
try
{
BinaryFormatter formatter = new BinaryFormatter();
//This is the most important part
formatter.Binder = new typeconvertor();
temp = formatter.Deserialize(input) as Drawing;
input.Close();
}
catch (SerializationException ex)
{
MessageBox.Show(ex.ToString());
}
return temp;
}
During deserialization, Drawing on the ScreenIn 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 When we have the handle retrieved, we pass it to a [DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
Graphics gr = Graphics.FromHdc(GetDC(IntPtr.Zero))
Once we have created the Points of InterestDeserializing an object into a different type than the one it was serialized into was the most interesting and tricky part in the whole application. History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||