I did this 'script' thing using the .NET integrated C# compiler.
It is some work to do but basically is looks like this:
public Assembly Compile(string[] source, string[] references) {
CodeDomProvider provider = new CSharpCodeProvider();
CompilerParameters cp = new CompilerParameters(references);
cp.GenerateExecutable = false;
cp.GenerateInMemory = true;
cp.TreatWarningsAsErrors = false;
try {
CompilerResults res = provider.CompileAssemblyFromSource(cp, source);
// ...
return res.Errors.Count == 0 ? res.CompiledAssembly : null;
}
catch (Exception ex) {
// ...
return null;
}
}
public object Execute(Assembly a, string className, string methodName) {
Type t = a.GetType(className);
if (t == null) throw new Exception("Type not found!");
MethodInfo method = t.GetMethod(methodName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); // Get method
if (method == null) throw new Exception("Method '" + methodName + "' not found!"); // Method not found
object instance = Activator.CreateInstance(t, this);
object ret = method.Invoke(instance, null);
return ret;
}
The real code does a lot more things, including a code editor.
It works very well for years now in our factory.
This way the users use C# for the scripts and can therefore use the same API as you do.
You can use a code template that looks like a plain .cs
file. It is build at runtime and provided to the source
parameter of Compile
.
using System;
using System.IO;
using ...
namespace MyCompany.Stuff {
public class ScriptClass {
public object main() {
// copy user code here
// call your own methods here
}
// or copy user code here
private int test(int x) { /* ... */ }
}
}
Example:
string[] source = ??? // some code from TextBoxes, files or whatever, build with template file...
string[] references = new string[] { "A.dll", "B.dll" };
Assembly a = Compile(source, references);
object result = Execute(a, "MyCompany.Stuff.ScriptClass", "main");