Created
July 16, 2015 03:37
-
-
Save gregberns/fbece9d6a8fa9dfe30c6 to your computer and use it in GitHub Desktop.
Code to Compile and Run .Net 'Scripts' At Runtime
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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