0

So I'm working on this c library thing called cROS however I want to retrieve a byte[] from C# so that I can share it using the library and I'm have trouble passing a byte[] to a char*.

C# code

[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 code

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);
}
11
  • 1
    I'm not good at C#, but is passing byte[] to return of a function whose return type seems byte valid in C#? Commented Mar 3, 2018 at 16:11
  • May be ---related--- dupe? Commented Mar 3, 2018 at 16:12
  • Although your code use C not C++, the other question have extern "C", so it may be the same problem. Commented Mar 3, 2018 at 16:13
  • 1
    The delegate declaration needs an [UnmanagedFunctionPointer] attribute to declare it cdecl. You can't return a byte[] since the garbage collector is going to move it around, consider Marshal.AllocHGlobal() or having the C code pass a pointer to its own buffer and its length as arguments. The delegate object passed to the C code needs to be stored in a static variable so the GC can't collect it. Commented Mar 3, 2018 at 16:20
  • 1
    David : Can't you say anything positive? You never give a answer, just negative comments. The code I posted i just a snippet. Commented Mar 4, 2018 at 8:54

2 Answers 2

0

You cannot return a byte[] array if you have declared a return type of byte. I don't see what the signature of the function should be (ie. what is the definition of "unity_get_camera_screenshot_callback"?), but probably you can just change it to

private static byte[] get_camera_screenshot(int resWidth, int resHeight)
{
    ...
    byte[] bytes = screenShot.EncodeToPNG();
    return bytes;
}
Sign up to request clarification or add additional context in comments.

Comments

0

Okay, there are several problems with this interop scenario, so we'll start with the byte[]. The way an array of bytes is marshalled to unmanaged code that accepts a char* is by using an IntPtr, like this:

private static IntPtr funcCalledByNativeCode()
{
  byte[] bytes = getSomeBytes();
  IntPtr retVal = Marshal.AllocHGlobal(bytes.Length);
  Marshal.Copy(bytes, 0, retVal, bytes.Length);
  return retVal;
}

Now, here's the rub. The memory allocated by the call to Marshal.AllocHGlobal() needs to be freed by a call to Marshal.FreeHGlobal(). Since you using callbacks, probably the easiest thing to do is to register a C-style destructor with the native code that it can call whenever it is finished with a block of interop memory.

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void InteropDtor(IntPtr pMem);

Which you would implement like:

private static void myInteropKiller(IntPtr allocatedMemory)
{
  Marshal.FreeHGlobal(allocatedMemory);
}

Another (and simpler) way is to have the native code allocate the buffer on its side and provide it in the callback function, which you would just fill on the managed side, like this:

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);
  }
}

The other main problem is that when passing function pointers to native code from managed code, you have to make sure that the GC doesn't collect the delegate instance before the unmanaged code has a chance to use it. Normally when you call a method that expects a delegate, you just supply the name of the function that you want to use. That creates a temporary delegate instance that the GC will track just like any other object reference. The problem is that if you pass that delegate over the managed/unmanaged layer, the GC can lose all references to it, so it will collect it.

So any time you have a delegate that you are going to pass to native code as a callback, besides decorating the delegate definition with the UnmanagedFunctionPointer attribute, you need to define a static variable that will hold the actual delegate reference for the implemented method.

[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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.