Tip: How to use Late Binding

Late Binding is a mechanism to invoke methods from objects at the runtime of a program. We know this calling method mainly from the Component Object Method (COM) environment. Based on my approach to using the Win32 API I made an extension so that late binding can also be realized with the VisualBasic dotNET programming language.

Here the code of the Invoke Code activity. It is almost the same as the Win32 API usage, but here the language (C#, VB dotNET or JScript) can be selected.

//-UiPath Begin---------------------------------------------------------

CompilerResults CompResult;

//-Setting of the compiler version--------------------------------------
Dictionary<string, string> providerOptions = new Dictionary<string, string> {
  {"CompilerVersion", "v4.0"}
};

//-Setting of the language----------------------------------------------
CodeDomProvider CodeProvider = null;
switch(Language.ToUpper()) {
  case "CS":
    CodeProvider = new CSharpCodeProvider(providerOptions);
    break;
  case "VB":
    CodeProvider = new VBCodeProvider(providerOptions);
    break;
  case "JS":
    CodeProvider = new JScriptCodeProvider();
    break;
  default:
    ErrorMessage = "Language unknown";
    Console.WriteLine("Error: " + ErrorMessage);
    return;
}

//-Setting of the compiler parameters-----------------------------------
CompilerParameters compilerParams = new CompilerParameters();
compilerParams.GenerateInMemory = true;
compilerParams.GenerateExecutable = false;
compilerParams.IncludeDebugInformation = true;
compilerParams.WarningLevel = 3;
string[] assemblies = Assemblies.Split(new char[] { ',' } );
foreach(string Assembly in assemblies) {
  compilerParams.ReferencedAssemblies.Add(Assembly);
}

//-Checking the code----------------------------------------------------
if(System.String.IsNullOrEmpty(Code.Trim())) {
  ErrorMessage = "Code missed";
  return;
}
//-Compiling of the code------------------------------------------------
CompResult = CodeProvider.CompileAssemblyFromSource(compilerParams, Code);
//-Checking if compiling code exists------------------------------------
if(CompResult == null) {
  ErrorMessage = "Compiling code missed";
  return;
}
StringCollection CompOutput = CompResult.Output;
if(CompResult.Errors.Count != 0) {
  ErrorMessage = "Compilation failed" + Environment.NewLine;
  foreach(string CompOutputLine in CompOutput) {
    if(!string.IsNullOrEmpty(CompOutputLine)) {
      if(!CompOutputLine.Contains("This compiler is provided as part of the Microsoft (R) .NET Framework") &&
        !CompOutputLine.Contains("Copyright (C) Microsoft Corporation. All rights reserved.")) {
        ErrorMessage += CompOutputLine + Environment.NewLine;
      }
    }
  }
  return;
}

//-Read constructor parameters and add it to a list---------------------
object[] ConstructorParams = null;
if(!System.String.IsNullOrEmpty(ConstructorParameters)) {
  string[] partsConstructorParams = ConstructorParameters.Split(new char[] { ',' });
  ConstructorParams = new List<string>(partsConstructorParams).ToArray();
}

//-Read method parameters and add it to a list--------------------------
object[] MethodParams = null;
if(!System.String.IsNullOrEmpty(MethodParameters)) {
  string[] partsMethodParams = MethodParameters.Split(new char[] { ',' });
  MethodParams = new List<string>(partsMethodParams).ToArray();
}

//-Checking the instance------------------------------------------------
if(System.String.IsNullOrEmpty(Instance.Trim())) {
  ErrorMessage = "Instance missed";
  return;
}
//-Create instance and invoke method------------------------------------
try {
  object oInstance = CompResult.CompiledAssembly.CreateInstance(
    Instance,
    false,
    BindingFlags.Instance | BindingFlags.Public | BindingFlags.CreateInstance,
    null, // use default binder
    ConstructorParams,
    null, // use CultureInfo from current thread,
    null
  );
  if(!System.String.IsNullOrEmpty(Method)) {
    MethodInfo MethInfo = oInstance.GetType().GetMethod(Method);
    object oResult = MethInfo.Invoke(oInstance, MethodParams);
    if(oResult != null) {
      Result = oResult.ToString();
    }
  }
} catch (System.Exception ex) {
  ErrorMessage = ex.Message + Environment.NewLine + ex.StackTrace;
}

//-UiPath End-----------------------------------------------------------

Here a tiny example to use late binding.

Here the invoked code. In this example I use the MSScriptControl to execute a VBScript code, only to show a message box.

@"
Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic

Class Test

  Sub Main()
    Dim VBS As Object
    VBS = CreateObject(""MSScriptControl.ScriptControl"")
    VBS.Language = ""VBScript""
    VBS.AllowUI = True
    VBS.ExecuteStatement(""MsgBox """"Hello World via Late Binding in UiPath from VBScript!"""", VBOkOnly, """"ExecuteStatement"""""")
  End Sub

End Class
"

And it works as expected.

image

On this way late binding can be used in the context of UiPath, without additional packages.

Here my example project:
LateBinding.zip (34.5 KB)

And also it is possible to use JScript. :smiley:

@"
import System.Windows.Forms;

class Test {

  function Main() {
    MessageBox.Show(""Hello World with native dotNET"", ""JScript"");
  }

}
"

image

Addendum 11.09.2022: The approach presented here is deprecated, because it can’t work in the Windows compatibility mode. It is suggested to use the late binding approach with dotNET 5 and 6.

4 Likes

Here as tiny addendum a C# code for the Invoke Code Activity which stores all in one source, for a fast testing. It is only necessary to reference to Microsoft.JScript library.

//-UiPath Begin---------------------------------------------------------

string ErrorMessage = null;
string Result = null;
string Method = "Main";
string MethodParameters = null;
string ConstructorParameters = null;
string Instance = "Test";
string Assemblies = @"System.Runtime.InteropServices.dll";
string Language = "VB";

string Code = @"
Imports System
Imports System.Runtime.InteropServices
Imports Microsoft.VisualBasic

Class Test

  Sub Main()
    Dim VBS As Object
    VBS = CreateObject(""MSScriptControl.ScriptControl"")
    VBS.Language = ""VBScript""
    VBS.AllowUI = True
    VBS.ExecuteStatement(""MsgBox """"Hello World via Late Binding in UiPath from VBScript!"""", VBOkOnly, """"ExecuteStatement"""""")
  End Sub

End Class
";

System.CodeDom.Compiler.CompilerResults CompResult;

//-Setting of the compiler version--------------------------------------
System.Collections.Generic.Dictionary<string, string> providerOptions = new System.Collections.Generic.Dictionary<string, string> {
  {"CompilerVersion", "v4.0"}
};

//-Setting of the language----------------------------------------------
System.CodeDom.Compiler.CodeDomProvider CodeProvider = null;
switch(Language.ToUpper()) {
  case "CS":
    CodeProvider = new Microsoft.CSharp.CSharpCodeProvider(providerOptions);
    break;
  case "VB":
    CodeProvider = new Microsoft.VisualBasic.VBCodeProvider(providerOptions);
    break;
  case "JS":
    CodeProvider = new Microsoft.JScript.JScriptCodeProvider();
    break;
  default:
    ErrorMessage = "Language unknown";
    System.Console.WriteLine("Error: " + ErrorMessage);
    return;
}

//-Setting of the compiler parameters-----------------------------------
System.CodeDom.Compiler.CompilerParameters compilerParams = new System.CodeDom.Compiler.CompilerParameters();
compilerParams.GenerateInMemory = true;
compilerParams.GenerateExecutable = false;
compilerParams.IncludeDebugInformation = true;
compilerParams.WarningLevel = 3;
string[] assemblies = Assemblies.Split(new char[] { ',' } );
foreach(string Assembly in assemblies) {
  compilerParams.ReferencedAssemblies.Add(Assembly);
}

//-Checking the code----------------------------------------------------
if(System.String.IsNullOrEmpty(Code.Trim())) {
  ErrorMessage = "Code missed";
  System.Console.WriteLine(ErrorMessage);
  return;
}
//-Compiling of the code------------------------------------------------
CompResult = CodeProvider.CompileAssemblyFromSource(compilerParams, Code);
//-Checking if compiling code exists------------------------------------
if(CompResult == null) {
  ErrorMessage = "Compiling code missed";
  System.Console.WriteLine(ErrorMessage);
  return;
}
System.Collections.Specialized.StringCollection CompOutput = CompResult.Output;
if(CompResult.Errors.Count != 0) {
  ErrorMessage = "Compilation failed" + System.Environment.NewLine;
  foreach(string CompOutputLine in CompOutput) {
    if(!string.IsNullOrEmpty(CompOutputLine)) {
      if(!CompOutputLine.Contains("This compiler is provided as part of the Microsoft (R) .NET Framework") &&
        !CompOutputLine.Contains("Copyright (C) Microsoft Corporation. All rights reserved.")) {
        ErrorMessage += CompOutputLine + System.Environment.NewLine;
      }
    }
  }
  System.Console.WriteLine(ErrorMessage);
  return;
}

//-Read constructor parameters and add it to a list---------------------
object[] ConstructorParams = null;
if(!System.String.IsNullOrEmpty(ConstructorParameters)) {
  string[] partsConstructorParams = ConstructorParameters.Split(new char[] { ',' });
  ConstructorParams = new System.Collections.Generic.List<string>(partsConstructorParams).ToArray();
}

//-Read method parameters and add it to a list--------------------------
object[] MethodParams = null;
if(!System.String.IsNullOrEmpty(MethodParameters)) {
  string[] partsMethodParams = MethodParameters.Split(new char[] { ',' });
  MethodParams = new System.Collections.Generic.List<string>(partsMethodParams).ToArray();
}

//-Checking the instance------------------------------------------------
if(System.String.IsNullOrEmpty(Instance.Trim())) {
  ErrorMessage = "Instance missed";
  System.Console.WriteLine(ErrorMessage);
  return;
}
//-Create instance and invoke method------------------------------------
try {
  object oInstance = CompResult.CompiledAssembly.CreateInstance(
    Instance,
    false,
    System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.CreateInstance,
    null, // use default binder
    ConstructorParams,
    null, // use CultureInfo from current thread,
    null
  );
  if(!System.String.IsNullOrEmpty(Method)) {
    System.Reflection.MethodInfo MethInfo = oInstance.GetType().GetMethod(Method);
    object oResult = MethInfo.Invoke(oInstance, MethodParams);
    if(oResult != null) {
      Result = oResult.ToString();
    }
  }
} catch (System.Exception ex) {
  ErrorMessage = ex.Message + System.Environment.NewLine + ex.StackTrace;
}

System.Console.WriteLine(ErrorMessage);
System.Console.WriteLine(Result);

//-UiPath End-----------------------------------------------------------
1 Like