Pass Byte Array from Unity C# to C++ Plugin

Pass byte array from Unity C# to C++ plugin

Few things you need to do with your current code:

1.You have to send the size of the array to the C++ plugin in another parameter as UnholySheep mentioned.

2.You also have to pin the array before sending it to the C++ side. This is done with the fixed keyword or GCHandle.Alloc function.

3.If the C++ function has a void return type, your C# function should also have the void return type.

Below is a corrected version of your code. No additional memory allocation is performed.

C#:

[DllImport("LibName", CallingConvention = CallingConvention.Cdecl)]
static extern void ProcessData(IntPtr data, int size);

public unsafe void ProcessData(byte[] data, int size)
{
//Pin Memory
fixed (byte* p = data)
{
ProcessData((IntPtr)p, size);
}
}

C++:

extern "C" 
{
__declspec(dllexport) void ProcessData(unsigned char* data, int size)
{
//sizeof(data) is always 8
}
}

Passing byte array from Unity to Android (C++) for modification

Firstly, you did not provide any information about your configuration. What is your scripting backend: Mono or IL2CPP?

Secondly, why don't you call C++ code directly from C#?

1) Go to: [File] > [build Settings] > [Player Settings] > [Player] and turn on [Allow 'unsafe' Code] property.

2) After building the library, copy the output .so file(s) into the Assets/Plugins/Android directory in your Unity project.

Sample Image

C# code:

using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices;
using System;

public class CallNativeCode : MonoBehaviour
{
[DllImport("NativeCode")]
unsafe private static extern bool PostprocessNative(int width, int height, short* data_out);

public short[] dataShortOut;
public Text TxtOut;

public void Update()
{
dataShortOut = new short[100];
bool o = true;

unsafe
{
fixed (short* ptr = dataShortOut)
{
o = PostprocessNative(10, 10, ptr);
}
}

TxtOut.text = "Function out: " + o + " Array element 0: " + dataShortOut[0];
}
}

C++ code:

#include <stdint.h>
#include <android/log.h>

#define LOG(...) __android_log_print(ANDROID_LOG_VERBOSE, "HamidYusifli", __VA_ARGS__)

extern "C"
{
bool PostprocessNative(int width, int height, short *data_out)
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
data_out[x + y * width] = 10;
}
}

LOG("Log: %d", data_out[0]);

// Changing the return value here is correctly reflected in C#.
return false;
}
}

Unity, c++ native plugin mismatching byte array

Your return type is byte[] in C# but vector<unsigned char> in C++. These don't match. In your other questions, you were encouraged to fill the array instead of returning it but you still want to return an array, this is how to do it:

Convert the Vector to array then return it. The C++ return type should be char* and the C# return type should be IntPtr. Also, you need a way to tell C# the size of the array. You can do this with an argument. On the C# side, you have to create new array again with the size returned by that argument. After this, use Marshal.Copy to copy the data from IntPtr to that new array.

C++:

char* getCPPOutput(int* outValue)
{
//Convert the Vector to array
char* vArrray = &some_vector[0];
*outValue = some_vector.size();
return vArrray;
}

C#:

[DllImport("MySharedObj", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getCPPOutput(out int outValue);

//Test
void Start()
{
int size = 0;

//Call and return the pointer
IntPtr returnedPtr = getCPPOutput(out size);

//Create new Variable to Store the result
byte[] returnedResult = new byte[size];

//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, size);

//The returned value is saved in the returnedResult variable

}

Note about your C++ code:

I did not see your some_vector variable declaration. If this is declared in that function as a local variable then it's on the stack and you have to allocate new array dynamically with the new keyword and create a another function to free it with the delete keyword after receiving it on the C# side. You can't return array on the stack unless it is declared as a static object or dynamically allocated with the new keyword.

Any proper way of sending byte data to Unity3D from a C plugin?

Base-64 (or another similar base) is the correct way to do this; you cannot use an encoding here (such as UTF8) - an encoding is intended to transform:

arbitrary string <===encoding===> structured bytes

i.e. where the bytes have a defined structure; this is not the case with protobuf; what you want is:

arbitrary bytes <===transform===> structured string

and base-64 is the most convenient implementation of that in most cases. Strictly speaking, you can sometimes go a bit higher than 64, but you'd probably have to roll it manually - not pretty. Base-64 is well-understood and well-supported, making it a good choice. I don't know how you do that in C, but in Unity it should be just:

string s = Convert.ToBase64String(bytes);

Often, you can also avoid an extra buffer here, assuming you are serializing in-memory to a MemoryStream:

string s;
using(var ms = new MemoryStream()) {
// not shown: serialization steps

s = Convert.ToBase64String(ms.GetBuffer(), 0, (int)ms.Length);
}

Passing byte array from C++ unmanaged dll to C# unity

There are many ways to return byte arrays from C# and below is one of them. The memory allocation and de-allocation are both done in C++. You must call the function to free the memory from C#. I made the example very simple so that you can easily integrate it in your current code.

IntPtr is the key in this answer.

C++:

char* getByteArray() 
{
//Create your array(Allocate memory)
char * arrayTest = new char[2];

//Do something to the Array
arrayTest[0]=3;
arrayTest[1]=5;

//Return it
return arrayTest;
}

int freeMem(char* arrayPtr){
delete[] arrayPtr;
return 0;
}

C#:

[DllImport("Test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr getByteArray();

[DllImport("Test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int freeMem(IntPtr ptr);

//Test
void Start() {
//Call and return the pointer
IntPtr returnedPtr = getIntArray();

//Create new Variable to Store the result
byte[] returnedResult = new byte[2];

//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, 2);

//Free native memory
freeMem(returnedPtr);

//The returned value is saved in the returnedResult variable
byte val1 = returnedResult[0];
byte val2 = returnedResult[1];
}


Related Topics



Leave a reply



Submit