所以我正在研究这个名为cROS的c库,但是我想从C#中检索一个byte [],这样我就可以使用这个库来共享它了,而且我很难将一个byte []传递给一个char *。
C#代码
[DllImport("aavatar")] private static extern void unity_reg_get_camera_screenshot_callback(unity_get_camera_screenshot_callback callback_fn);
private static byte get_camera_screenshot(int resWidth, int resHeight)
{
...
byte[] bytes = screenShot.EncodeToPNG();
return bytes;
}
void Start(){
...
unity_reg_get_camera_screenshot_callback(new unity_get_camera_screenshot_callback(get_camera_screenshot));
}
C代码
void unity_reg_get_obj_coordinate_callback(unity_get_obj_coordinate_callback callback_fn)
{
Unity_get_obj_coordinate_fn= callback_fn;
}
CallbackResponse callback_srv_get_cam_screenshot(cRosMessage *request, cRosMessage *response, void* data_context)
{
...
char *ret_cam_screenshot;
ret_cam_screenshot = Unity_get_cam_screenshot_fn((int) scr_width32,(int)scr_height32);
}
如果已声明返回类型为byte,则不能返回byte []数组。我没看到函数的签名应该是什么(例如,“unity_get_camera_screenshot_callback”的定义是什么?),但是你可以把它改成
private static byte[] get_camera_screenshot(int resWidth, int resHeight)
{
...
byte[] bytes = screenShot.EncodeToPNG();
return bytes;
}
好的,这个互操作方案有几个问题,所以我们将从byte[]
开始。将字节数组编组为接受char*
的非托管代码的方式是使用IntPtr
,如下所示:
private static IntPtr funcCalledByNativeCode()
{
byte[] bytes = getSomeBytes();
IntPtr retVal = Marshal.AllocHGlobal(bytes.Length);
Marshal.Copy(bytes, 0, retVal, bytes.Length);
return retVal;
}
现在,这就是问题。调用Marshal.AllocHGlobal()
分配的内存需要通过调用Marshal.FreeHGlobal()
来释放。由于您使用回调,可能最简单的方法是使用本机代码注册C样式的析构函数,只要它完成了一个互操作内存块就可以调用它。
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void InteropDtor(IntPtr pMem);
您将实现如下:
private static void myInteropKiller(IntPtr allocatedMemory)
{
Marshal.FreeHGlobal(allocatedMemory);
}
另一种(也是更简单的)方法是让本机代码在其旁边分配缓冲区并在回调函数中提供它,您只需填写托管端,如下所示:
private static void funcCalledByNativeCode(IntPtr buffer, ref int length)
{
byte[] bytes = getSomeBytes();
lenth = bytes.Length;
if (bytes.Length <= length) // if this isn't enough, the native code can
// check 'length' for the required size
{
Marshal.Copy(bytes, 0, buffer, bytes.Length);
}
}
另一个主要问题是,当从托管代码传递函数指针到本机代码时,您必须确保在非托管代码有机会使用它之前GC不会收集委托实例。通常,当您调用需要委托的方法时,您只需提供要使用的函数的名称。这将创建一个临时委托实例,GC将像任何其他对象引用一样进行跟踪。问题是,如果您通过托管/非托管图层传递该代理,GC可能会丢失对它的所有引用,因此它将收集它。
因此,每当您有一个委托将要作为回调传递给本机代码时,除了使用UnmanagedFunctionPointer
属性修饰委托定义之外,还需要定义一个静态变量,该变量将保存已实现方法的实际委托引用。
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void Fn_CallbackFunction(IntPtr buffer, ref int length);
private static Fn_CallbackFunction CallbackOne = myCallback;
private static void myCallback(IntPtr buffer, ref int length)
{
// implementation
}
// when calling the native function that wants the callback function
// pointer, use the variable, not the method name:
nativeRegisterFunction(CallbackOne);