DEV Community

Bruce Axtens
Bruce Axtens

Posted on • Updated on

Explorations in C# - Attaching assemblies on the fly in Lychen*

I have these scripting tools that nobody wants: Lychen and LychenBASIC. (That's not counting the others I have in my github repository that are DOS-specific.) I don't care, really, as these tools are just for me to experiment with.

So today I extended Lychen and LychenBASIC so that they can load assemblies during the running of a script. ClearScript makes this quite easy through their .Script property, which can be used to expose C#-side items directly into the V8, JScript and VBScript engines.

In my Action delegate gives print to VBScript article, I demonstrated the use of the .Script mechanism to add a print command to LychenBASIC. I have now done the same to Lychen (which targets V8) and could conceivably do the same to the in-house tool, RulesetRunner (which targets JScript).

So here it is adding print to Lychen

v8.Script.print = (Action<object>)Console.WriteLine;
v8.Script.attach = (Action<string, string>)Attach;
Enter fullscreen mode Exit fullscreen mode

So what's this attach about? Well, it's the hook to the function to load dotnet assemblies during the running of a script.

Attach is, according to the cast at the front, an Action that takes two strings. The first is the path to the dotnet dll we want to attach. The second is an optional symbol to which the dll's PropertyBag is attached. Ordinarily, one might get away with leaving that second parameter as an empty string, but occasionally we need something in there. I'll discuss why a little later.

So the implementation of Attach is as follows:

private static void Attach(string dllPath, string name = "")
{
    var htc = new HostTypeCollection();
    try
    {
        var assem = Assembly.LoadFrom(dllPath);
        htc.AddAssembly(assem);
        if (name.Length == 0)
        {
            name = assem.FullName.Split(',')[0];
        }
        v8.AddHostObject(name, htc);
        Console.WriteLine($"Attached {name}");
    }
    catch (FileNotFoundException fnfe)
    {
        Console.WriteLine(fnfe.Message);
    }
    catch (ReflectionTypeLoadException ctle)
    {
        Console.WriteLine(ctle.Message);
    }
}

Enter fullscreen mode Exit fullscreen mode

With that compiled into the Lychen and LychenBASIC binaries, we now have an attach procedure that makes the following possible:

attach "C:\programdata\lychen\newtonsoft.json.dll", "J"
a = J.Newtonsoft.Json.jsonconvert.deserializeobject("{a:1,b:2,c:{d:1}}")
print a.c.d
Enter fullscreen mode Exit fullscreen mode

VBScript ignores case, and after a while in VBScript, JavaScript's addiction to case-sensitivity really takes some getting used to.

Now something similar in Lychen (V8)

attach("C:\\programdata\\lychen\\newtonsoft.json.dll", "J");
var a = J.Newtonsoft.Json.JsonConvert.SerializeObject({a:1,b:2,c:{d:1}});
print(a);
Enter fullscreen mode Exit fullscreen mode

The reason why the above specifies a name to attach the Newtonsoft.Json's PropertyBag to is that if you don't, my ClearScript code will create a name derived from the assembly's FullName and in this case that name is "Newtonsoft.Json". This confuses the host. The "." implies that there's a Newtonsoft PropertyBag with a Json property. If you code Newtonsoft.Json.JsonConvert ... the host crashes. I should do something about this, but at this point the solution is just to provide an explicit attachment point.

Delegates offer a lot of scope for extension in dotnet applications. Expect to see more about this subject as I continue to experiment on Lychen and LychenBASIC.

Discussion (0)