How to use Late Binding with C# Invoke Code Activity

A longer time ago I present the possibility how to use late binding via a dotNET code provider, which compiles and executes the code in memory. In this tutorial I show now a way how to use late binding with the Invoke Code Activity with the language C#. This approach bases on the use of lambda expressions.

Routines

To use late binding here a few lambda expressions, which are based on VBdotNET. The functions CreateObject and GetObject are simply wrappers around the VBdotNET equivalent. Furthermore there are the functions InvokeMethod to execute a method, GetProperty to get the content and SetProperty to set the content of a property. Last but not least the function FreeObject to release an object which was created with CreateObject. This code block can implemented into an Invoke Code Activity with C# language.

//-CreateObject---------------------------------------------------------
Func <string, object> CreateObject = (string ProgId) => {
  return Microsoft.VisualBasic.Interaction.CreateObject(ProgId, null);
};

//-GetObject------------------------------------------------------------
Func <string, object> GetObject = (string Class) => {
  return Microsoft.VisualBasic.Interaction.GetObject(Class);
};

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

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

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

//-FreeObject-----------------------------------------------------------
Action <object> FreeObject = (object obj) => {
  Marshal.ReleaseComObject(obj);
};

Example

Here now an Example how to use this functions. At first an object of the class MSScriptControldotScriptControl is instantiated. This is a very old COM library to use Windows Scripting Host languages. The language is set to VBScript and the command MsgBox will be executed. At the end the object is destroyed.

//-Main-----------------------------------------------------------------
object VBScript = null;
string command = string.Empty;

try {

  VBScript = CreateObject("MSScriptControl.ScriptControl");

  InvokeMethod(VBScript, "_AboutBox", new object[0]);

  SetProperty(VBScript, "Language", new object[1]{"VBScript"});
  SetProperty(VBScript, "AllowUI", new object[1]{1});

  // MsgBox "Dies ist ein Test", vbOkOnly, "MessageBox"
  command = "MsgBox \"Dies ist ein Test\", vbOkOnly, \"MessageBox\"";
  InvokeMethod(VBScript, "ExecuteStatement", new object[1]{command});

} catch {
  return;
}

FreeObject(VBScript);

Here the about box and …

image

… here the message box.

image

Conclusion

With the combination of lambda expressions and the methods of the dotNET framework it is very easy possible to realize late binding inside of UiPath Invoke Code Activity via C#. The code is easy to read and an implementation, combined with a conversion of e.g. from VBdotNET or VBScript, should be simple.

Perspective

To test the validity of the approach I used C# code from the Scripting Tracker, to automate the SAP GUI for Windows. It works without any problems.
:grinning:

//-Main-----------------------------------------------------------------
object SapGuiAuto = null;
object app = null;
object connection = null;
object session = null;

try {
  SapGuiAuto = GetObject("SAPGUI");
  app = InvokeMethod(SapGuiAuto, "GetScriptingEngine", new object[0]);
  SetProperty(app, "HistoryEnabled", new object[1]{false});
  connection = GetProperty(app, "Children", new object[1]{0});
  session = GetProperty(connection, "Children", new object[1]{0});
  if(GetProperty(session, "Busy", new object[0]) == true) {
    return;
  }
  object info = GetProperty(session, "Info", new object[0]);
  if(GetProperty(info, "IsLowSpeedConnection", new object[0]) == true) {
    return;
  }
} catch {
  return;
}

dynamic ID = null;

ID = InvokeMethod(session, "findById", new object[1]{"wnd[0]/tbar[0]/okcd"});
SetProperty(ID, "text", new object[1]{"/nse16"});
ID = InvokeMethod(session, "findById", new object[1]{"wnd[0]"});
InvokeMethod(ID, "sendVKey", new object[1]{0});
ID = InvokeMethod(session, "findById", new object[1]{"wnd[0]/usr/ctxtDATABROWSE-TABLENAME"});
SetProperty(ID, "text", new object[1]{"TADIR"});
ID = InvokeMethod(session, "findById", new object[1]{"wnd[0]/usr/ctxtDATABROWSE-TABLENAME"});
SetProperty(ID, "caretPosition", new object[1]{5});
ID = InvokeMethod(session, "findById", new object[1]{"wnd[0]"});
InvokeMethod(ID, "sendVKey", new object[1]{0});

SetProperty(app, "HistoryEnabled", new object[1]{true});

FreeObject(session);
FreeObject(connection);
FreeObject(app);
FreeObject(SapGuiAuto);

The automatically generated code only had to be supplemented manually that new object[0] is to be added for parameterless calls. On this way, SAP GUI Scripting can be used seamlessly in the Invoke Code activity.

4 Likes