2

I've seen several applications that let the user add customization via vb-script or javascript. A heavy example of this would be office addons via vbscript or RPG maker with ruby scripting.

I was wanting to add the option in one of my apps to let the users write some custom rules in some scripting language that run every-time they try and save/submit the webpage

I know this question is kind of deep but after spending about an hour on google I still don't even know where to start to solve this type of problem. I know there is a lot to consider in attempting this.

Please point me in the right direction.

Atomhax
  • 103
  • 2
  • 8
  • To what level are you expecting your users to comprehend the language and/or programming? In other words, how advanced are you expecting your engine (and its language) to be? – Kenneth K. Apr 18 '16 at 20:18
  • The scripting addon would be more catered to IT staff who know some programming. Visual basic is what the project manager wants – Atomhax Apr 18 '16 at 22:54
  • You might look into the [Roslyn project](https://github.com/dotnet/roslyn). I believe it can be used as a script host. It looks likes [others have already been using Roslyn for similar](http://stackoverflow.com/questions/11159007/roslyn-visualbasic-scriptengine-doesnt-recognize-hostobject-written-on-c-sharp). – Kenneth K. Apr 18 '16 at 23:12

2 Answers2

3

There are a couple of options depending on which language(s) you want to support. VB Script can be done using MSScriptControl and C# can be done using Microsoft.CSharp.

Here's a quick example of something I just did to pull a C# script out of a database and execute it. Please note that this only takes in strings so you would have to adjust it if you wanted your arguments to be a collection or different data type.

value = CreateTransformMethodInfo(_script.ScriptBody).Invoke(null, args.Select(x => x.Value).ToArray()); //args would be the arguments in your script

public static MethodInfo CreateTransformMethodInfo(string script)
    {
        using (var compiler = new CSharpCodeProvider())
        {
            var parms = new CompilerParameters
            {
                GenerateExecutable = false,
                GenerateInMemory = true,
                CompilerOptions = "/optimize",
                ReferencedAssemblies = { "System.Core.dll" }
            };

            return compiler.CompileAssemblyFromSource(parms, script)
                .CompiledAssembly.GetType("Transform")
                .GetMethod("Execute");
        }
    }

Then the actual script looks like this:

public class Transform
{
    public static string Execute(string firstName)
    {
        return "Test";
    }
}

The one caveat to this is that you would need to name the class 'Transform' and the method to run 'Execute' every time since you can see we use these two values when compiling the method to run. You could name helper classes or methods whatever you wanted though, as long as the 'executing' class and method stays Transform/Execute.

Ryan Intravia
  • 420
  • 6
  • 12
1

If you want the coding to be on the client's side, you can use Javascript and easily just eval(userCode) where usercode is a string.

If you're willing to run the user's code in the server side using c#, you can use the built in compiler with the libraries Microsoft.CSharp and System.CodeDom.Compiler. It can be done like that:

string code = @"
    using System;

    namespace First
    {
        public class Program
        {
            public static void Main()
            {
            " +
                "Console.WriteLine(\"Hello, world!\");"
                + @"
            }
        }
    }
"; //Assume this is the code the client gave you
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();

parameters.GenerateInMemory = true; //You can add references to libraries using parameters.ReferencedAssemblies.Add(string - name of the assembly).

CompilerResults results = provider.CompileAssemblyFromSource(parameters, code); //Compiling the string to an assembly

if (results.Errors.HasErrors)
{
    StringBuilder sb = new StringBuilder();

    foreach (CompilerError error in results.Errors)
    {
        sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
    }

    throw new InvalidOperationException(sb.ToString());
} //Error checking

Assembly assembly = results.CompiledAssembly;
Type program = assembly.GetType("First.Program"); //Getting the class object
MethodInfo main = program.GetMethod("Main"); //Getting the main method to invoke

main.Invoke(null, null); //Invoking the method. first null - because the method is static so there is no specific instance to run in, second null tells us there are no parameters.

Code snippet was taken from codeproject: http://www.codeproject.com/Tips/715891/Compiling-Csharp-Code-at-Runtime

Hope it helped!

Yotam Salmon
  • 2,400
  • 22
  • 36