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?

Ghopper21
  • 10,287
  • 10
  • 63
  • 92
  • 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. – The Vivandiere Mar 20 '15 at 16:16
  • 1
    Don't forget `CallingConvention = CallingConvention.Cdecl` from your DllImport since you're using `extern "C"` – cbr Mar 20 '15 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. – Ghopper21 Mar 20 '15 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 – The Vivandiere Mar 20 '15 at 16:18
  • @GrawCube - in Unity, seems like setting the C# calling convention explicitly isn't necessary. Declaring the C# method as `extern` is enough. – Ghopper21 Mar 20 '15 at 16:19
  • @Ghopper21 interesting. – The Vivandiere Mar 20 '15 at 16:20
  • @Golazo - managed C# calling unmanaged C++, if I understand how Unity works correctly. – Ghopper21 Mar 20 '15 at 16:21
  • 1
    you may want to have a look at this: http://www.snowbolt.com/index.php/blog/28-tech/91-pinvoke – Alex Mar 20 '15 at 19:45
  • @Alex - thanks Alex, am going to take a close look at that blog. – Ghopper21 Mar 20 '15 at 20:01

2 Answers2

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.

Erwin
  • 178
  • 7
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)
  • 1
    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 Dec 01 '17 at 08:00
  • @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)`. –  Oct 14 '19 at 14:05
  • Additionally, changing it to a unsigned `char*` rather than a `char` just makes sizeof 8 while the length of the array is 1. –  Oct 14 '19 at 14:41
  • 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 : https://stackoverflow.com/questions/399003/is-the-sizeofsome-pointer-always-equal-to-four . If you want to do it this way, you will need to also pass the length of the C# bytes array. – Pierre Wargnier Jan 23 '20 at 09:27
  • 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. – Pierre Wargnier Jan 23 '20 at 14:24