manski's blog

P/Invoke Tutorial: Passing parameters (Part 3)

P/Invoke tries to make your life easier by automatically converting (“marshalling”) data types from managed code to native code and the other way around.

Marshalling Primitive Data Types

Primitive data types (bool, int, double, …) are the easiest to use. They map directly to their native counterparts.

C# type C/C++ type Bytes Range
bool bool (with int fallback) usually 1 true or false
char wchar_t (or char if necessary) 2 (1) Unicode BMP
byte unsigned char 1 0 to 255
sbyte char 1 -128 to 127
short short 2 -32,768 to 32,767
ushort unsigned short 2 0 to 65,535
int int 4 -2 billion to 2 billion
uint unsigned int 4 0 to 4 billion
long __int64 8 -9 quintillion to 9 quintillion
ulong unsigned __int64 8 0 to 18 quintillion
float float 4 7 significant decimal digits
double double 8 15 significant decimal digits

Marshalling Strings

For passing strings, it’s recommended that you pass them as Unicode strings (if possible). For this, you need to specify Char.Unicode like this:

[DllImport("NativeLib.dll", CharSet = CharSet.Unicode)]
private static extern void do_something(string str);

This requires the C/C++ parameter type be a wchar_t*:

void do_something(const wchar_t* str);

For more details, see P/Invoke Tutorial: Passing strings (Part 2).

Marshalling Arrays

Arrays of primitive types can be passed directly.

private static extern void do_something(byte[] data);

Marshalling Objects

To be able to pass objects you need to make their memory layout sequential:

class MyClass {

This ensures that the fields are stored in the same order they’re written in code. (Without this attribute the C# compiler reorder fields around to optimize the data structure.)

Then simply use the object’s class directly:

private static extern void do_something(MyClass data);

The object will be passed by reference (either as struct* or struct&) to the C function:

typedef struct {
} MyClass;

void do_something(MyClass* data);

Note: Obviously the order of the fields in the native struct and the managed class must be exactly the same.

Marshalling Structs

Marshalling managed structs is almost identical to marshalling objects with only one difference: structs are passed by copy by default.

So for structs the C/C++ function signature reads:

void do_something(MyClass data);

Of course, you can pass the struct also by reference. In this case, use (MyClass* data) or (MyClass& data) in C/C++ and (ref MyClass data) in C#.

Marshalling Delegates

Delegates are marshalled directly. The only thing you need to take care of is the “calling convention”. The default calling convention is Winapi (which equals to StdCall on Windows). If your native library uses a different calling convention, you need to specify it like this:

public delegate void MyDelegate(IntPtr value);

Marshalling Arbitrary Pointers

Arbitrary pointers (like void*) are marshalled as IntPtr objects.

So this C function:

void do_something(void* ptr);


private static extern void do_something(IntPtr ptr);


  1. Levy said:

    How do I marshall an object from a third party DLL for which I do not have access to the source code?

  2. Manski (post author) said:

    If it’s a C++ object/class, I think you’re out of luck – at least, if you want to call any methods of the class (no matter whether you have the source code or not). If you just want to pass it around, use “IntPtr”.

    • Shawn replied:

      Hi, it is really very nice tutorial about pinvoke, and very organized.

      Even if its C++ class/object, it can still be called using PInvoke. Please read PInvoke Interop SDK for C++ DLL, basically, you would be able to marshal anything from C++ to C# as long as they are exported from the C++ DLL.

  3. Levy said:

    How do I marshall an array of pointers in a C# callback? For example:

    public delegate void CallbackPrototype(int sizeOfArray, IntPtr[] arrayOfPointers);
    void MyCallback(int sizeOfArray, IntPtr[] arrayOfPointers)
      for (int i = 0; i < sizeOfArray; i++)
        IntPtr intPtr = arrayOfPointers[i]; // fails for i != 0 because arrayOfPointers.Length is one
  4. Tore Aurstad said:

    Great overview of P/Invoke! Is it possible to retrieve internal errors in the code you call, simple as wrapping a try catch I suppose? But will that give a generic error message, or is it possible to get internal details from the unmanaged dll?

  5. steve said:

    Dear Sebastian
    Maybe, you got an idea or advice what’s the issue with passing a 2D string array from C# to C++. Many thanks ..

    Here the C# code, which works fine:

    public struct TestStruct
    public string[,] stringArray;

    EntryPoint = “DDentry”,
    CallingConvention = CallingConvention.StdCall)]

    public static extern void DDentry
    ArraySubType = UnmanagedType.BStr)]
    string[,] arrayReadDat, int iDim1, int iDim2

    private void button6_Click_1(object sender, EventArgs e)
    TestStruct arrayReadDat = new TestStruct();
    arrayReadDat.stringArray = new string[lastRow+1, lastCol+1];
    string strK = “testify”;
    for (int i = 2; i <= lastRow; i++)
    for (int j = 1; j <= lastCol; j++)
    arrayReadDat.stringArray[i, j] = strK;

    int size = Marshal.SizeOf(typeof(TestStruct));
    IntPtr strPointer = Marshal.AllocHGlobal(size);
    Marshal.StructureToPtr(arrayReadDat, strPointer, false);

    DDentry(arrayReadDat.stringArray, lastRow+1, lastCol+1);


    Here the C++ code, where "no" data were passed to the pointer/array:

    extern "C"
    _declspec(dllexport) void DDentry(string *p2DIntArray, int iDim1, int iDim2)

    int iIndex = 0;
    for (int i = 2; i <= iDim1; i++)
    for (int j = 1; j <= iDim2; j++)
    arrayREAD[i][j] = p2DIntArray[iIndex++];

  6. fibriZo raZiel said:

    This sentence: “[…] The object will be passed by reference […]” could lead to confusion. In most cases, you will be passing a marshalling copy of the managed object. You must follow some restricted rules (such as avoiding classes in favor of structures) or manually pin your managed object before calling a native method, if you don’t want to end with a marshalling copy.


    • Alan replied:

      I agree. That will certainly create a marshalled copy of the object and references/pointers aren’t as simple to pass because the GC is responsible for maintaining them.
      @Manski: while you’re at it also fix the typo at that line: stuct&

  7. D said:

    Can I just say bless you for this post??? Hundreds of stack overflow answers and MSDN pages didn’t clarify the relationship between C# and native types as easily as this post…

  8. Baris Karadeniz said:

    Thanks to you. I was trying to pass structures from C++ DLL to C# WPF for a whole day. This 4 part series did, what the other sites couldn’t. Your style is really nice, short and to the point.


  9. Robert Cohn said:

    Hi Sebastian –

    You have provided a great service to readers of this blog. Thank you very much for your clear and simple examples of very difficult subjects. You are most appreciated!

    Best regards.

Leave a Reply to steve Cancel reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.