的传统方式来做到这一点是:
__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#方法,知道如何处理一个字符串的原始指针(例如通过使用Marshal.PtrToStringAnsi
),并且C++代码使用它。请注意,在C++/CLI中执行此操作会更容易(因为您不需要委托,C++/CLI可以同时使用std::string
和System.String^
)
我认为返回'std: :string' - 除非您使用manage C++,否则C#CLR只能处理基元。我会选择后者(将'char *'传递给C++实现) – NirMH
正如你写的那样...只要记住'strncpy'不能保证添加终止'\ 0',所以你应该手动添加它。 – xanatos
@xanatos:非常感谢您的帮助!我该如何在C#中声明缓冲区?字节[]还是我使用StringBuilder? – Andy