Tip: How to use Win32 API Calls or Win32 DLL Functions

In the last days I was looking for a way to realize Win32 API calls without additional packages. Surely this is a rather rare requirement, since almost everything can be handled via the dotNET framework. However, sometimes there are use cases where this may be required. Especially in the environment of older applications there may be Win32 libraries whose functions can be used on this way.

In the main thing the Invoke Code activity calls another C# provider, which offers the possibility to perform a DLLImport.

Here the code, it is more or less a reduced dotNETRunner. It compiles the code, invokes the method of the instance and delivers a result back:

//-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 = new CSharpCodeProvider(providerOptions);

//-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 call the Win32 API function.

Here the invoked code:

@"
using System;
using System.Runtime.InteropServices;

class Test {

  // Use DllImport to import the Win32 MessageBox function.
  [DllImport(""user32.dll"", CharSet = CharSet.Unicode)]
  public static extern int MessageBox(
    IntPtr hWnd,
    String text,
    String caption,
    uint type
  );

  public static void Main() {
    int Result = MessageBox(IntPtr.Zero, ""Hello World from UiPath via Win32 API Call"", 
      ""MessageBox from Win32 API"", 0);
  }

}
"

The only drawback is the necessary doubling of the quotation marks in the string.

And it works as expected.

image

Maybe not elegant, but on this way functions of Win32 libraries can be used in the context of UiPath, without additional packages.

Here my example project:
DLLImport.zip (33.4 KB)

Enjoy it.

11 Likes

Great work!

1 Like

Here as tiny addendum a C# code for the Invoke Code Activity which stores all in one source, for a fast testing. It is not necessary to import respectively to reference to any libraries.

//-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 Code = @"
using System;
using System.Runtime.InteropServices;

class Test {

  // Use DllImport to import the Win32 MessageBox function.
  [DllImport(""user32.dll"", CharSet = CharSet.Unicode)]
  public static extern int MessageBox(
    IntPtr hWnd,
    String text,
    String caption,
    uint type
  );

  public static void Main() {
    int Result = MessageBox(IntPtr.Zero, ""Hello World from UiPath via Win32 API Call"", 
      ""MessageBox from Win32 API"", 0);
  }

}
";

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 = new Microsoft.CSharp.CSharpCodeProvider(providerOptions);

//-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(Result);
System.Console.WriteLine(ErrorMessage);

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

Here as tiny addendum: This approach works only with Windows Legacy Compatibility Mode. With .NET5+ an the error PlatformNotSupportedException occurs at compilation at when calling CompileAssemblyFromSource. This function is no longer supported.