我在C ++中有以下函数,我想用C#调用:
loadError
我如何做到这一点(使用Pinvoke)?
我是否正确假设从C ++到C#的返回分配内存有问题,并且在C#中分配大字符串然后将其传递给C ++以进行写入会更好(更容易)?
在这种情况下,我想我应该用C?包装C ++函数:
std::string getString();
答案 0 :(得分:4)
这样做的经典方法是:
__declspec(dllexport) void get_string(int size, char *buffer)
{
std::string str = getString();
strncpy(buffer, str.c_str(), size);
buffer[size - 1] = '\0';
}
C#方:
[DllImport("NativeLibrary", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void get_string(int len, [Out] StringBuilder sb);
然后(非常重要!你必须预先调整StringBuilder
)
var sb = new StringBuilder(100);
get_string(sb.Capacity, sb);
string str = sb.ToString();
您可以使用char[]
,但它更复杂,因为您必须手动“修剪”\0
。
使用更少的内存副本有一种更复杂的方法...但它有点复杂:
C ++方面:
__declspec(dllexport) void get_string(void (*func)(const char*))
{
std::string str = getString();
func(str.c_str());
}
C#方:
[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);
}
}
然后你这样使用:
var sfip = new StringFromIntPtr();
get_string(sfip.FromIntPtr);
string str = sfip.Value;
关键是你向C ++代码传递一个C#方法的委托,该方法知道如何处理一个字符串的原始指针(例如,通过使用Marshal.PtrToStringAnsi
),而C ++代码使用它。请注意,在C ++ / CLI中执行此操作要容易得多(因为您不需要委托,C ++ / CLI可以同时使用std::string
和System.String^
)
答案 1 :(得分:3)
如果您使用的是C ++ / CLI,则也可以返回System :: String。您可以从getString()。c_str()
构造System :: String