Click here to Skip to main content
15,888,610 members
Articles / All Topics

P/Invoke Tutorial: Passing parameters (Part 3)

Rate me:
Please Sign up or sign in to vote.
4.50/5 (2 votes)
12 Jun 2012CPOL2 min read 12.9K   5   1
P/Invoke tries to make your life easier by automatically converting (“marshaling”) data types from managed code to native code and the other way around.

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# typeC/C++ typeBytesRange
boolbool (with int fallback)usually 1true or false
charwchar_t (or char if necessary)2 (1)Unicode BMP
byteunsigned char10 to 255
sbytechar1-128 to 127
shortshort2-32,768 to 32,767
ushortunsigned short20 to 65,535
intint4-2 billion to 2 billion
uintunsigned int40 to 4 billion
long__int648-9 quintillion to 9 quintillion
ulongunsigned __int6480 to 18 quintillion
floatfloat47 significant decimal digits
doubledouble815 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:

C#
[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*:

C#
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.

C#
[DllImport("NativeLib.dll")]
private static extern void do_something(byte[] data);

Marshalling Objects

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

C#
[StructLayout(LayoutKind.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:

C#
[DllImport("NativeLib.dll")]
private static extern void do_something(MyClass data);

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

C#
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:

C#
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 Arbitrary Pointers

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

So this C function:

C#
void do_something(void* ptr);

becomes:

C#
[DllImport("NativeLib.dll")]
private static extern void do_something(IntPtr ptr);

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer University of Stuttgart
Germany Germany
I have studied Software Engineering and am currently working at the University of Stuttgart, Germany.

I have been programming for many years and have a background in C++, C#, Java, Python and web languages (HTML, CSS, JavaScript).

Comments and Discussions

 
GeneralMy vote of 4 Pin
Klaus Luedenscheidt12-Jun-12 17:42
Klaus Luedenscheidt12-Jun-12 17:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.