2

I am trying to return the byte array from my unmanaged c++ dll back to c# unity. Thank you very much in advance for taking the time to help >< I'm really new to DLL in unity so I'm extremely confused how 2 languages are even suppose to work together.

CPP

The problem is in here, I've done my calculations, but I'm struggling to find a way to return it back to c# with an array format.

Currently, the byte array holds color codes e.g RGBA (223,124,23,255,212,143,234,255) and it repeats

#include "WebcamDLL.h"
#include <vector>

extern "C" {
int adjustBrightnesss(unsigned char* bytes, int sizeOfArray)
{
    std::vector<int> myvector;
    int alphaP = 0;
    for (int i = 0; i < sizeOfArray; i++) {
        switch (alphaP) {
        case 0:
        case 1:
        case 2:
            myvector[i] = bytes[i] / 2;
            alphaP++;
            break;
        case 3:
            alphaP = 0;
            break;
        }
    }
    return bytes;
}
}

Header file

#ifdef TESTFUNCDLL_EXPORT
#define TESTFUNCDLL_API __declspec(dllexport) 
#else
#define TESTFUNCDLL_API __declspec(dllimport) 
#endif

extern "C" {
    TESTFUNCDLL_API int adjustBrightnesss(unsigned char* bytes, int sizeOfArray);
}

C# File

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices; // Needed for custom DLL
using System;

public class WebcamManager : MonoBehaviour
{
    private RawImage ri; // Gets the RawImage component from script parent
    private WebCamTexture wct; // Object to hold the WebCamTexture add-on
    private AspectRatioFitter arf; // Ensures RawImage object has same scaling as Webcam
    private Color32[] pixels; // Keeps the pixels from webcamtexture
    Texture2D tex; // A placeholder for the texture2D

    [DllImport("WebcamBrightness", EntryPoint = "adjustBrightness")]
    public static extern int adjustBrightness(byte bytes, int b);

    void Start()
    {
        newBrightness = 1; // Default 1, if 0 will make image go super bright
        arf = GetComponent<AspectRatioFitter>(); // Gets the component AspectRatioFitter in script parent
        ri = GetComponent<RawImage>(); // Gets the component RawImage in script parent
        wct = new WebCamTexture(Screen.width, Screen.height); // Creates a new WebCamTexture in wct with the width and height of the current screen
        wct.Play(); // plays webcam

        //adjustBrightness(wct.GetPixels32());
    }

    // Update is called once per frame
    void Update()
    {
        float videoRatio = (float)wct.width / (float)wct.height; // Ensures that the scaling is the same as the webcam
        arf.aspectRatio = videoRatio; // applies webcam scaling to rawimage

        tex = new Texture2D(wct.width, wct.height, TextureFormat.ARGB32, false); // pass texture2D tex the information of webcamtexture height, width, ARGB32 (32 bit with alpha) and no mipmaps

        pixels = wct.GetPixels32();

// After getting the bytes, I wanna save it back to color32 or atleast an array format.

        tex.SetPixels32(pixels);
        tex.Apply();

        // Sets texture of rawimage from canvas to be web camera view
        ri.texture = tex;
    }
}

EDITTED CPP FILE

#include "WebcamDLL.h"
#include <vector>

extern "C" {
    unsigned char* adjustBrightness(unsigned char* bytes, int sizeOfArray)
    {
        int alphaP = 0;
        for (int i = 0; i < sizeOfArray; i++) {
            switch (alphaP) {
            case 0:
            case 1:
            case 2:
                bytes[i] = bytes[i] / 2;
                alphaP++;
                break;
            case 3:
                alphaP = 0;
                break;
            }
        }
        return bytes;
    }

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

EDITTED HEADER FILE

#ifdef TESTFUNCDLL_EXPORT
#define TESTFUNCDLL_API __declspec(dllexport) 
#else
#define TESTFUNCDLL_API __declspec(dllimport) 
#endif

extern "C" {
    TESTFUNCDLL_API unsigned char* adjustBrightness(unsigned char* bytes, int sizeOfArray);
    TESTFUNCDLL_API int freeMem(unsigned char* arrayPtr);
}

EDITTED C# FILE

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.Runtime.InteropServices; // Needed for custom DLL
using System;

public class WebcamManager : MonoBehaviour
{
    private RawImage ri; // Gets the RawImage component from script parent
    private WebCamTexture wct; // Object to hold the WebCamTexture add-on
    private AspectRatioFitter arf; // Ensures RawImage object has same scaling as Webcam
    private Color32[] pixels; // Keeps the pixels from webcamtexture
    Texture2D tex; // A placeholder for the texture2D
    public RawImage ri2;

    float timer;

    [DllImport("WebcamBrightness", EntryPoint = "adjustBrightness", CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr adjustBrightness(byte[] bytes, int b);

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

    [SerializeField]
    int newBrightness;

    void Start()
    {
        newBrightness = 1; // Default 1, if 0 will make image go super bright
        arf = GetComponent<AspectRatioFitter>(); // Gets the component AspectRatioFitter in script parent
        ri = GetComponent<RawImage>(); // Gets the component RawImage in script parent
        wct = new WebCamTexture(Screen.width, Screen.height); // Creates a new WebCamTexture in wct with the width and height of the current screen
        wct.Play(); // plays webcam

        float videoRatio = (float)wct.width / (float)wct.height; // Ensures that the scaling is the same as the webcam
        arf.aspectRatio = videoRatio; // applies webcam scaling to rawimage
        tex = new Texture2D(wct.width, wct.height, TextureFormat.ARGB32, false); // pass texture2D tex the information of webcamtexture height, width, ARGB32 (32 bit with alpha) and no mipmaps
    }

    // Update is called once per frame
    void Update()
    {
        //timer += Time.deltaTime;

        pixels = wct.GetPixels32();

        IntPtr returnedPtr = adjustBrightness(Color32ArrayToByteArray(pixels), Color32ArrayToByteArray(pixels).Length);

        byte[] returnedResult = new byte[Color32ArrayToByteArray(pixels).Length];

        Marshal.Copy(returnedPtr, returnedResult, 0, Color32ArrayToByteArray(pixels).Length);

        freeMem(returnedPtr);

        Debug.Log(returnedResult[0]);

        tex.SetPixels32(pixels);

        ri.texture = tex;

        tex.Apply();

        // Sets texture of rawimage from canvas to be web camera view
    }

    public void AdjustBrightness(float b)
    {
        newBrightness = (int)b;
    }

    private static byte[] Color32ArrayToByteArray(Color32[] colors)
    {
        if (colors == null || colors.Length == 0)
            return null;

        int lengthOfColor32 = Marshal.SizeOf(typeof(Color32));
        int length = lengthOfColor32 * colors.Length;
        byte[] bytes = new byte[length];

        GCHandle handle = default(GCHandle);
        try
        {
            handle = GCHandle.Alloc(colors, GCHandleType.Pinned);
            IntPtr ptr = handle.AddrOfPinnedObject();
            Marshal.Copy(ptr, bytes, 0, length);
        }
        finally
        {
            if (handle != default(GCHandle))
                handle.Free();
        }
        return bytes;
    }
}

2 Answers 2

7

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];
}
Sign up to request clarification or add additional context in comments.

7 Comments

Hi!, Thank you very much for the help! I seem to have managed to bring the byte array back using the solution you've given, but I'm having troubles with the freemem function, whenever I uncommented the delete[] arrayPtr;, it seems to crash my unity without giving any errors. I've edited my post with the new code, I feel like there was afew parts that I messed up trying to integrate over ><
Once you call freeMem(returnedPtr); you can't and should not use the returnedPtr variable again until it initialized again with IntPtr returnedPtr = getIntArray();. The result is now in returnedResult. Another thing to do is to check if the pointer is null on the C++ side before deleting it. Add if (arrayPtr!= nullptr){delete[] arrayPtr;} to your freeMem function. Let me know if that solves your problem.
Oh! I get it, so freemem is only called when I want to disable my webcam(stop using the bytes) and is reinitialized when I want to use it again. Is that right?
Not really. I noticed there is a difference between my C++ code getByteArray function and the one you just modified into your question. I did new byte[2]; but you did not. You seem to be modifying the variable you passed into the function instead. in C++, if you use the new keyword, you must use the delete keyword. This is why C+ is complicated. I think you should learn C++ before working on this project. You will save your self so many bugs you will run into. See here for more info.
With the current code you have, you will run into problems since you are modifying the array you passed in. The purpose of my answer is to create new array byte[] returnedResult = new byte[sizeOfArray]; then copy the array that is passed in to that new local variable std::memcpy(returnedResult, bytes, sizeOfArray);. Now modify that cop and return it. You can then call freeMem after from C#.
|
0

You could pass an extra parameter to the function, let's say another byte array, and then in the adjustBrightnesss function replace that myvector with that array. As far as i know you will get the array with the modified values

1 Comment

Hi, Thanks for the help! but doesn't the extra parameter only acts as a way to bring items into a function? the weird problem is that I'm able to return back a singular int, or float, but not an array or vector. or atleast I don't know how to haha ><

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.