How to use Late Binding with C# Invoke Code Activity with dotNET 5 and 6

A few days ago I presented the possibility to use late binding in the Invoke Code Activity. With the current version of UiPath we have the possibility to choose between different versions of dotNET. The approach above is for Windows - Legacy. Then I asked myself if the same approach will work with dotNET 5.0 - in the selection list Windows. Here now the approach that works with Windows 64-bit.

Namespace Microsoft.VisualBasic

The first challenge was the import of the Microsoft.VisualBasic namespace. For this it is necessary to load the assembly Microsoft.VisualBasic.Core. I wanted to do that at runtime.

//-Load Assembly--------------------------------------------------------
string VisualBasicCore = "Microsoft.VisualBasic.Core, Version=10.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
System.Reflection.Assembly VisualBasicCoreAssembly = System.Reflection.Assembly.Load(VisualBasicCore);

Two lines of code do it for us. All we need here is the assembly name to load it.

The methods CreateObject and GetObject are part of the class Microsoft.VisualBasic.Interaction. The class must be instantiated. There were problems with the constructor, the instantiation is done without executing the it.

//-Create new instance without constructor call-------------------------
System.Type VisualBasicInteractionType = VisualBasicCoreAssembly.GetType("Microsoft.VisualBasic.Interaction");
object VisualBasicInteraction = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(VisualBasicInteractionType);

Now we can use the methods of the Interaction class of Visual Basic inside our code. This approach can be used with any assembly. On this way at the runtime of an automation workflow any assembly can be dynamically loaded to it.

Late Binding

To use late binding I took the lambda expression approach, which I described here. Here now the complete code:

//-Begin----------------------------------------------------------------

//-Load Assembly--------------------------------------------------------
string VisualBasicCore = "Microsoft.VisualBasic.Core, Version=10.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
System.Reflection.Assembly VisualBasicCoreAssembly = System.Reflection.Assembly.Load(VisualBasicCore);
//-Create new instance without constructor call-------------------------
System.Type VisualBasicInteractionType = VisualBasicCoreAssembly.GetType("Microsoft.VisualBasic.Interaction");
object VisualBasicInteraction = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(VisualBasicInteractionType);

//-InvokeMethod---------------------------------------------------------
Func <object, string, object[], dynamic> InvokeMethod = (object obj, string methodName, object[] methodParams) => {
  return obj.GetType().InvokeMember(methodName, System.Reflection.BindingFlags.InvokeMethod, null, obj, methodParams);
};

//-GetProperty----------------------------------------------------------
Func <object, string, object[], dynamic> GetProperty = (object obj, string propertyName, object[] propertyParams) => {
  return obj.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.GetProperty, null, obj, propertyParams);
};

//-SetProperty----------------------------------------------------------
Func <object, string, object[], dynamic> SetProperty = (object obj, string propertyName, object[] propertyParams) => {
  return obj.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.SetProperty, null, obj, propertyParams);
};

//-Main-----------------------------------------------------------------
object wshNetwork = InvokeMethod(VisualBasicInteraction, "CreateObject", new object[2] { "WScript.Network", null });
string ComputerName = GetProperty(wshNetwork, "ComputerName", new object[0]);
string UserName = GetProperty(wshNetwork, "UserName", new object[0]);
Console.WriteLine(ComputerName + " - " + UserName);

//-End------------------------------------------------------------------

In the Main block I create an object of WScript.Network and query two properties. Nothing special, but shows very nicely how late binding is used.

GetObject

The method GetObject is only available with dotNET 5 and higher. GetObject is very interesting for finding existing objects in the Running Object Table (ROT), like SAPGUI for the SAP GUI Scripting. Here now an approach to use GetObject and to get the version of the SAP GUI Scripting library.

//-Begin----------------------------------------------------------------

//-Load Assembly--------------------------------------------------------
string VisualBasicCore = "Microsoft.VisualBasic.Core, Version=10.0.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
System.Reflection.Assembly VisualBasicCoreAssembly = System.Reflection.Assembly.Load(VisualBasicCore);
//-Create new instance without constructor call-------------------------
System.Type VisualBasicInteractionType = VisualBasicCoreAssembly.GetType("Microsoft.VisualBasic.Interaction");
object VisualBasicInteraction = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(VisualBasicInteractionType);

//-InvokeMethod---------------------------------------------------------
Func <object, string, object[], dynamic> InvokeMethod = (object obj, string methodName, object[] methodParams) => {
  return obj.GetType().InvokeMember(methodName, System.Reflection.BindingFlags.InvokeMethod, null, obj, methodParams);
};

//-GetProperty----------------------------------------------------------
Func <object, string, object[], dynamic> GetProperty = (object obj, string propertyName, object[] propertyParams) => {
  return obj.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.GetProperty, null, obj, propertyParams);
};

//-SetProperty----------------------------------------------------------
Func <object, string, object[], dynamic> SetProperty = (object obj, string propertyName, object[] propertyParams) => {
  return obj.GetType().InvokeMember(propertyName, System.Reflection.BindingFlags.SetProperty, null, obj, propertyParams);
};

//-Main-----------------------------------------------------------------
object SapGuiAuto = InvokeMethod(VisualBasicInteraction, "GetObject", new object[2] { "SAPGUI", null });
object app = InvokeMethod(SapGuiAuto, "GetScriptingEngine", new object[0]);

int majorVersion = GetProperty(app, "MajorVersion", new object[0]);
int minorVersion = GetProperty(app, "MinorVersion", new object[0]);
int patchLevel = GetProperty(app, "PatchLevel", new object[0]);
Console.WriteLine(majorVersion.ToString() + "." + minorVersion.ToString() + "." + patchLevel.ToString());

//-End------------------------------------------------------------------

With this approach is it possible to use SAP GUI Scripting seamlessly in UiPath in the context of Windows 64-bit with dotNET 5.0.

Conclusion

As this and my previous tutorials shows, is the using of late binding with the C# Invoke Code Activity easy possible. This is independently of using dotNET Framework 4.6.1 (Windows Legacy - x86) or dotNET 5.0 (Windows - x64). It was also important for me to do this with the namespace Microsoft.VisualBasic, because the using of late binding is a common practice there. My main target is to investigate whether SAP GUI Scripting can be made to work on this ways. I will look at that further, but the foundation stone is laid.

Addendum 16.04.2022
This approach works also with dotNET 6.0, so it can be used with all releases of UiPath and with the Windows Legacy and Windows compatbility mode.

4 Likes