Skip to content

Instantly share code, notes, and snippets.

@gregberns
Created July 16, 2015 03:37
Show Gist options
  • Save gregberns/fbece9d6a8fa9dfe30c6 to your computer and use it in GitHub Desktop.
Save gregberns/fbece9d6a8fa9dfe30c6 to your computer and use it in GitHub Desktop.
Code to Compile and Run .Net 'Scripts' At Runtime
Imports System.Reflection
Imports System.CodeDom.Compiler
Imports IBE.Scripting
Module Module1
Sub Main()
Dim vbScript = <a>
Imports System
Imports IBE.Scripting
Public Class ScriptImpl
Implements IScript
Public Sub RunScript(context As ScriptContext) Implements IScript.RunScript
Console.WriteLine("Hello, " + context.Name + "!")
End Sub
End Class
</a>.Value
Dim csScript = <a>
using System;
using IBE.Scripting;
public class ScriptImpl : IScript
{
public void RunScript(ScriptContext context)
{
Console.WriteLine("Hello, " + context.Name + "!");
}
}
</a>
Dim context1 As New ScriptContext With {.Name = "World"}
Dim context2 As New ScriptContext With {.Name = "IB Team"}
Dim runner As New ScriptRunner
runner.RunScript(vbScript, ScriptType.VisualBasic, context1)
runner.RunScript(csScript, ScriptType.CSharp, context2)
End Sub
End Module
Namespace IBE.Scripting
Public Interface IScript
Sub RunScript(context As ScriptContext)
End Interface
End Namespace
Public Class ScriptContext
Public Property Name As String
End Class
Public Enum ScriptType
VisualBasic
CSharp
End Enum
Public Class ScriptCompilerResult
Property CompiledAssembly As Assembly
Property Errors As New List(Of String)
ReadOnly Property Successful As Boolean
Get
Return Not Errors.Count > 0
End Get
End Property
End Class
Public Class ScriptRunner
Sub RunScript(code As String, type As ScriptType, context As ScriptContext)
Dim compiler As New ScriptCompiler
Dim exec As New ScriptExecutor
Dim compiledResult = compiler.Compile(code, type)
If compiledResult.Successful Then
Dim scriptToRun = exec.FindScriptImplementations(compiledResult.CompiledAssembly)
exec.RunScript(scriptToRun, context)
Else
Return
End If
End Sub
End Class
Public Class ScriptCompiler
Public Function Compile(code As String, type As ScriptType) As ScriptCompilerResult
Dim results As New ScriptCompilerResult
Dim compiled As CompilerResults
compiled = CreateProvider(type).CompileAssemblyFromSource(GetOptions, code)
If compiled.Errors.HasErrors Then
For Each [error] As CompilerError In compiled.Errors
results.Errors.Add(String.Format("Line {0},{1}" & vbTab & ": {2}" & vbLf, [error].Line, [error].Column, [error].ErrorText))
Next
Else
results.CompiledAssembly = compiled.CompiledAssembly
End If
Return results
End Function
Private Function GetOptions() As CompilerParameters
Dim options As New CompilerParameters
options.GenerateExecutable = False
options.GenerateInMemory = True
options.ReferencedAssemblies.Add(Assembly.GetExecutingAssembly().Location)
'cp.ReferencedAssemblies.Add("mscorlib.dll");
'cp.ReferencedAssemblies.Add("System.dll");
'cp.ReferencedAssemblies.Add("System.Core.dll");
'cp.ReferencedAssemblies.Add("MyApp.Data.dll");
Return options
End Function
Private Function CreateProvider(type As ScriptType) As CodeDomProvider
Dim provider As CodeDomProvider
Select Case type
Case ScriptType.VisualBasic
provider = New Microsoft.VisualBasic.VBCodeProvider
Case ScriptType.CSharp
provider = New Microsoft.CSharp.CSharpCodeProvider
Case Else
Throw New Exception("Invalid script type supplied")
End Select
Return provider
End Function
End Class
Public Class ScriptExecutor
Function FindScriptImplementations(assemblyToRun As Assembly) As IScript
' Now that we have a compiled script, lets run them
For Each type As Type In assemblyToRun.GetExportedTypes()
For Each iface As Type In type.GetInterfaces()
If iface = GetType(IScript) Then
' yay, we found a script interface, lets create it and run it!
' Get the constructor for the current type
' you can also specify what creation parameter types you want to pass to it,
' so you could possibly pass in data it might need, or a class that it can use to query the host application
Dim constructor As ConstructorInfo = type.GetConstructor(System.Type.EmptyTypes)
If constructor IsNot Nothing AndAlso constructor.IsPublic Then
' lets be friendly and only do things legitimitely by only using valid constructors
' we specified that we wanted a constructor that doesn't take parameters, so don't pass parameters
Dim scriptObject As IScript = TryCast(constructor.Invoke(Nothing), IScript)
If scriptObject IsNot Nothing Then
'Lets run our script and display its results
'MessageBox.Show(scriptObject.RunScript(50))
'Console.Write(scriptObject.RunScript("World"))
Return scriptObject
' hmmm, for some reason it didn't create the object
' this shouldn't happen, as we have been doing checks all along, but we should
' inform the user something bad has happened, and possibly request them to send
' you the script so you can debug this problem
Else
End If
' and even more friendly and explain that there was no valid constructor
' found and thats why this script object wasn't run
Else
End If
End If
Next
Next
End Function
Sub RunScript(script As IScript, context As ScriptContext)
script.RunScript(context)
End Sub
End Class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment