C# Wrap C++
项目中遇到Unity(C#)层调用C++实现的算法,下面记录实现方法
1. 简单接口-传入struct
C++实现接口:
#define JNAAPI extern "C" __declspec(dllexport) // C方式导出函数
//传递的实际的struct
struct CCtest {
int id;
int x;
};
//nLen 传入的可写大小
//return: 实际的写入的数目
JNAAPI int getValue(CCtest * test, int nLen);
int gloabl_id = 10; //全局变量,测试结果:常驻内存
// 实际的实现
int getValue(CCtest * test, int nLen) {
int tag_num = 5;
tag_num = tag_num > nLen ? nLen : tag_num;
for (int i = 0; i < tag_num; i++) {
test[i].id = gloabl_id;
test[i].x = 100;
gloabl_id++;
}
return tag_num;
}
C#的调用:
// 需定义完全对应于C++的struct
[StructLayout(LayoutKind.Sequential)]
public struct CCtest {
public int id;
public int x;
}
// 将上述C++编译成dll后放到C#工程,命名为getValue.dll
// EntryPoint是函数名称
[DllImport("getValue.dll", EntryPoint = "getValue")]
public static extern int getValue(IntPtr p, int nLen);
static void Main(string[] args)
{ // 调用中,先分配了10个的长度
IntPtr pt = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(CCtest)) * 10);
int tag_num = getValue(pt, 10);
for (int i = 0; i < tag_num; i++) {
CCtest res = new CCtest();
res = (CCtest)Marshal.PtrToStructure( // 按照偏移量取C++返回的struct
(IntPtr)(pt.ToInt64() + i * Marshal.SizeOf(typeof(CCtest))), typeof(CCtest));
Console.WriteLine("{0} {1}\n", res.id, res.x);
}
Marshal.FreeHGlobal(pt);
}
2. 复杂接口-传入二维数组
上述的实现是在C#中分配内存,给C++中写。 若C#事先不知道会返回struct的数目,将分配空间的任务交给C++,则需要传递二维指针:
JNAAPI int getValue(CCtest** tags) {
if (!tags) return 0;
m.lock();
size_t size = results.size();
if (size > 0) {
*tags = new CCtest[size]; // 需要另外的函数releaseTags delete tags
std::copy(results.begin(), results.end(), *tags);
}
m.unlock();
return size;
}
// release memory created in getTags防止内存泄漏
JNAAPI void releaseTags(CCtest* tags) { delete tags; }
C#的调用:
[DllImport("apriltag.dll")]
private static extern int getValue(out IntPtr p);
// C#函数调用上述接口
public static List<CCtest> getValue() {
IntPtr arrayValue;
var size = getValue(out arrayValue); // out代表二维指针
var list = new List<CCtest>();
if (size <= 0) return list;
var ptr = arrayValue;
var structSize = Marshal.SizeOf(typeof(CCtest));
for (var i = 0; i < size; i++) {
var cur = (CCtest)Marshal.PtrToStructure(ptr, typeof(CCtest));
list.Add(cur);
ptr = new IntPtr(ptr.ToInt64() + structSize); // 往后便宜指针
}
releaseTags(arrayValue); // 释放C++中的内存
return list;
}
3. 其他-return bool
若C++中返回bool类型,由于其为1byte,而C#默认接受的是4byte,因此会始终返回true,解决:
[DllImport("a.dll")]
[return: MarshalAs(UnmanagedType.I1)] // 指示返回的是1byte,以便正确解析
public static extern bool start();