0

How do I marshal a .net string to a null terminated ascii or utf8 IntPtr. For use on linux when interloping to a c lib.

I'm trying to call a c function with signiture like the following:

void my_function(const char* str)

My c# definition is as follows:

[DllImport("mylib.so)]
public static extern void my_function(IntPtr str);

I'm using an IntPtr for the interop because the native code holds onto the string longer than the duration of the function call. So I need to be able to marshal/pin it myself.

Things I've considered:

  • Marshal.StringToHGlobalUni (seems to do 16 bit characters)
  • Marshal.StringToHGlobalAnsi (ansi is for windows I think)
  • Marshal.StringToCoTaskMemUTF8 (says it uses the Com allocator, which seems wrong as this is not for com interop)
  • Marshal.StringToBSTR (says it's for com interop)
  • Marshal.StringToHGlobalAuto (not sure about this one, says it converts to ansi if required which isn't what I want, also no details on how it decides or what it converts to if it doesn't do ansi)

There is also the option of converting to a ascii or utf8 byte array and then marshalling the resulting array (with a null terminator on the end) but this seem like overkill, especially if one of the string marshalling options does the correct thing.

trampster
  • 8,598
  • 4
  • 37
  • 52
  • 1
    There is no method. You need to create and free it yourself. I would give you a code example but I am sure you know how to achieve it – TheGeneral Dec 18 '20 at 02:45
  • 2
    Marshal.StringToCoTaskMemUTF8 is fine. You'll need to deallocate it after the call returns, as per the docs. – David Heffernan Dec 18 '20 at 05:53
  • 1
    It's open source. Just have a look at it: https://github.com/dotnet/runtime/blob/c3c768b4c36012661baf46a717649ae04d642c90/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.cs#L756 and for unix: https://github.com/dotnet/runtime/blob/c3c768b4c36012661baf46a717649ae04d642c90/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Marshal.Unix.cs#L107 so StringToCoTaskMemUTF8 is fine, AllocCoTaskMem and AllocHGlobal are the same and it's not COM allocator, just platform allocator. – Simon Mourier Dec 18 '20 at 07:56

1 Answers1

1

The correct solution is Marshal.StringToHGlobalAnsi. This is the ANSI version of the Marshal.StringToHGlobalUni function. The ANSI version deals with strings which use char as the underlying character type, whereas the Unicode version deals with wide strings which use wchar_t as the underlying character type. The function names were written with Win32-colored glasses.

From your perspective, you have an unmanaged C routine that expects a C-style string, using char as the underlying character type, so that matches with Marshal.StringToHGlobalAnsi.

Marshal.StringToHGlobalAnsi allocates unmanaged memory for the string, which you can then pass to the C code. Because the memory is allocated on the unmanaged heap, GC pinning is not necessary. Once the C code is done, you will need to free the memory by calling Marshal.FreeHGlobal.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574