C# Script

There is VBScript, JScript and PHP. But there isn't anything like C#-Script. So, I decided to play around a bit, only as a matter of checking out the possibilties.
The plan:

Create a CS-Script.exe file that we can use to 'launch' a .cs file. Without having to add the file to a solution or anything.

A very basic tool, that can launch one .cs file. View it as some sort of batch scripting tool. Where you can quickly open notepad, write something and run it.

Our goal is to reach it through the right click menu in the shell. For every file! So we'll create a console app (CS-Script) that accepts one argument, the filename.

We want to let the user know when he fucks up and doesn't provide an argument, so we add a reference to System.Windows.Forms and display a MessageBox.

using System;

using System.Windows.Forms;

 

namespace CS_Script {

      class CS_Script {

            static void Main(string[] args) {

                  if (args.Length > 0) {

                 

                  } else {

                        MessageBox.Show("You need to provide the filename as an argument!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

                  }

            } /* Main */

      } /* CS_Script */

} /* CS_Script */


Next, we want to check if the file exists. Otherwise, just hit them with another MessageBox.

if (File.Exists(args[0])) {

                       

} else {

      MessageBox.Show("You need to provide an existing file as an argument!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

}


Now we read the file, just use an example from MSDN.

try {

      using (StreamReader textReader = new StreamReader(args[0])) {

            String textFile = textReader.ReadToEnd();

            Console.WriteLine(textFile);

      }

}     catch (Exception e) {

      MessageBox.Show("The file could not be read:\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

}


After we have read in the source we're going to compile it and run it. This is the hardest part :) There are some samples out there, but they all seemed to fail on my test data.

try {

      ExecuteSource(textFile);

}     catch (Exception e) {

      MessageBox.Show("The file could not be executed:\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

}

The first thing we have to do is setup the compiler. Here I've put some restrictions. Every file should have a class CScript with a public void Main in it. If you want to extend this, be sure to get rid of that ;)

CSharpCodeProvider codeProvider = new CSharpCodeProvider();

                 

ICodeCompiler compiler = codeProvider.CreateCompiler();

CompilerParameters parameters = new CompilerParameters();

parameters.GenerateExecutable = false;

parameters.GenerateInMemory = true;

parameters.OutputAssembly = "CS-Script-Tmp-Junk";

parameters.MainClass = "CScript.Main";

parameters.IncludeDebugInformation = false;

Another important parameter is the assemblies we involve in compiling our source. A "good" thing would be to put the most important namespaces in your project with using. We're going to include all the ones our project uses.

foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies()) {

      parameters.ReferencedAssemblies.Add(asm.Location);

}


And now it's time to compile our source, let's hope everything goes fine.

 

CompilerResults results = compiler.CompileAssemblyFromSource(parameters, sourceText);


Ofcourse not everything went fine. Someone messed up his source, let's give him another error.

 

if (results.Errors.Count > 0) {

      string errors = "Compilation failed:\n";

      foreach (CompilerError err in results.Errors) {

            errors += err.ToString() + "\n";

      }

      MessageBox.Show(errors, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

}


But some people don't make mistakes, and we run their code.

The compiler gave back an Assembly to use. It's very simply to invoke this one.

One strange issue, even thou we have set GenerateInMemory, it does create a file on our harddisk (at least here), so we have to clean that up.

 

object o = results.CompiledAssembly.CreateInstance("CScript");

Type type = o.GetType();

MethodInfo m = type.GetMethod("Main");

m.Invoke(o, null);

if (File.Exists("CS-Script-Tmp-Junk")) { File.Delete("CS-Script-Tmp-Junk"); }


If everything was fine, we now launched our program!

Here's a the little program I tested on:

using System;

using System.Windows.Forms;

 

class CScript {

      public void Main() {

            MessageBox.Show("I'm being loaded dynamicly!");

      }

}


We reached our goal of running code from a text file for now.

In my next article I'll talk about adding our CS-Script.exe file to the shell menu so we can use it on our files.

As always, I've uploaded the sources for this project.

Update: Here is the article about adding your menu to the shell: Associating your program with every file - ShellExtension

4 Comments

Comments have been disabled for this content.