VBParser.RegisterCode() for ScriptRun?

I have a expression text field where i have a Parser attached for Intellisense and Autocompletion. I managed to update intellisense with a dynamic enum class by making making an enum string and passing it in the Parser.RegisterCode.

Because i want it to be dynamic i am having problems making the enum visible for my ScriptRun component, it works with static assemblies but i need something more dynamic. Is there something similar to the RegisterCode function for the ScriptRun component?

I have tried investigating the possibility of making a dynamic assembly which gets referenced through ScriptRun.ScriptSource.Add() and then recompiled so both my intellisense options are updated, but also the scriptruns understanding of these enums when evaluated in an expression.

But dotnet does not seems to like the idea of generating assemblies as there were problems making metadatareferences to an in-memory assembly. And i therefore wanted an answer to my initial question. Can i register my dynamic enum class (VB) for the ScriptRun component without having to reference an assembly?

Hi,

I assume you’re using scriptRun.FromExpression to evaluate expression, right?

If so, the easiest way to add declarations to the expression code would be to modify ScriptConsts.VisualBasicGlobalCode static field (by adding an enum definition to it).
Please let me know if it works for you.

Kind regards,
Dmitry

I have tried simplifying my code to see if i can reference the enums but it still wont recognize it. Is it the wrong approach?

string sourcecode = cm.GenerateEnum(scriptRun1, vbParser1);

Public string GenerateEnum(ScriptRun sr, VbParser vbParser1)
{
  StringBuilder sb = new();
  enumString.Add("hello1");
  enumString.Add("hello2");
  enumString.Add("hello3");
  
  sb.AppendLine("'This file is auto-generated for holding component enums, dont edit this'");
  
  sb.AppendLine("Public Module ScriptGlobalClass");
  sb.AppendLine("\tPublic Enum myEnum");
  
  foreach (string line in enumString)
  {
      sb.AppendLine("'''<summary>Hello</summary>");
      sb.AppendLine("\t\t" + line);
  }
  
  sb.AppendLine("\tEnd Enum");
  sb.AppendLine("End Module");
  
  ScriptConsts.VisualBasicGlobalCode = sb.ToString();
  
  return sb.ToString();
}

//Should output "1"
scriptRun1.FromExpression("myEnum.hello1 + myEnum.hello2")

if (!scriptRun1.Compile())
{
    //It fails here unless i use ScriptConsts.VisualBasicExpressionCode
]

//Always returns null
var obj = scriptRun1.EvaluateExpression("myEnum.hello1 + myEnum.hello2");



Hi,

Sorry, I’ve mistakenly mentioned the wrong constant:
Here’s an updated code snippet:

ScriptConsts.VisualBasicExpressionCode = GenerateEnum() + "\r\n" + ScriptConsts.VisualBasicExpressionCode;

  public string GenerateEnum()
  {
      StringBuilder sb = new StringBuilder();
      IList<string> enumString = new List<string>();
      enumString.Add("hello1");
      enumString.Add("hello2");
      enumString.Add("hello3");

      sb.AppendLine("'This file is auto-generated for holding component enums, dont edit this'");
      sb.AppendLine("\tPublic Enum myEnum");

      foreach (string line in enumString)
      {
          sb.AppendLine("'''<summary>Hello</summary>");
          sb.AppendLine("\t\t" + line);
      }

      sb.AppendLine("\tEnd Enum");
      return sb.ToString();
  }

Please let me know if it works now, on my tests, it evaluates myEnum.hello1 just fine

Yes i also made it work using VisualBasicExpressionCode and adding the Enum field above the ScriptEvaluator.

I ended up going a different route using the ScriptHost.GenerateModulesOnDisk which gave me the ability to add the reference to ScriptSource.

sr.ScriptSource.FromScriptCode(sb.ToString());
sr.ScriptHost.AssemblyFilenName = Guid.NewGuid().ToString("N");
sr.ScriptHost.GenerateModulesOnDisk = true;

sr.ScriptSource.References.Add(sr.ScriptHost.ExecutableModulePath);

if (!sr.Compile())
{
    MessageBox.Show(string.Join(' ', sr.ScriptHost.CompilerErrors));
}

Now i can access my enum class as well as custom functions if needed.

1 Like

I am back.

I have a vb script which i register for my vbParser for intellisense (Works good). And i then generate a dll and add the reference to my ScriptRun.Scriptsource.

When i test to see if i can evaluate the content of my vb script via EvaluateExpression. It works fine with input like:

Math.Sin(1)
myEnum.hello1 (returns the index 0, which is good)
myMethods.TestMethod() → Fails

My EvaluateExpression does not work with my function which is in the same dll as myEnum.

sb.AppendLine("Namespace TestSpace");
sb.AppendLine("Public Enum myComponents");

foreach (string line in enumString)
{
    sb.AppendLine("'''<summary>Hello</summary>");
    sb.AppendLine("\t" + line);
}

sb.AppendLine("End Enum");

sb.AppendLine("NotInheritable Class testClass");
sb.AppendLine("\tPrivate Sub New()");
sb.AppendLine("\tEnd Sub");
sb.AppendLine("\tPublic Shared Function TestMethod() As String");
sb.AppendLine("\t\tReturn \"Hello World\"");
sb.AppendLine("\tEnd Function");
sb.AppendLine("End Class");
sb.AppendLine("End Namespace");

But ScriptRun still seems to have a reference because if i type

scriptRun.RunMethod(“TestMethod”)

Then it runs fine and returns the value as i was expecting.

So why am i not able to call the TestMethod through EvaluateExpression? Maybe i thought that was just how EvaluateExpression works but if i were to put in Math.Sin(1) into EvaluateExpression as a string, then it knows how to run the method and return the value as well.

Hi,

Sorry for the delay in reply.

On my tests, the following works in EvaluateExpression:

TestSpace.myComponents.hello1
TestSpace.testClass.TestMethod()

If you’d like TestMethod to be evaluated without namespace/class qualifier, it’s the best to declare it as part of module and without namespace like this:

  sb.AppendLine("Public Enum myComponents");

  foreach (string line in enumString)
  {
      sb.AppendLine("'''<summary>Hello</summary>");
      sb.AppendLine("\t" + line);
  }

  sb.AppendLine("End Enum");

  sb.AppendLine("Module testClass");
  sb.AppendLine("\tPublic Function TestMethod() As String");
  sb.AppendLine("\t\tReturn \"Hello World\"");
  sb.AppendLine("\tEnd Function");
  sb.AppendLine("End Module");
  return sb.ToString();

Kind regards,
Dmitry

Thanks for the reply, unfortunately it does not seem to work on my end, i am assuming it has something to do with the referencing of my generated dll?

 sb.AppendLine("Public Enum myComponents");

  foreach (string line in enumString)
  {
      sb.AppendLine("'''<summary>Hello</summary>");
      sb.AppendLine("\t" + line);
  }

  sb.AppendLine("End Enum");

  sb.AppendLine("Module testClass");
  sb.AppendLine("\tPublic Function TestMethod() As String");
  sb.AppendLine("\t\tReturn \"Hello World\"");
  sb.AppendLine("\tEnd Function");
  sb.AppendLine("End Module");

sr.ScriptSource.FromScriptCode(sb.ToString();

sr.ScriptHost.AssemblyFileName = Guid.NewGuid().ToString("N");
sr.ScriptHost.GenerateModulesOnDisk = true;
sr.ScriptSource.References.Add(sr.ScriptHost.ExecutableModulePath);

sr.Compile()..

var obj = sr.EvaluateExpression("testClass.TestMethod()");
/*obj is NULL*/

Hi,

I’ve uploaded modified ExpressionEvaluation demo here:

It shows how to use enum and method defined in the script, and also enum defined in the application itself.

Please note that EvaluateExpression calls FromExpression method, which resets ScriptSource (and clears all the references).
Instead I’m using the following code:

        scriptRun.ScriptSource.FromExpression(tbExpression.Text);
        scriptRun.ScriptSource.References.Add(Assembly.GetExecutingAssembly().Location);
        object obj = scriptRun.RunMethod(ScriptConsts.EvaluateExpressionMethod);

Which does exactly what EvaluateExpressin does (but without removing references).

Also, adding reference to the script assembly does not seem to be a way to go:
sr.ScriptSource.References.Add(sr.ScriptHost.ExecutableModulePath);
Firstly, it will not compile, as during compilation this reference is not yet existing. And even if it was, it effectively would reference itself.

If it still does not work for you, could you please send us demo project where we can reproduce and fix the problem?

Kind regards,
Dmitry

Hello,

Thank you for the reply!

I managed to make it work through your example. My initial idea was to generate a global .dll which could be updated at runtime. And after that, be referenced by different scriptrun components. But it seems the ScriptConsts can do something similar if you update the VisualBasicExpressionCode when you need to add elements to the enum list for example.

Hi,

Great to hear you’ve got it working. Please be aware that generating a global dll that can be updated at run-time might not work, as once this dll is loaded into the application’s process, it can not be unloaded (unless separate application domain is used).

Kind regards,
Dmitry

That is what i figured out, you could generate a new and updated dll with another name, and refernce that, but it didn’t seem viable. Thank you for helping out!

1 Like