Platform Invoke (P/Invoke)
P/Invoke is a technology that allows you to call c/c++ function or function from a dll library from your managed code. It's divided into two namespace System and System.Runtime.InteropServices. you only need these namespaces to call external c/c++ function.
why do we need to call c/c++ function inside c#
- It's more efficient than writing your own in c#
- decreases code repetition
Windows Example
Here is an example of how you can use P/Invoke to call function from user32.dll.
using System;
using System.Runtime.InteropServices;
public partial class Program
{
// Import user32.dll (containing the function we need) and define
// the method corresponding to the native function.
[LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16, SetLastError = true)]
private static partial int MessageBoxW(IntPtr hWnd, string lpText, string lpCaption, uint uType);
public static void Main(string[] args)
{
// Invoke the function as a regular managed method.
MessageBoxW(IntPtr.Zero, "Command-line message box", "Attention!", 0);
}
}
what happening in the snippet:
- we have imported
SystemandSystem.Runtime.InteropServiceswhich contains the necessary attributes to call a function in a dll file. - the library user32.dll will be loaded at runtime and it will complete the implementation of
MessageBoxWfunction. ( for those of who don't know what the function do: it's just showing a prompt to the user). keep in mind that this example is windows specific, it will not work on Linux or MacOS. - you have probably noticed that we doing StringMarshalling. ( Marshalling is the process of transforming types when they need to cross between c# to native code (c/c++ or dll))
- you must define the c# function with the same signature of the c counterpart.
MacOS Example
using System;
using System.Runtime.InteropServices;
namespace PInvokeSamples
{
public static partial class Program
{
// Import the libSystem shared library and define the method
// corresponding to the native function.
[LibraryImport("libSystem.dylib")]
private static partial int getpid();
public static void Main(string[] args)
{
// Invoke the function and get the process ID.
int pid = getpid();
Console.WriteLine(pid);
}
}
}
- In the example we are loading
libSystem.dyliblibrary and calling agetpid()function inside that library.
Linux example
using System;
using System.Runtime.InteropServices;
namespace PInvokeSamples
{
public static partial class Program
{
// Import the libc shared library and define the method
// corresponding to the native function.
[LibraryImport("libc.so.6")]
private static partial int getpid();
public static void Main(string[] args)
{
// Invoke the function and get the process ID.
int pid = getpid();
Console.WriteLine(pid);
}
}
}
Invoking managed code from unmanaged code
c# allows us to pass a callback function to unmanaged code which will be called when a particular event gets triggered. The closes thing to a function pointer in managed code is a delegate. keep in mind that you have to create the delegate which has the same signature as expected in the dll function.
Here is an example:
using System;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
public static partial class Program
{
// Define a delegate that corresponds to the unmanaged function.
private delegate bool EnumWC(IntPtr hwnd, IntPtr lParam);
// Import user32.dll (containing the function we need) and define
// the method corresponding to the native function.
[LibraryImport("user32.dll")]
private static partial int EnumWindows(EnumWC lpEnumFunc, IntPtr lParam);
// Define the implementation of the delegate; here, we simply output the window handle.
private static bool OutputWindow(IntPtr hwnd, IntPtr lParam)
{
Console.WriteLine(hwnd.ToInt64());
return true;
}
public static void Main(string[] args)
{
// Invoke the method; note the delegate as a first parameter.
EnumWindows(OutputWindow, IntPtr.Zero);
}
}
}
I hope you learned something new today!
C# ( c sharp )