Reputation: 3789
I have the following function in C++ that I want to call from C#:
std::string getString();
How do I best do this (using Pinvoke)?
Am I correct in assuming that return allocated memory from C++ to C# is problematic and that it would be better (easier) to allocate a large string in C# and then pass it on to C++ for writing?
In this case I guess I should wrap the C++ function with C ?:
extern "C"
{
__declspec(dllexport) void get_string(int size, char *buffer)
{
call getString and strncpy to buffer
}
}
Upvotes: 2
Views: 385
Reputation: 111830
The classical way to do it is:
__declspec(dllexport) void get_string(int size, char *buffer)
{
std::string str = getString();
strncpy(buffer, str.c_str(), size);
buffer[size - 1] = '\0';
}
C# side:
[DllImport("NativeLibrary", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void get_string(int len, [Out] StringBuilder sb);
and then (very important! You must pre-size the StringBuilder
)
var sb = new StringBuilder(100);
get_string(sb.Capacity, sb);
string str = sb.ToString();
You can use a char[]
, but it is more complex, because then you have to manually "trim" the \0
.
There is a more complex way to do it with one less copy of memory... But it is a little more complex:
C++ side:
__declspec(dllexport) void get_string(void (*func)(const char*))
{
std::string str = getString();
func(str.c_str());
}
C# side:
[DllImport("NativeLibrary", CallingConvention = CallingConvention.Cdecl)]
public static extern void get_string(StringFromIntPtr.FromIntPtrDelegate func);
public class StringFromIntPtr
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FromIntPtrDelegate(IntPtr ptr);
public string Value { get; protected set; }
public void FromIntPtr(IntPtr ptr)
{
Value = Marshal.PtrToStringAnsi(ptr);
}
}
Then you use like this:
var sfip = new StringFromIntPtr();
get_string(sfip.FromIntPtr);
string str = sfip.Value;
The point is that you pass to the C++ code a delegate to a C# method that knows how to handle a raw pointer to a string (through the use of Marshal.PtrToStringAnsi
for example), and the C++ code uses it. Note that it would be much easier to do this in C++/CLI (because you wouldn't need the delegate, the C++/CLI can use both std::string
and System.String^
)
Upvotes: 4
Reputation: 2369
If you are using C++/CLI, you can return System::String too. You can construct the System::String from getString().c_str()
Upvotes: 3