2

We had a 32 bits service that we are trying to migrate to 64 bits.

We were using Interop.MSScriptControl.dll to evaluate vb script written by users.

Since there is no 64 bits version of the MSScriptControl. I created a process that was called inside the service. Each time that we need to evaluate users scripts, we call the process. After trying this solution, I found it really slow.

I just discovered the CodeFluentRuntimeClient library that can evaluate vb script as well as JavaScript. However, the way it evaluates the script is complety different from MSScriptControl library.

I created a simple test program to evaluate the old vb script wrote by users.

public class VBScriptEvaluator
{
    public static dynamic Evaluate(string key, string script, IDictionary<string, object> parameterValuePair)
    {
        try
        {
            using (ScriptEngine engine = new ScriptEngine(ScriptEngine.VBScriptLanguage))
            {
                ParsedScript parsed = engine.Parse(string.Format(@"Function {0}()
{1}
End Function", key, script));

                if (script.Contains("NecUserProfile"))
                    engine.SetNamedItem("NecUserProfile", @"" + "ADMIN" + @""); //Hardcoded For now
                if (parameterValuePair != null && parameterValuePair.Count > 0)
                {
                    foreach (var para in parameterValuePair)
                        engine.SetNamedItem(para.Key, para.Value);
                }
                dynamic value = parsed.CallMethod(key);
                return (value != null) ? value.ToString() : string.Empty;
            }
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

If I use like this, it's working fine:

static void Main(string[] args)
{
    string key = "necGlobalValue";
    string script = @"necGlobalValue = ""ADMIN""";
    var result = VBScriptEvaluator.Evaluate(key, script, null); //ADMIN
}

Like this it works well too:

static void Main(string[] args)
{
    Dictionary<string, object> parameterValuePair = new Dictionary<string, object>();
    parameterValuePair.Add("ZINVOICE_MARGIN_0", 141615427.8);
    parameterValuePair.Add("ZINVOICE_AMTNOTLIN_0", 187260276.84);
    var script = @"If (ZINVOICE_AMTNOTLIN_0) <> 0 Then
         SERVER_FLD0000001 = Abs(ZINVOICE_MARGIN_0) / ZINVOICE_AMTNOTLIN_0
    else
        SERVER_FLD0000001 = 0
    End If";
    var key = "SERVER_FLD0000001";
    var result = VBScriptEvaluator.Evaluate(key, script, parameterValuePair);
}

In the previous library it was detecting automatically the type of the variables that will be evaluated. I can pass integers as string and it will work just fine.

If I replace the value of the dictionary like by using the ScripEngine, it will fail:

Dictionary<string, object> parameterValuePair = new Dictionary<string, object>();
parameterValuePair.Add("ZINVOICE_MARGIN_0", "141615427.8");
parameterValuePair.Add("ZINVOICE_AMTNOTLIN_0", "187260276.84");

Also, If I do this I'm not getting the user ADMIN.

string key = "necGlobalValue";
string script = @"necGlobalValue = ""NecUserProfile""";
var result = VBScriptEvaluator.Evaluate(key, script, null); // output NecUserProfile instead of ADMIN

And BTW I tried to give as much details, that's why the question is that long.

billybob
  • 2,859
  • 6
  • 35
  • 55
  • 1
    Although ScriptEngine is based on the same (old) underlying technology as MSScriptControl ("ActiveX Scripting"), they may differ on some points. However, it shouldn't be such a problem. And in your example, ScriptEngine works as expected. But named items are in general used for complex objects (like 'document' in HTML or 'Request' in ASP), not for integral objects like strings or integers. If you want to pass standard parameters, declare them in the function, and pass them with CallMethod. Also, for performance reason, you want to keep the parsed objects instead of parsing at each call. – Simon Mourier Sep 24 '16 at 05:39
  • That's what I did at the end and it's working great (use parameters instead of `SetNamedItem`). I just didn't had the time to answer my own question. And btw, I want to thank you for the library, it's awesome. – billybob Sep 25 '16 at 14:24
  • Does putting MSScriptControl into a 32 bit dllhost.exe allow it to work without any additional code? In theory it should. –  Sep 27 '16 at 00:23
  • Yeah, but I want to avoid doing that. It's really slow when it's wrapped through dllhost.exe. – billybob Sep 27 '16 at 15:53

1 Answers1

1

I made it work by passing the parameters to the function instead of using the SetNamedItem function.

public class VBScriptEvaluator
{
    public static dynamic Evaluate(string key, string script, IDictionary<string, object> parameterValuePair = null)
    {
        try
        {
            using (ScriptEngine engine = new ScriptEngine(ScriptEngine.VBScriptLanguage))
            {
                List<object> parameters = new List<object>() { "ADMIN" };
                string extraParameters = string.Empty;
                if (parameterValuePair != null && parameterValuePair.Count > 0)
                {
                    extraParameters = "," + string.Join(",", parameterValuePair.Select(e => e.Key));
                    foreach (var para in parameterValuePair)
                        parameters.Add(para.Value);
                }
                string parsedScript = string.Format(@"Function {0}(NecUserProfile {2})
{1}
End Function", key, script, extraParameters);
                ParsedScript parsed = engine.Parse(parsedScript);

                dynamic value = parsed.CallMethod(key, parameters.ToArray());
                return (value != null) ? value.ToString() : string.Empty;
            }
        }
        catch (Exception ex)
        {
            throw;
        }
    }
}

And here's how to use it:

Dictionary<string, object> parameterValuePair = new Dictionary<string, object>()
{
    {"Param1", 100.0 },
    {"Param2", 10.0}
};
var script = @"If (Param2) <> 0 Then
     result = Param1 + Param2
else
    result = 1 + 2
End If";
var key = "result";
var result = VBScriptEvaluator.Evaluate(key, script, parameterValuePair); // output 110
billybob
  • 2,859
  • 6
  • 35
  • 55