Introduction
Well, this is my first article at The Code Project, so I'll try my best to stay on topic. I was recently interested in creating a sort of precedence calculator library. I was about 10% underway when I suddenly had a thought. What if instead I could just use C# code, compile and execute it, then get the result of the mathematical equation back.
Well, I figured it wouldn't be easy, but there must be a way of making it work. Turns out I was wrong, it was easy. But, I wanted it to be even easier. I wanted to abstract this concept enough that all I would have to do is pass in the code (or for a more complex situation, the code and some parameters describing what to run).
private Assembly BuildAssembly(string code)
{
Microsoft.CSharp.CSharpCodeProvider provider =
new CSharpCodeProvider();
ICodeCompiler compiler = provider.CreateCompiler();
CompilerParameters compilerparams = new CompilerParameters();
compilerparams.GenerateExecutable = false;
compilerparams.GenerateInMemory = true;
CompilerResults results =
compiler.CompileAssemblyFromSource(compilerparams, code);
if (results.Errors.HasErrors)
{
StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
foreach (CompilerError error in results.Errors )
{
errors.AppendFormat("Line {0},{1}\t: {2}\n",
error.Line, error.Column, error.ErrorText);
}
throw new Exception(errors.ToString());
}
else
{
return results.CompiledAssembly;
}
}
This is the core of my library. This function builds the Assembly
object and stores it in memory. As you can see, I'm using the C# code provider. To compile VB.NET code (or any other code for that matter) is as simple as changing the provider class. I told the compiler to generate a DLL by setting the GenerateExecutable
to false
, then told it to keep the DLL in memory instead of creating a file on the local disk. Any errors will be caught and displayed much like they are in Visual Studio .NET. Finally, if the compilation is successful, the Assembly
object is returned.
public object ExecuteCode(string code,
string namespacename, string classname,
string functionname, bool isstatic, params object[] args)
{
object returnval = null;
Assembly asm = BuildAssembly(code);
object instance = null;
Type type = null;
if (isstatic)
{
type = asm.GetType(namespacename + "." + classname);
}
else
{
instance = asm.CreateInstance(namespacename + "." + classname);
type = instance.GetType();
}
MethodInfo method = type.GetMethod(functionname);
returnval = method.Invoke(instance, args);
return returnval;
}
This is the pubic interface to my library. Very straight forward, pass in the code (which could even be read from a C# file on your disk), pass in the namespace that contains the executing class, pass in the class of the executing function, pass in the function to be executed, specify if the executing function is static or not, and finally the params array for any parameters that the executing function calls for. With this setup, you could theoretically execute code that uses multiple namespaces. As long as the code is good and you specify the correct parameters, it can be done.
One thing that cannot be done with this library (at the moment at least) is running code that calls for referenced namespaces to external DLLs. This is possible, it is part of the compiler parameters, I just haven't set it up yet.
That's basically it, that's all the code you need to compile and execute uncompiled code at runtime. Pretty nifty stuff, the practical applications for this are numerous. Personally, I'm going to continue with my calculator project for now, but feel free to use this library or do whatever.
The library also includes a template setup for doing mathematical calculations.
I also included a small test application with the library. It's nothing special, just what I used to debug this library. The instructions are pretty straight forward, the only confusing command is the Execute command '/x' -- it takes a number of parameters and uses the following syntax:
/x myNamespace MyClass MyFunction False param1,param2,param3,etc
The False
states that MyFunction
is not static.