Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Add JavaScript and VBScript to Your .NET Apps

4.84/5 (6 votes)
14 Jan 2020MIT3 min read 10.5K   140  
Easily add scripting support to your apps using Microsoft's active script technology

Scripting

Introduction

I wanted to take a break from building parsers and such and do something a little different, so here's a handy little library I've whipped up to add scripting support to your .NET applications.

Occasionally, I've found the need to execute JavaScript in my apps, sometimes for scraping, sometimes just to have JS scripting support (VBScript works too) and at least once for legacy ASP emulation.

Microsoft provides COM interfaces to use these but using them from .NET is ugly.

Luckily, this little library makes it easy.

Conceptualizing this Mess

Microsoft provides an extensible scripting framework they use in legacy ASP pages and in Internet Explorer. By default, Windows ships with VBScript and JavaScript (JScript) but 3rd parties have added others.

These script engines are COM visible and expose their scripts as COM objects that can call your code or be called from your code. The only trouble is the interfaces are messy.

What I've done is wrapped these interfaces in two easy to use objects, ScriptHost and ScriptEngine.

Using this Mess

First of all, script engines cannot exist autonomously. They need a place to put down roots. That's what ScriptHost is for. ScriptHost manages script engine creation, and lifetime, as well as providing various services to script engines, like error reporting and object retrieval. Before we can create any script engines, we must create one of these lil guys. Remember to dispose of it when you're done. Closing a script host closes all engines that were created by it as well.

Next, we have ScriptEngine which you can get via ScriptHost.Create(). These represent autonomous regions of script code. For an idea of what this means, know that each <script> block in a web page would correspond to one of these, and the page itself would be the ScriptHost. Pass a language to Create() to get a script engine in your desired language. There's a "fast" JS engine you can access by passing the ChakraJS constant but I haven't tested it and I'm not sure how fast it actually is.

With the ScriptEngine, we can Evaluate() expressions, Run() statements, or AddCode() to the script block. We can also add objects to the Globals dictionary which can then be accessed by name in your script code.

Hopefully, the demo code will make all things clear:

C#
Console.WriteLine("Creating script host");
using (var host = new ScriptHost())
{
    // create a JavaScript engine
    Console.WriteLine("Creating javascript engine");
    var engine = host.Create("javascript"); // you can use the ChakraJS const here 
                                            // for IE9+'s fast engine
                                            // evaluate an expression
    Console.WriteLine("Evaluate 2+2: ");
    Console.WriteLine(engine.Evaluate("2+2"));
                
    // add some code to the current script
    Console.WriteLine("Add code: var i = 1;");
    engine.AddCode("var i = 1;");
                
    // get the object for the script
    Console.Write("i = ");
    dynamic obj = engine.Script;
                
    // print var i
    Console.WriteLine(obj.i);
                
    // add an app object to the script
    Console.WriteLine("Add global app object");
    engine.Globals.Add("app", new AppObject());
                
    // let the script call the app object
    Console.Write("Run \"app.writeLine('Hello World!');\": ");
    engine.Run("app.writeLine('Hello World!');");
}

Note that we've added AppObject to the script engine above. Here's the definition for it:

C#
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDispatch /*.AutoDual */)]  // automatically 
                                 // implements com interface IDispatch ( and IUnknown )
public class AppObject
{
    /// <summary>
    /// allows scripts to write to the console
    /// </summary>
    public void writeLine(string s) { Console.WriteLine(s); }
}

Note that we've added a couple of attributes from System.Runtime.InteropServices. This is necessary so that .NET creates the infrastructure needed to let the script communicate with the object via COM/OLE Automation. If you don't add these, your script will not be able to communicate with your object and you'll get an error.

Also note, weirdly, despite each script engine having its own collection of globals, all globals within a script site share the same naming container. That means that only one global per name is honored regardless of what script engine it is created under. In the case where two script engines have an item with the same name, only the first instance will be associated with that name. It's okay to add an object to two or more script engines under the same name. I didn't design it this way - it's part of the design of the Microsoft Active Script framework.

That covers it. It should be simple enough to take it from here and add JavaScript and VBScript scripting to your apps. Have fun!

History

  • 14th January, 2020 - Initial submission

License

This article, along with any associated source code and files, is licensed under The MIT License