Library tutorials & articles

Using .NET to make your Application Scriptable

Dynamic Compilation

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 System.CodeDom.Compiler namespace.

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.VisualBasic.VBCodeProvider and 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 CodeDomProvider's 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 GenerateExecutable to false.
  • We don't want a file on disk, so set GenerateInMemory to true.
  • We don't want debugging symbols in it, so set IncludeDebugInformation to false.
  • We add a reference to the assembly passed as a parameter.
  • We add a reference to System.dll and System.Windows.Forms.dll to 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.

[VB]
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
    'Configure parameters
    With params
        .GenerateExecutable = False
        .GenerateInMemory = True
        .IncludeDebugInformation = False
        If Not Reference Is Nothing AndAlso Reference.Length <> 0 Then _
            .ReferencedAssemblies.Add(Reference)
        .ReferencedAssemblies.Add("System.Windows.Forms.dll")
        .ReferencedAssemblies.Add("System.dll")
    End With
    'Compile
    results = compiler.CompileAssemblyFromSource(params, Source)
    Return results
End Function

[C#]
public static CompilerResults CompileScript(string Source, string Reference,
CodeDomProvider Provider)
{
    ICodeCompiler compiler = Provider.CreateCompiler();
    CompilerParameters parms = new CompilerParameters();
    CompilerResults results;
    // Configure parameters
    parms.GenerateExecutable = false;
    parms.GenerateInMemory = true;
    parms.IncludeDebugInformation = false;
    if (Reference != null && Reference.Length != 0)
        parms.ReferencedAssemblies.Add(Reference);
    parms.ReferencedAssemblies.Add("System.Windows.Forms.dll");
    parms.ReferencedAssemblies.Add("System.dll");
    // Compile
    results = compiler.CompileAssemblyFromSource(parms, Source);
    return results;
}

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.

[VB]
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
            Return DLL.CreateInstance(t.FullName)
        End If
    Next
    Return Nothing
End Function

[C#]
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)
            return DLL.CreateInstance(t.FullName);
    }
    return null;
}

Comments

  1. 17 Dec 2005 at 10:07

    How can we expose host application objects to the script that we dynamically compile?

  2. 12 Dec 2005 at 06:17

    How can we expose object in the host application to the script that we dynamically compile?

  3. 01 Jan 1999 at 00:00

    This thread is for discussions of Using .NET to make your Application Scriptable.

Leave a comment

Sign in or Join us (it's free).

Tim Dawson
AddThis

Related podcasts

  • A Practical Look at Silverlight 2 Part 1

    Now that Silverlight 2 is at the Olympics and making a big splash, we wanted to explore this fascinating technology more. Microsoft Silverlight 2 is a cross-browser, cross-platform, and cross-device plug-in for delivering the next generation of .NET based media experiences and rich interactive ap...

Events coming up

  • Nov 18

    15 Minutes of Fame

    Dresher, United States

    This is a yearly tradition. We select 10 of the favorite speakers from monthly meetings, code camps, and hands on labs. Each one does a 15 minute talk on their favorite .NET technology. This is our 10th anniversary so we plan a gala event with special prizes and refreshments.

Want to stay in touch with what's going on? Follow us on twitter!