Skip to content

Integrate with .net program

Jing Lu edited this page Jun 19, 2013 · 82 revisions

ReoScript provides the ability to execute JavaScript-like script in your application. It usually can be used in the following cases:

  • Application with script execution

    Software with Macro or Script execution is required to be available for end-user. (like VBA in Excel)

  • Script to be ran under a native library that is provided by your Application

    You have a library written in .NET Application, and you want to your end-user can use them by writing script.

  • Console Script Execution

    Some batch process program, Daily build, File publish or something else. You are able to make extensions for ReoScript in .NET Application, and writing and running the script in console.

Setup ReoScript

  1. Download ReoScript binary or build source file. Add the following DLLs into reference list of your project.

     Antlr3.Runtime.dll
     Unvell.ReoScript.dll
    
  2. Import the following namespace

     using Unvell.ReoScript;
    
  3. Create ScriptRunningMachine and keep the instance

     ScriptRunningMachine srm = new ScriptRunningMachine();
    
  4. Run script from different source

    • Run script in text

        srm.Run("console.log('hello world');");
      
    • Run script from internal resource

        using(MemoryStream ms = new MemoryStream(Resources.script)) {
            srm.Load(ms);
        }
      
    • Run script from specified file

        srm.Load("C:\\scripts\\main.rs");
      
    • Run script from specified stream (could be a network, or unzipped stream etc.)

        srm.Load(new NetworkStream(...));
      

    See ScriptRunningMachine.

Practices to integrate ReoScript into your application

There are two practices to integrate ReoScript into your application.

  1. Manual Mode (writing wrapper objects, properties or methods)
  2. Automatic Mode (using DirectAccess)

Manual Mode (writing wrapper objects, properties or methods)

To connect the two worlds between script and .NET, the wrapper objects, properties and methods may be necessary to write.

              +-----------------------------------------------------+
              |                       Script                        |
              |    +-------------------------------------------+    |
              +--- |  Wrapper objects, properties and methods  | ---+        <--- You may have to do
              |    +-------------------------------------------+    |
              |                  .NET Application                   |
              +-----------------------------------------------------+

I called this as 'Manual Mode' because anything you want to provide for script are all controllable precisely. Your end-user can only uses the functions that you want to be used. This practice is more safer and stable than Automatic Mode, it is also recommended usage if you are planning to make script execution is available to your end-user.

To write wrapper objects, properties and methods, the following classes you may need to use in .NET Application.

Global Object

Once the script execution context(ScriptRunningMachine) is created, ReoScript keeps an global object until the context is destroyed. Global object can be understood as a root object in the context, all objects to be used in script should be added into global object firstly. Once the object(even a function) has been added into global object, it will be available to all scopes of functions in a context. (See GlobalObject)

Add Own Customize Function

If you want to add a function for script, you may create a NativeFunctionObject instance and add it into global object. For example:

ScriptRunningMachine srm = new ScriptRunningMachine();
srm["myfunc"] = new NativeFunctionObject("myfunc", (ctx, owner, args) => {
    Console.WriteLine('myfunc called!');
});

Now use this function in script:

myfunc();

The text will be printed out in console:

myfunc called!

In ReoScript, everything even a function are objects, and one object can be added into another object as its property by a given name. For example:

var obj = new Object();         // create an object instance
var func = function() { };      // create an anonymous function
obj.myfunc = func;              // set func into obj as its property (now it more likes a method)

See NativeFunctionObject.

Automatic Mode (using DirectAccess)

All the objects, types, properties and methods which requested to use in script are all mapped to .NET Runtime automatically by ReoScript engine. In this Automatic Mode, the intermediate wrapper objects is unnecessary.

              +-----------------------------------------------------+
              |                       Script                        |
              |    +-------------------------------------------+    |
              +--- |     .NET Reflection / ReoScript Engine    | ---+        <--- You don't have to do
              |    +-------------------------------------------+    |
              |                  .NET Application                   |
              +-----------------------------------------------------+

DirectAccess allows script to access the property of .NET object by .NET Reflection Technology. Although this feature can be a very simple way to integrate ReoScript, but it may also became a potential risk at script run-time. Unless you uses script inside your application, please consider about the Manual Mode as 'My Suggestion'.

(See more DirectAccess)

My Suggestion

Although ReoScript provides DirectAccess mechanism to access .NET object directly, it is recommended that writing wrapper objects, properties and methods if you are planning to make script execution to be available for your end-user.

For example, assuming there is a .NET object with one method and property:

public class Application
{
    public void Start() { ... }
    public string Name { get; set; }
}

With DirectAccess, you would be able to call 'Start' method from script directly:

var app = new Application();
app.start();

Or access its property like:

app.name = 'stuff';

But if you reformed your .NET Application, like renaming method, the script will be unavailable. For usability it is recommended that writing wrapper method for your application and script.

  1. Create wrapper object - create a class inheriting from ObjectValue, keep an instance of original object. And add methods that you want to provide for script.

     public class ApplicationObject : ObjectValue 
     {
         public Application Application { get; set; }
    
         public ApplicationObject() {
             // keep original instance
             this.Application = new Application();
    
             // add wrapper method
             this["start"] = new NativeFunctionObject("start", (ctx, owner, args)=> {
                 this.Application.Start();
             });
         }
     }
    

    About function extension please see NativeFunctionObject.

  2. Import this wrapper object type into script context

     // prepare srm
     ScriptRunningMachine srm = new ScriptRunningMachine();
    
     // import .Net type into srm
     srm.ImportType(typeof(ApplicationObject), "Application");
    

Then the Application class will be available to script.

var app = new Application();
app.start();

Enable or Disable Built-in Features

ReoScript supports many JavaScript features like showing message box using 'alert' function, perform an async-call using setTimeout or setInterval function. Sometimes you may want to decide what features can be available to your end-user, you are able to enable or disable ReoScript's built-in features by setting CoreFeatures enum. (See CoreFeatures)

The following example shows how to disable the built-in 'alert' function. There are two methods available as blow:

  1. Modify CoreFeatures, minus the 'Alert'.

     CoreFeatures features = CoreFeatures.StandardFeatures;
     features &= ~(CoreFeatures.Alert);
    
     ScriptRunningMachine srm = new ScriptRunningMachine(features);
     srm.Run("alert('hello,world')");
    

    Then try to run script containing 'alert' function call, the exception will be thrown.

     Function is not defined: alert
    
  2. Override the built-in 'alert' function

    Set global variable 'alert' to a new function that overriding the built-in one.

     ScriptRunningMachine srm = new ScriptRunningMachine();
     srm.Run("alert = function() { };");
    

    Then try to run the script:

     srm.Run("alert('hello,world')");
    

    Nothing happened.

    Overriding a built-in function is more safer because no exception will be thrown, and you would able to do something instead of the built-in feature. The following code overrides the 'alert' function and forwards message to [Standard I/O interface](Standard IO Interface).

     srm.Run("alert = function(msg) { console.log(msg); };");