mohsen mohsen - 1 month ago 13
C# Question

when C# call a c++ function, is it possible to initialize array in called c++ function and back to C#?

the c++ function know size of array and have to create it.

the c++ code:

__declspec(dllexport) int getlog(int* &data)
{
data = new int[5];

for (int i = 0; i < 5; i++)
{
data[i] = i;
}

return 0;
}


and c#:

[DllImport(@"myDll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int getlog(int[] data);


and

int[] arr = null;
getlog(arr);


i see this but it not helped

Answer

On C# side:

[DllImport(@"myDll.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int getlog([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] out int[] data, 
out int length);

On C++ side:

#include <Objbase.h>

extern "C" __declspec(dllexport) int getlog(int** data, int * length)
{
  *length = 5;
  int sizeInBytes = sizeof(int) * (*length);
  *data = static_cast<int*>(CoTaskMemAlloc(sizeInBytes ));

  for (int i = 0; i < 5; i++)
  {
    (*data)[i] = i;
  }

  return 0;
}

CoTaskMemAlloc will give CLR ability to take care of freeing the memory once it's no longer reachable. More about it in "Memory Management with the Interop Marshaler":

The runtime always uses the CoTaskMemFree method to free memory. If the memory you are working with was not allocated with the CoTaskMemAlloc method, you must use an IntPtr and free the memory manually using the appropriate method.

Method above works for .NET 4.0 and above. For .NET 3.5 it fails due to the limited support of unmanaged arrays:

The array being controlled cannot be passed as ref or out parameters. Likewise, the parameter containing the size of the array must be passed by value (the SizeParamIndex field cannot refer to a ref or out parameter)

To overcome this problem and get the array on the C# side use the following signature:

[DllImport(@"MyDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int getlog(out IntPtr data, out int length);

When you have IntPtr to the data it's possible to copy content to the managed array:

int length;
IntPtr data;
getlog(out data, out length);
int[] managedArr = new int[length];
Marshal.Copy(data, managedArr, 0, length);

Also you could process the content of such array without coping. To use this method enable "Allow unsafe code" in project settings:

int length;
IntPtr data;
getlog(out data, out length);

unsafe
{
  int* intArray = (int*)data.ToPointer();
  for (int i = 0; i < length; i++)
  {
    Console.WriteLine(intArray[i]);  
  }
}