Returning a byte array to c# from objc

Hi

I’m trying to create a plugin that lets you grab images from the ALAssetLibrary in the photo album. I’ve made pretty good progress considering I’m very new to objective c. The specific problem I’m having now is that I have the bytes for a jpeg, and I just need to return them back to my c# script.

The objective c:

UIImage *image = [UIImage imageWithCGImage: [asset thumbnail]];
NSData* pictureData =  UIImageJPEGRepresentation (image, 1.0);    
//return (const char*)[pictureData bytes];
//return (unsigned char*)[pictureData bytes];
return (Byte*)[pictureData bytes];

The C#:

[DllImport ("__Internal")]
private static extern byte[] _getjpeg(string unique_id);

void applytexture() {
      System.Byte[] thebytes = _getjpeg("pleh");
      var texture = new Texture2D(1,1);
      Debug.Log("Bytes length is "+thebytes.Length);
      texture.LoadImage(thebytes);
      renderer.material.mainTexture = texture;
}

The length of the bytes I get back in C# is 16777217. No idea what it is, but its not the 12k I saw in the XCode debugger.

Thanks.

I’ve copied this to answers.unity3d.com for anyone who cares to respond:

1 Like

I think it’s because you’re trying to return an unmanaged array. Try calling a C# static function with a byte[ ] parameter from native code. This might help: http://www.tinytimgames.com/2010/01/10/the-unityobjective-c-divide/

Thanks for the reply, but would prefer to go a simpler route. Here is what I’ve managed to put together so far, but I’m now getting a EXC_BAD_ACCESS. Heres the code so far:

ObjC/C++ code:

int _getjpeg(const char* url,unsigned char** returnbytes) {

    ALAsset* asset = [_pictures objectForKey:[NSString stringWithUTF8String:url]];

    if(asset != NULL)
        NSLog(@"_getjpeg() found URL: %@",[NSString stringWithUTF8String: url]);
    else {
        NSLog(@"_getjpeg() could not find URL: %@",[NSString stringWithUTF8String: url]);
        return NULL;
    }

    UIImage *image = [UIImage imageWithCGImage: [asset thumbnail]];
    NSData* pictureData =  UIImageJPEGRepresentation (image, 1.0);

    picturesize = (int)[pictureData length];

    unsigned char* bytes = (unsigned char*)[pictureData bytes];

    // This test does not give EXC_BAD_ACCESS
    *returnbytes[5] = (unsigned int)3;

    for(int i=0 ; i < picturesize ; i++) {
        // below lines gives EXC_BAD_ACCESS
        *returnbytes[i] = bytes[i];            
    }

    NSString* debugstr  = [NSString string];

    for(int i=5000; i < 5020 ; i++) {
        unsigned char byteint = bytes[i];
        debugstr = [debugstr stringByAppendingString:[NSString stringWithFormat:@"%i ",byteint]];

    }


    NSLog(@"bytes %s",[debugstr UTF8String]);
    return picturesize;
}

C# code:

[DllImport ("__Internal")]
private static extern int _getjpeg(string url,ref IntPtr thebytes); 

void somefunction(string image_id) {
    int maxsize = 50000;

    byte[] thebytes = new byte[maxsize];
    IntPtr byteptr = Marshal.AllocHGlobal(maxsize);

    int imagesize = _getjpeg(image_id,ref byteptr);

    Debug.Log("Getting _picturesize()... "+ image_id);
    int picsize = _picturesize(); 

    Marshal.Copy(byteptr,thebytes,0,picsize);   

    var texture = new Texture2D(1,1);

    string bytedebug = "";
    for (int i=5000 ; i < 5020 ; i++)
        bytedebug+=thebytes[i] + " ";

    Debug.Log("Bytes length is "+imagesize);
    Debug.Log("Bytes content is "+bytedebug);
}

The following code works:

ObjC/C++

int _testArray(int* dataPtr)
{
    int len = 5;
    unsigned char* data = (unsigned char*)malloc(len);
    for (int i = 0; i < len; i++)
        data[i] = i;
    *dataPtr = (int)data;
    return len;
}

C#

[DllImport ("__Internal")]
private static extern int _testArray(out IntPtr p);
void Start()
{
	IntPtr unmanagedPtr;
	int size = _testArray(out unmanagedPtr);
	byte[] mangedData = new byte[size];
	Marshal.Copy(unmanagedPtr, mangedData, 0, size);
	for (int i = 0; i < size; i++)
		print(string.Format("managedData[{0}]:{1}", i, mangedData[i]));
	//don't forget to free the unmanaged memory
	Marshal.FreeHGlobal(unmanagedPtr);
}

Output:

Excellent. Thanks hencz. This has been holding me up for a few days.

I had this problem. Could you tell me how to fix it? Thank you!

I was getting EXC_BAD_ACCESS (SIGABRT) when using hencz’s code:

 libmonobdwgc-2.0.dylib            0x00000001595682b8 ves_icall_System_Runtime_InteropServices_Marshal_copy_from_unmanaged

I got it working on Unity 2019.4.7 by changing the native argument type to be a pointer to int array instead of pointer to int:

Obj-C

// int* is an array, int** is pointer to array.
int GetBytes(int** dataPtr)
{
        int len = 5;
        unsigned char* data = (unsigned char*)malloc(len);
        for (int i = 0; i < len; i++)
                data[i] = i;
        // Don't care about array type; cast to same pointer type.
        *dataPtr = (int*)data;
        return len;
}

C#

[DllImport("PluginName", CallingConvention = CallingConvention.Cdecl)]
private static extern int GetBytes(out IntPtr p);
void Start()
{
    IntPtr unmanagedPtr;
    int len = GetBytes(out unmanagedPtr);
    byte[] mangedData = new byte[len];
    Marshal.Copy(unmanagedPtr, mangedData, 0, len);
    for (int i = 0; i < len; i++)
        Debug.Log(string.Format("managedData[{0}]:{1}", i, mangedData[i]));
    Marshal.FreeHGlobal(unmanagedPtr);
}