6

I have a C++ library method that I'd like to call from a Unity C# script.

I understand there are three key steps. First, to declare the C++ methods to be extern "C". Second, to use [DllImport('foobar')] before the corresponding C# extern method declaration. Third, to name the library file with the lib prefix (e.g. libfoobar.so) and place it in the Unity plugins folder.

So far so good -- if I'm only passing simple parameter types like int from C# into C++. However, to pass a byte[] parameter, I will need to account for the different ways C# and C++ handle memory and pointers. I haven't been able to find a definitive example of how to this should be done.

My question: how to pass a byte[] from a Unity C# script into a external C++ library method?

9
  • Is this the P/Invoke method? You can also use C++ CLI. I have heard that C++ CLI gives you more control. But, I've never done it. Take it with a grain of salt. Commented Mar 20, 2015 at 16:16
  • 1
    Don't forget CallingConvention = CallingConvention.Cdecl from your DllImport since you're using extern "C" Commented Mar 20, 2015 at 16:17
  • @Golazo - not using P/Invoke, simply declaring the C++ extern to be extern "C" and referencing it from C# via the DllImport attribute. No P/Invoke is needed, at least not to pass in simple parameter types. Commented Mar 20, 2015 at 16:17
  • Is this managed C++? Because to my knowledge, you cannot call native C++ code in C# without P/Invoke, C++ CLI or COM Commented Mar 20, 2015 at 16:18
  • 1
    you may want to have a look at this: snowbolt.com/index.php/blog/28-tech/91-pinvoke Commented Mar 20, 2015 at 19:45

2 Answers 2

7

You have to change your method definition to take an IntPtr. This is the C# way to define a pointer to non-managed memory. To create this pointer use Marshal.AllocHGlobal() and then copy the data to it using Marshal.Copy().

Example (not tested):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Assets.Scripts
{
    public class DLLCall
    {

        [DllImport("thedll")]
        public static extern void TheCall(IntPtr byteArray, int size);

        public void PerformCall(byte[] data)
        {
            IntPtr unmanagedArray = Marshal.AllocHGlobal(data.Length);
            Marshal.Copy(data, 0, unmanagedArray, data.Length);

            TheCall(unmanagedArray, data.Length);

            Marshal.FreeHGlobal(unmanagedArray);
        }

    }
}

Notice that you have to free the unmanaged memory manually by using Marshal.FreeHGlobal. After this the memory is no longer valid so the C++ library can't use it any more. If it needs to access the array at a later point remove the FreeHGlobal call and make sure to delete the memory after the library no longer requires access to it.

Sign up to request clarification or add additional context in comments.

Comments

3

works for me:

in unity:

[DllImport ("dllplugin")]
public static extern void Method (byte[] bytes);

in c++ Plugin

#define EXPORT_API __declspec(dllexport)

extern "C" void EXPORT_API Method(unsigned char* bytes)

5 Comments

I am passing the byte array from unity to c++ plugin like this way, It works fine but untiy sending the 17 bytes otherside(C++) receive 4 bytes only.what is the reason?Please Help
@Rajesh Same problem here. Passing 4096 bytes from C# to C++, but on the C++ end the array is only ever 8 bytes according to sizeof(bytes).
Additionally, changing it to a unsigned char* rather than a char just makes sizeof 8 while the length of the array is 1.
I know this is an old post but in C++, sizeof(bytes) will only give you the size of the pointer and not the size of the data it points to, see the first answer on this post : stackoverflow.com/questions/399003/… . If you want to do it this way, you will need to also pass the length of the C# bytes array.
I edited the answer for the function to also have the length of the array as parameter. Note that in this case, we do not need element size since we know it is one byte per element. On the c++ side, the start of the buffer will be bytes and the end of the buffer will be bytes + length.

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.