Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What to pass for pinned string in P/Invoke?

Assume this C function:

void do_something(const char* str)

It stores the string somewhere for later reference.

Furthermore, I have this signature in C# to call this function:

[DllImport("NativeLib")]
static extern void do_something(string str);

Now, what do I need to do when passing a string to this method:

  • Do I need to pin the string (with GCHandle.Alloc()) (or is the marshaller creating a copy)?
  • If I do need to pin it, do I pass the "original" string (i.e. the string I passed to GCHandle.Alloc())? Or do I need to pass the return value of GCHandle.AddrOfPinnedObject()?
  • Is string the correct data type (for do_something) in this case? Or should I use IntPtr instead?
like image 605
Sebastian Krysmanski Avatar asked Jun 11 '12 09:06

Sebastian Krysmanski


1 Answers

Storing pointers through an interop boundary is a Really Bad Idea, do everything you can to modify the C code to make a copy of the string instead.

Pinning the string as suggested in the upvoted answer isn't going to work, the pinvoke marshaller must convert the string to char* and releases that converted string after making the native call, invalidating the pointer. You must marshal the string yourself. Declare the argument as IntPtr and use Marshal.StringToHGlobalAnsi() to create the pointer value.

Or use Marshal.StringToCoTaskMemAnsi(), it allocates from the COM heap. Which is your real problem, there is no great scenario to release the string. The C code cannot release the string because it doesn't know how it was allocated. Your C# code cannot release the string because it doesn't know when the C code is done with the pointer. This is why copying the string is so important. You can live with the memory leak if you make this call only a few times.

like image 184
Hans Passant Avatar answered Nov 13 '22 03:11

Hans Passant