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
GenerateExecutableto false. - We don't want a file on disk, so set
GenerateInMemoryto true. - We don't want debugging symbols in it, so set
IncludeDebugInformationto false. - We add a reference to the assembly passed as a parameter.
- We add a reference to
System.dllandSystem.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.
[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;
}
Related articles
Related discussion
-
Creating a Windows Service in VB.NET
by Templario55 (107 replies)
-
High-Performance .NET Application Development & Architecture
by Manjot Bawa (0 replies)
-
An Introduction to VB.NET and Database Programming
by carlosmen (14 replies)
-
Compatibility Issue on Firefox to display on Cursor Location
by dinc3r (1 replies)
-
VB/VB.NET
by surath (7 replies)
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.
How can we expose host application objects to the script that we dynamically compile?
How can we expose object in the host application to the script that we dynamically compile?
This thread is for discussions of Using .NET to make your Application Scriptable.