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();
Loading Disqus comments...
Table of Contents