Having a native function that returns a string (as char *) by parameter, what would be the best option between pre-allocating the char * via managed code and passing it by parameter and allocating the char * from inside the native code and then release it from c#?
could you kindly explain to me why should I use one over the other? Please answer only if there is a specific reason to prefer a solution over the other. If instead either solutions are ok, my question can be considered answered as well.
As bonus, I would like to know how I should allocate the char * variable from c# in the first case(using the Marshal class or with a simple new or with a StringBuilder like I often see in other answers?) and how I should delete the pointer if instead I create the char * variable from inside the native code in the second case.
It is usually not good practice to return a char*
from a C function and expect it to be de-allocated by the caller. The caller may not do this (correctly or at all) and thus will leak memory. One common way to avoid this (as used by OpenGL, OpenCL and other libraries I've seen) is to declare the prototype as:
int GetString(char* str, int* len);
with an implementation like so:
int GetString(char* str, int* len)
{
if (str == NULL)
{
len = internal_get_string_length();
return 0; // No errors
}
else
{
if (len <= internal_get_string_length())
return -1; // not enough space in str
char* internal_str = internal_get_string_ptr();
strcpy(str, internal_str);
return 0;
}
}
The documentation will then state that if str
is NULL, the length of the string to be returned is returned in len
. Otherwise, the pointer str
is expected to contain as many characters as required. In order to use it, the user calls the function twice, once with NULL for str
and an int for len
, then again with an allocated char array as long as len
. A possibly prototype for P/Invoking this sort of function is:
// Declaration
[DllImport("myDll.dll")]
int GetString(StringBuilder sb, ref int len);
// Usage
int length;
GetString(null, length);
var sb = new StringBuilder(length); // Set capacity
GetString(sb, length);
Console.WriteLine(sb.ToString()); // Do stuff with C# string
Hope that helps!