How do I call a C ++ function that sets the values โ€‹โ€‹of an int array from C #?

I have the following function in C ++ inside a .dll:

extern "C"
{
   __declspec(dllexport) void ColorRamps_getColorRampTable(int n, int *table);
}

      

It takes in an array with n elements and sets the values โ€‹โ€‹of the array.

How do I call this from C #?

I understand that I must first DllImport function (?):

[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int[] table);
public static void getColorRampTable(int[] table)
{
    int n = table.Length;
    ColorRamps_getColorRampTable(n, table);
}

      

Is it correct?

When I call getColorRampTable (int [] table), should I bind the array? How to do it?

I tried:

int[] table = new int[164];
GCHandle handle = GCHandle.Alloc(table, GCHandleType.Pinned); 
getColorRampTable(table);
handle.Free();

      

+3


source to share


1 answer


You have two options. One is to rely on .NET marshaller, the other is to use unsafe code.

Both are actually pretty straightforward for your case. Marshaller:

[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, [In, Out] int[] table);

public static void getColorRampTable(int[] table)
{
    int n = table.Length;
    ColorRamps_getColorRampTable(n, table);
}

      

This has a small overhead where the marshaller first copies the array to unmanaged memory and then copies it back to your array again. EDIT: As Xanatos correctly pointed out, since it int[]

is a blittable type, the marshaller actually cheats and passes a pointer to your actual array. Or at least this is the documented behavior.

If this is a performance issue for you (and it is a real, measured issue - don't do it otherwise), you can pass a pointer to a .NET array directly using unsafe code:

[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int* table);

public unsafe static void getColorRampTable(int[] table)
{
    int n = table.Length;
    fixed (int* pTable = &table)
    {
      ColorRamps_getColorRampTable(n, pTable);
    }
}

      

The class must be tagged with a keyword unsafe

and you need to enable unsafe code in your project settings.

EDIT:



As above, this doesn't really apply for the case int[]

. I'm going to leave it here because this is true for non-blittable types, but int[]

is blittable and the marshaller should just pass the pointer directly, handling the pinning for you. You should still use the attribute [In, Out]

, although although it will behave like In / Out by default, the contract should be well defined.

The main difference is that in the first case, the C (++) code never has access to any of your managed memory - it always works with copies of the data. By adding attributes [In, Out]

, you are telling the marshaler not only to copy the data, but to copy it after the call completes, otherwise your array table

will never change.

On the other hand, the second option is passing a pointer to the actual array that you are using on the C # side - this may or may not be faster, depending on too many things, and it is usually a little more dangerous (very careful to specify the correct array length, eg).

Another EDIT:

It is also possible to use IntPtr table

P / Invoke in the method declaration, which gives you additional flexibility. The simplest example is to simply bind an array, which is equivalent to the case fixed

:

var hTable = GCHandle.Alloc(table, GCHandleType.Pinned);

try
{
  ColorRamps_getColorRampTable(n, hTable.AddrOfPinnedObject());
}
finally
{
  hTable.Free();
}

      

And even allowing you to explicitly control copying easily:

var pTable = Marshal.AllocHGlobal(sizeof(int) * table.Length);

try
{
  Marshal.Copy(table, 0, pTable, table.Length);

  ColorRamps_getColorRampTable(n, pTable);

  Marshal.Copy(pTable, table, 0, table.Length);
}
finally
{
  Marshal.FreeHGlobal(pTable);
}

      

This is a decent workaround if for some reason you cannot use the code unsafe

, but remember that it is used as "unsafe" like code unsafe

in most scenarios. In truth, since you interact with native code, you are always (somewhat) insecure - a mistake in native code can easily damage your application.

+2


source







All Articles