This is a technical blog post today that I hope will be fairly useful to everyone out there. I have wondered how to actually include scripting abilities within my applications and today I decided to figure it out. The process is actually pretty simple. You need to include two namespaces which are System.CodeDom.Compiler and System.Reflection to do this (the first compiles your ‘script’ while the second lets you use the result) and then you can do whatever you want from there. Here’s one I made earlier…

private void btnRun_Click(object sender, EventArgs e) 
  CodeDomProvider codeProvider = CodeDomProvider.CreateProvider("C#"); 
  CompilerParameters codeParams = new CompilerParameters(); 
  CompilerResults codeResults = null; 
  codeParams.GenerateExecutable = false; 
  codeParams.GenerateInMemory = true; 
  codeParams.IncludeDebugInformation = false; 
  codeResults = codeProvider.CompileAssemblyFromSource( codeParams, new string[] { txtSourceInput.Text } ); 
  CompilerErrorCollection codeErrors = cResults.Errors; 
  Type[] resultTypes = cResults.CompiledAssembly.GetTypes(); 
  object o = codeResults.CompiledAssembly.CreateInstance( resultTypes[0].FullName ); 
  MethodInfo[] m = resultTypes[0].GetMethods(); 
  m[0].Invoke(o, null); 

There’s a lot to note here:


  1. Lines 9 and 10 are adding referenced assemblies programmatically but this is the same as right clicking “References” in your project and clicking “Add Reference” as this makes the methods within those assemblies available to the dynamically compiled code (the user’s script). I was testing this by popping up a messagebox so needed System.Windows.Forms.dll but you could add anything here that would work using the “Add Reference” option of a normal project.
  2. Line 11 uses an array of strings, I’m not sure why but for a standard user script you can just do what I’ve done and enter the whole text box as the first element.
  3. Line 15 shows the retrieval of the compile errors collection which is IMPORTANT! If you don’t check this for errors then your application will likely crash due to bad end-user code. I was just doing this as testing so I haven’t added anything to actually check the collection but if “codeErrors.HasErrors” is false then it’s good to go.
  4. Line 17 creates the type by using the FULL NAME If you do not do this you will get the exception “Non-static method requires a target” unless you execute static methods.
  5. The remaining lines are NOT good code and you shouldn’t do things this way in your live applications but this is fine as an example. I’m showing the way to get the types, create an instance, get the methods of that type and then how to invoke the first method of the instance of that type. You can easily modify this code to check for an expected type and method and provide parameters as needed.
When you come to test this you must feed it fully correct C# or the CompilerErrorCollection will have errors. So, for example, you cannot try to compile “MessageBox.Show(“HI!!”);” but you can compile:
namespace MyDynamicNamespace 
  public class MyDynamicClass 
    public void MyDynamicMethod() 
      MessageBox.Show("This is a test");