# Platform Invoke
# Calling a Win32 dll function
using System.Runtime.InteropServices;
class PInvokeExample
{
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern uint MessageBox(IntPtr hWnd, String text, String caption, int options);
public static void test()
{
MessageBox(IntPtr.Zero, "Hello!", "Message", 0);
}
}
Declare a function as static extern
stting DllImportAttribute
with its Value
property set to .dll name. Don't forget to use System.Runtime.InteropServices
namespace. Then call it as an regular static method.
The Platform Invocation Services will take care of loading the .dll and finding the desired finction. The P/Invoke in most simple cases will also marshal parameters and return value to and from the .dll (i.e. convert from .NET datatypes to Win32 ones and vice versa).
# Using Windows API
Use pinvoke.net (opens new window).
Before declaring an extern
Windows API function in your code, consider looking for it on pinvoke.net (opens new window). They most likely already have a suitable declaration with all supporting types and good examples.
# Marshalling arrays
Arrays of simple type
[DllImport("Example.dll")]
static extern void SetArray(
[MarshalAs(UnmanagedType.LPArray, SizeConst = 128)]
byte[] data);
Arrays of string
[DllImport("Example.dll")]
static extern void SetStrArray(string[] textLines);
# Marshaling structs
Simple struct
C++ signature:
typedef struct _PERSON
{
int age;
char name[32];
} PERSON, *LP_PERSON;
void GetSpouse(PERSON person, LP_PERSON spouse);
C# definition
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct PERSON
{
public int age;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string name;
}
[DllImport("family.dll", CharSet = CharSet.Auto)]
public static extern bool GetSpouse(PERSON person, ref PERSON spouse);
Struct with unknown size array fields. Passing in
C++ signature
typedef struct
{
int length;
int *data;
} VECTOR;
void SetVector(VECTOR &vector);
When passed from managed to unmanaged code, this
The data
array should be defined as IntPtr and memory should be explicitly allocated with Marshal.AllocHGlobal()
(opens new window) (and freed with Marshal.FreeHGlobal()
(opens new window) afterwords):
[StructLayout(LayoutKind.Sequential)]
public struct VECTOR : IDisposable
{
int length;
IntPtr dataBuf;
public int[] data
{
set
{
FreeDataBuf();
if (value != null && value.Length > 0)
{
dataBuf = Marshal.AllocHGlobal(value.Length * Marshal.SizeOf(value[0]));
Marshal.Copy(value, 0, dataBuf, value.Length);
length = value.Length;
}
}
}
void FreeDataBuf()
{
if (dataBuf != IntPtr.Zero)
{
Marshal.FreeHGlobal(dataBuf);
dataBuf = IntPtr.Zero;
}
}
public void Dispose()
{
FreeDataBuf();
}
}
[DllImport("vectors.dll")]
public static extern void SetVector([In]ref VECTOR vector);
Struct with unknown size array fields. Receiving
C++ signature:
typedef struct
{
char *name;
} USER;
bool GetCurrentUser(USER *user);
When such data is passed out of unmanaged code and memory is allocated by the unmanaged functions, the managed caller should receive it into an IntPrt
variable and convert the buffer to a managed array. In case of strings there is a convenient Marshal.PtrToStringAnsi()
(opens new window) method:
[StructLayout(LayoutKind.Sequential)]
public struct USER
{
IntPtr nameBuffer;
public string name { get { return Marshal.PtrToStringAnsi(nameBuffer); } }
}
[DllImport("users.dll")]
public static extern bool GetCurrentUser(out USER user);
# Marshaling unions
Value-type fields only
C++ declaration
typedef union
{
char c;
int i;
} CharOrInt;
C# declaration
[StructLayout(LayoutKind.Explicit)]
public struct CharOrInt
{
[FieldOffset(0)]
public byte c;
[FieldOffset(0)]
public int i;
}
Mixing value-type and reference fields
Overlapping a reference value with a value type one is not allowed so you cannot simply use the will not compile forFieldOffset(0) text; FieldOffset(0) i;
typedef union
{
char text[128];
int i;
} TextOrInt;
and generally you would have to employ custom marshaling. However, in particular cases like this simpler technics may be used:
[StructLayout(LayoutKind.Sequential)]
public struct TextOrInt
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
public byte[] text;
public int i { get { return BitConverter.ToInt32(text, 0); } }
}