Finally on to the meat of the article. As I said already, compiler services are built in to the .NET framework. These live in the
System.CodeDom namespace. I say compiler services and not compilers because
CodeDom encompasses a lot more than that. However, the bits we're interested in live in the
To start off with we need an instance of a class that inherits from
CodeDomProvider. This class provides methods to create instances of other helper classes specific to that language. Derivatives of
CodeDomProvider are shipped with the framework for all four .NET languages, however the only two we're interested in are VB and C#, the most popular. They are
Microsoft.CSharp.CSharpCodeProvider. The design of this system is so good that after choosing which of these to use, the steps are exactly the same.
The first thing we use is the
CreateCompiler method to get an instance of a class implementing
ICodeCompiler. Once we have this that's the last we need from our
CodeDomProvider. Our helper class will have a
CompileScript function, which will accept script source, the path to an assembly to reference and a language to use. We'll overload it so the user can use their own codeprovider if they want to support scripting in a language other than VB or C#.
The next step once we have our
ICodeCompiler is to configure the compiler. This is done using the
CompilerParameters class. We create an instance of it using the parameterless constructor and configure the following things:
- We don't want an executable, so set
- We don't want a file on disk, so set
- We don't want debugging symbols in it, so set
- We add a reference to the assembly passed as a parameter.
- We add a reference to
System.Windows.Forms.dllto make the TextBox usable.
As a side note, to provide information for compilers specific to the language you are using (such as specifying
Option Strict for VB) you use the
CompilerOptions property to add extra command-line switches.
Once we have our
CompilerParameters set up we use our compiler's
CompileAssemblyFromSource method passing only our parameters and a string containing the script source. This method returns an instance of the
CompilerResults class. This includes everything we need to know about whether the compile succeeded - if it did, the location of the assembly produced and if it didn't, what errors occured.
That is all this helper function will do. It will return the
CompilerResults instance to the application for further processing.
Public Shared Function CompileScript(ByVal Source As String, ByVal Reference As String, _
ByVal Provider As CodeDomProvider) As CompilerResults
Dim compiler As ICodeCompiler = Provider.CreateCompiler()
Dim params As New CompilerParameters()
Dim results As CompilerResults
.GenerateExecutable = False
.GenerateInMemory = True
.IncludeDebugInformation = False
If Not Reference Is Nothing AndAlso Reference.Length <> 0 Then _
results = compiler.CompileAssemblyFromSource(params, Source)
public static CompilerResults CompileScript(string Source, string Reference,
ICodeCompiler compiler = Provider.CreateCompiler();
CompilerParameters parms = new CompilerParameters();
// Configure parameters
parms.GenerateExecutable = false;
parms.GenerateInMemory = true;
parms.IncludeDebugInformation = false;
if (Reference != null && Reference.Length != 0)
results = compiler.CompileAssemblyFromSource(parms, Source);
We also need one more helper function, which we will pretty much completely take straight from the plugin services we developed in my previous tutorial. This is the function that examines a loaded assembly for a type implementing a given interface and returns an instance of that class.
Public Shared Function FindInterface(ByVal DLL As Reflection.Assembly, _
ByVal InterfaceName As String) As Object
Dim t As Type
'Loop through types looking for one that implements the given interface
For Each t In DLL.GetTypes()
If Not (t.GetInterface(InterfaceName, True) Is Nothing) Then
public static object FindInterface(System.Reflection.Assembly DLL, string InterfaceName)
// Loop through types looking for one that implements the given interface
foreach(Type t in DLL.GetTypes())
if (t.GetInterface(InterfaceName, true) != null)