0

So I'm using the MSScriptControl to run some javascript in my app and I want to be able to get some information about any errors the script may cause.

MSScriptControl.ScriptControlClass script = new MSScriptControl.ScriptControlClass();
try
{
    script.Language = "JScript";
    script.Timeout = 15000;
    script.Eval(Code);
}
catch (Exception ex)
{
    MSScriptControl.Error err = script.Error;
    ret = new Exception("Error on line: " + err.Line + ", Description: " + err.Description);
}

The code works fine on my development machine, a Windows 7 box, and gives me a line number with an error. So I happily publish and push it to the production machine which always tells me the error occurred at line 0 and no description is provided.

I tried going to http://www.microsoft.com/download/en/details.aspx?id=1949 to download the latest version but installing it had no effect. I also set the property Embed Interop Types to false as well as copying my own msscript.ocx file into the Windows 2008 server's system32 directory but neither of these attempts resolved anything.

Anyone have any recommendations?

Spencer Ruport
  • 34,865
  • 12
  • 85
  • 147
  • is there perhaps a difference in bitness - for example "build for AnyCPU", running ok on 32 Bit (dev machine), problem on Windows 2008 64 Bit machine ? – Yahia Mar 19 '12 at 20:09
  • Another point: according the link you provided neither Win7 not Win2K8 are supported platforms... which can be for example because of UAC or other changes which the control (from 2004!) is not implemented to support... – Yahia Mar 19 '12 at 20:11

2 Answers2

2

If you want to do it in all native c# without any 3rd party or "component" external dependencies use a CodeDomProvider with a tiny JScript bootstrap, like this:

private static readonly MethodInfo eval = CodeDomProvider
        .CreateProvider("JScript")
        .CompileAssemblyFromSource(new CompilerParameters(), "package e{class v{public static function e(e:String):Object{return eval(e);}}}")
        .CompiledAssembly
        .GetType("e.v")
        .GetMethod("e");

private static object JsEval(string jscript)
{
    try
    {
        return eval.Invoke(null, new[] { jscript });
    }
    catch (Exception ex)
    {
        return ex;
    }
}

that creates a JsEval(string) method that you can use anywhere in your code to "eval" a string as JavaScript (well JScript)... So calling:

MessageBox.Show("" + JsEval("2 + 2")); // 4
MessageBox.Show("" + JsEval("(function(){ return 3+7; })();")); // 10
MessageBox.Show("" + JsEval("function yay(a) { return a + 1; } yay(2);")); // 3

depending on your use you may not want to instantiate these members statically. if you want to manipulate complex objects you will need create a wrapper to reflectively extract data (or you could cast as the appropriate JScript counterpart, but I've never tried this as you'd have to include the JScript assemblies).

here is an example of a wrapper class that does everything JavaScript will let you do natively, adding anymore high level functionality would probably be cumbersome enough so that you'd be better off either extracting the members into a dictionary / hash table OR alternatively serializing and deserializing on the other end

private class JsObjectWrapper : IEnumerable
{
    public readonly object jsObject;
    private static PropertyInfo itemAccessor = null;
    private static MethodInfo getEnumerator = null;

    public JsObjectWrapper(object jsObject)
    {
        this.jsObject = jsObject;

        if (itemAccessor == null) 
        {
            itemAccessor = jsObject.GetType().GetProperty("Item", new Type[] { typeof(string) });
        }

        if (getEnumerator == null)
        {
            getEnumerator = jsObject.GetType().GetInterface("IEnumerable").GetMethod("GetEnumerator");
        }
    }

    public object this[string key]
    {
        get { return itemAccessor.GetValue(jsObject, new object[] { key }); }
        set { itemAccessor.SetValue(jsObject, value, new object[] { key }); } 
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return (IEnumerator)getEnumerator.Invoke(jsObject, null);
    }
}

you can see this in action by doing this:

var jsObj = JsEval("var x = { a:7, b:9 };");
var csObj = new JsObjectWrapper(jsObj);

MessageBox.Show("a: " + csObj["a"]);  // a: 7
MessageBox.Show("b: " + csObj["b"]);  // b: 9

csObj["yay!"] = 69;

foreach (string key in csObj)
{
    MessageBox.Show("" + key + ": " + csObj[key]); // "key": "value"
}

i personally have used code similar to this to great effect at one point or another and can vouch for it's availability and runnability inside a server environment.. I hope this helps -ck

ckozl
  • 6,751
  • 3
  • 33
  • 50
1

Regarding the problem you face just some thoughts:

  • according to the link you provided this control neither supports Windows 7 nor Windows 2008
  • it might be a security issue with regards to COM/UAC etc.
  • it might be a problem because of bitness if you compiled for AnyCPU, try using x86

Regarding possible alternatives:

Yahia
  • 69,653
  • 9
  • 115
  • 144
  • I've used JInt a little bit but within the first few minutes I found some discrepancies between it and the kind of javascript I can write in a browser. Most notably in regards to anonymous functions. It still might work but the JScript option is one I was unaware of so I'll look into that as well. – Spencer Ruport Mar 19 '12 at 22:41
  • @SpencerRuport Are you trying to mimic browser-like javascript execution (including DOM etc.) ? – Yahia Mar 20 '12 at 05:10