Share via



September 2014

Volume 29 Number 9

Cutting Edge : A Look at ClearScript

Dino Esposito

Dino EspositoYears ago, I was fascinated by the prospect of hosting the entire VBScript engine of Active Server pages within a Visual Basic application. I could create an astonishing proof of concept for a company willing to sell editorial content on CD that would reuse existing Active Server pages content just outside local or remote Web servers.

It was the late 1990s. There was no Microsoft .NET Framework. There was no HTML5. Only a few of us were actively exploring the depths of Dynamic HTML, yet hosting the scripting engine couldn’t have been easier. I had to reference an ActiveX control, publish my ActiveX objects within the scripting environment and I was ready to go.

More recently, a customer asked me about the most effective way to extract text files from SQL Server queries. That question was outside the usual range of topics I handle, so I was tempted to answer with something like, “Sorry, I don’t know.” However, I knew this customer was strong in database operations. I suspected there was more background behind the question.

The customer was regularly producing plain text files (mostly CSV and XML files) out of content stored in database tables within an instance of SQL Server. This annoyed its database staff, as requests were coming mostly from business with the usual urgency of business issues. There was no recurrent logic that could help create repeatable routines—at least in a SQL Server environment.

Ultimately, the customer was looking for a tool business people could program using easy script languages such as VBScript. The users needed controlled access to the databases for read-only purposes. Needless to say, the tool had to offer users a chance to easily create text files. This reminded me of the happy days of ActiveX and VBScript. I almost had a wave of regret when I heard about a relatively new library named ClearScript (clearscript.codeplex.com).

Integrate ClearScript into Windows Presentation Foundation

ClearScript lets you add scripting capabilities to a .NET application (as long as it uses the .NET Framework 4 or higher). ClearScript supports VBScript, JavaScript and V8. V8 is an open source JavaScript engine created by Google and integrated with Chrome. V8 has a high-performance JavaScript engine and fits well within multi-threading and asynchronous operation scenarios.

The net effect of adding ClearScript to a .NET application is you can pass JavaScript or VBScript expressions to the engine and they’ll be processed and run. Interestingly, you aren’t limited to using plain script objects such as arrays, JSON objects and primitive types. You can integrate external JavaScript libraries and script-managed .NET objects.

Once you’ve integrated ClearScript into an application, all that remains is to let the library know about the objects it can script. This means you publish your own objects in the context of ClearScript and let authorized users load and run existing scripts or write new ones.

If you want to add a layer of customization and let the user add pieces of his own logic without incurring the costs of change requests, then ClearScript is necessary, but it might not be sufficient. ClearScript is just one piece of the jigsaw puzzle. You might want to provide a way for the user to manage his own scripts. Also, you should create some ad hoc objects that simplify common tasks like creating files.

Here’s what I did to help a customer generate text and XML reports from a bunch of services exposed through a Web API back end. The main functional requirement was to let users create text files. For the proof of concept, I needed a shell application to host ClearScript. I opted for a Windows Presentation Foundation (WPF) application with a textbox to manually enter script code. Successive iterations added support for a default input folder and a UI to open/import existing script files. Figure 1 shows the WPF sample application in action.

A Sample Windows Presentation Foundation Application Hosting the ClearScript Engine
Figure 1 A Sample Windows Presentation Foundation Application Hosting the ClearScript Engine

Again, ClearScript is an open source project you can reference directly in your project by linking assemblies. You can also go through third-party NuGet packages, as shown in Figure 2.

You Can Install ClearScript via NuGet
Figure 2 You Can Install ClearScript via NuGet

Initialize ClearScript

You’ll have to do some work before you can use the scripting engine programmatically. But at the end of the day, once it’s completely set up, the code in Figure 3 is all you need to trigger script code execution.

Figure 3 Code to Trigger Script Code

public void Confirm()
{
  try
  {
    SonoraConsole.ScriptEngine.Execute(Command);
    OutputText = SonoraConsole.Output.ToString();
  }
  catch(Exception e)
  {
    OutputText = e.Message;
  }
}

The Confirm method belongs to the presenter class supporting the main view of the sample application. Trigger the method by any click on the Run button (visible in Figure 1). The SonoraConsole class you see referenced in the listing is just my own wrapper around the core classes of the ClearScript library.

Initializing the ClearScript engine takes place as the application starts up and is bound to the Startup event of the XAML Application class:

public partial class App : Application
{
  void Application_Startup(Object sender, StartupEventArgs e)
  {
    SonoraConsole.Initialize();
  }
}

The initialization can be as complex and sophisticated as you’d like, but it at least has to initialize the script engine of your selected language. You have to make the instance of the script engine available to the other parts of the application. Here’s one possible approach:

public class SonoraConsole
{
   public static void Initialize()
   {
     ScriptEngine = new VBScriptEngine()
   }
   public static ScriptEngine ScriptEngine { get; private set; }
   ...
}

You might want to read from the configuration file which scripting language you should enable within the application. This is one possible configuration schema:

<appSettings>
  <add key="language" value="vb" />
</appSettings>

Once you have an instance of your chosen scripting engine ready, you can run any valid JavaScript (or VBScript) code. There isn’t much you can do in a real-world scenario until you’re armed with these basic capabilities.

Add Scriptable Objects

All ClearScript engines expose a programmable interface through which you can add scriptable objects to the runtime environment. In particular, you use the method AddHostObject, like so:

ScriptEngine.AddHostObject("out", new SonoraOutput(settings));
ScriptEngine.AddHostObject("xml", new XmlFacade());

This method requires two parameters. The first parameter is the public name scripters will use to reference the object being published. The second parameter is just the object instance. Looking at the previous code snippet, in any JavaScript or VBScript you can use the name “out” to invoke any of the public methods available on the SonoraOutput interface. Here’s an example in JavaScript that references what’s shown in Figure 1:

var x = 4;
out.print(x + 1);

As you may know, it’s a common practice in JavaScript to name members according to the camelCase convention. In .NET programming, the PascalCase convention is more common and also recommended. In my implementation of the SonoraOutput class, I deliberately opted to follow the JavaScript convention and called the method print instead of Print, as it would be the case in plain C# programming.

Based on my experience, there’s not much more you need to know and understand to get started on ClearScript. Most of the time, you can configure a ClearScript environment within a host application with the primary purpose of making available application-specific objects. More often than not, these are tailor-made objects wrapped around existing business objects and made nicer to use from within a script environment.

The primary users of a ClearScript environment are usually not full-time developers. They’re most likely people with some soft development skills that would find it unnecessarily complex and annoying to deal with the full details of .NET classes. ClearScript lets you expose large chunks of the .NET Framework directly to JavaScript and VBScript. I opted to have tailor-made objects designed for extreme simplicity. Here’s how you can publish in ClearScript a type instead of an object:

ScriptEngine.AddHostType("dt", typeof(DateTime));

When you reference a type, you’re giving users the power to programmatically create instances of that type. For example, the previous line of code adds the power of the .NET DateTime object to the scripting environment. Now, the following JavaScript code becomes possible:

var date = new dt(1998, 5, 20);
date = date.AddDays(1000);
out.print(date)

From within JavaScript code, you’re taking advantage of the full power of methods such as AddDays and AddHours. What if you want to get the difference between two dates? You can do something like this:

var date1 = new dt(1998, 5, 20);
var date2 = date1.AddDays(1000);
var span = date2.Subtract(date1);
out.print(span.Days)

The TimeSpan object is handled correctly and the expression span.Days just returns 1000. This is due to the dynamic nature of the JavaScript language, which dynamically determines the object named “span” exposes a member named Days. If you want to create a TimeSpan instance instead, you need to first let the engine know it’s there.

To avoid exposing a million different types, ClearScript lets you host an entire assembly. This is one possible approach:

ScriptEngine.AddHostObject("dotnet",
  new HostTypeCollection("mscorlib", "System.Core"));

The keyword dotnet now becomes the key to access any types and static members within mscorlib and System.Core. Creating a new date object takes a bit longer, but in return you can explicitly work with TimeSpan objects:

var date1 = new dotnet.System.DateTime(1998, 5, 20);
var ts1 = new dotnet.System.TimeSpan(24, 0, 0);
var ts2 = ts1.Add(new dotnet.System.TimeSpan(24, 0, 0));
out.print(ts2.Days);

The JavaScript code snippet prints out the number 2, resulting from the sum of two distinct TimeSpan objects each counting for 24 hours. One thing that ClearScript doesn’t work well with is operator overloading. That just doesn’t exist. This means to sum up dates or timespans, you have to use methods such as Add or Subtract. It also supports reflection.

Generate the Output

The tool you see in Figure 1 must be able to display some results to the user. By default, the SonoraOutput object added to the ClearScript engine just maintains an internal StringWriter object. All text processed by the print method is actually written to the underlying writer. The content of the writer is exposed to the outside world via the SonoraConsole class. This class is the sole point of contact between the ClearScript engine and the host application. The host application presenter just returns the content of the string writer through a property. That property is then bound to a TextBlock in the WPF UI. The method print writes to the UI via the string writer. The method clr clears the buffer and the UI.

Save to a Text File

My customer only needed to create text files, mostly CSV files. This is relatively easy to achieve. All I did was create a file method and pass it some text content directly. I could also let it grab anything already printed to the screen and saved in the internal buffer. The most problematic aspect to deal with when it comes to files is naming and location. To make scripting really fast, it has to be trivially easy to create and retrieve files. I managed to have two default folders—one for input and one for output. I also assume that all files are TXT. If no file name is specified, files assume a default name.

These assumptions are possibly too restrictive for some scenarios, but my project was simply a proof of concept for a tool to produce files, not store them. As Figure 4 demonstrates, I could easily wrap the XmlWriter object into a nice component and create an XML file via script.

Create an XML File via Script
Figure 4 Create an XML File via Script

Wrapping Up

What’s the point of creating an XML file via script? It’s actually the same as having script capabilities in some enterprise applications. You need script because you want to automate tasks. In some cases, creating ad hoc text or XML files is just what you need. Perhaps you can run queries on SQL Server and import them to CSV, but that requires administrative access to the production database and, more important, the appropriate skills. I would have trouble myself using xp_cmdshell to grab text files out of SQL Server queries. For a developer, it might not be difficult to arrange some ad hoc, easy-to-use objects that are ready-made for scripting.

My customer loved this idea as much as I loved using ClearScript. He asked me to add a lot more objects to the dynamic environment. I ended up adding a layer of inversion of control to configure objects to be loaded at startup. He’s also considering ClickOnce deployment across the company for when new tools are released.


Dino Esposito is the co-author of “Microsoft .NET: Architecting Mobile Applications Solutions for the Enterprise” (Microsoft Press, 2014) and the upcoming “Programming ASP.NET MVC 5” (Microsoft Press, 2014). A technical evangelist for the .NET Framework and Android platforms at JetBrains and frequent speaker at industry events worldwide, Esposito shares his vision of software at software2cents.wordpress.com and on Twitter at twitter.com/despos.

Thanks to the following technical experts for reviewing this article: Microsoft ClearScript Team