Explorations in C# - Attaching assemblies on the fly in Lychen*
Tags: csharp, todayilearned
Published: 2019-08-31 09:57:11

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](https://microsoft.github.io/ClearScript/Reference/html/P_Microsoft_ClearScript_Windows_WindowsScriptEngine_Script.htm), 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](https://dev.to/bugmagnet/action-delegate-gives-print-to-vbscript-46o8) 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
```csharp
v8.Script.print = (Action<object>)Console.WriteLine;
v8.Script.attach = (Action<string, string>)Attach;
```
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:
```csharp
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);
	}
}

```
With that compiled into the Lychen and LychenBASIC binaries, we now have an `attach` procedure that makes the following possible:
```vb
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
```
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)
```javascript
attach("C:\\programdata\\lychen\\newtonsoft.json.dll", "J");
var a = J.Newtonsoft.Json.JsonConvert.SerializeObject({a:1,b:2,c:{d:1}});
print(a);
```
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](https://www.geeksforgeeks.org/c-sharp-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.