2

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

Community
  • 1
  • 1
mohsen
  • 25
  • 6
  • 4
    Possible duplicate of [Returning an int array in C++/CLI to c# .NET](http://stackoverflow.com/questions/27492520/returning-an-int-array-in-c-cli-to-c-sharp-net) – StoryTeller - Unslander Monica Nov 03 '16 at 10:19
  • @StoryTeller Mentioned link related to the question, but not a duplicate. Under the link, memory allocated on C++/CLI side via `gcnew`. It's not what OP asked for. – Nikita Nov 03 '16 at 10:40
  • thank you StoryTeller and Nikita. is it different between win32project and CLI? and i need some array in parameters not a return-value – mohsen Nov 03 '16 at 10:48

1 Answers1

3

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]);  
  }
}
Nikita
  • 6,270
  • 2
  • 24
  • 37
  • @mohsen Is it fixed your problem or more help required? – Nikita Nov 05 '16 at 08:39
  • thank you @Nikita and sorry. i try this code and get _Cannot marshal 'parameter #1': Cannot use SizeParamIndex for ByRef array parameters_ error. – mohsen Nov 05 '16 at 08:47
  • @mohsen Have you tried both, C# and C++ code from the answer? I've just run it and successfully got the buffer on C# side. I call the method in C# with this code: `int len; int[] data; getlog(out data, out len);` – Nikita Nov 05 '16 at 09:03
  • yes @Nikita, i am, and got that exception. then i remove _SizeParamIndex = 1_, program run and _length_ is good, _data_ created but has no element! – mohsen Nov 05 '16 at 09:16
  • just add _extern "C"_ on c++ side – mohsen Nov 05 '16 at 09:22
  • @mohsen without `SizeParamIndex` marshaler doesn't know the amount of elements in the created array. – Nikita Nov 05 '16 at 11:19
  • why it not let me set 'SizeParamIndex'? and why it's work for you? i use net3.5 and x86 build – mohsen Nov 05 '16 at 11:45
  • @mohsen It's an interesting question. I use net 4.0, x86. Will check it for 3.5 a bit later. – Nikita Nov 05 '16 at 12:01
  • @mohsen Great, if this solution fits you, you could mark the answer as correct. Otherwise we should search for some other way. – Nikita Nov 05 '16 at 12:13
  • OK @Nikita. i have to use net3.5 and i search. but your answer is correct, thank you very much. – mohsen Nov 05 '16 at 12:25
  • @mohsen I see, later today will try to find the proper way for your target .net version. – Nikita Nov 05 '16 at 12:29
  • @mohsen Check the updated answer. I've added information regarding usage of unmanaged arrays in .NET 3.5, together with code snippets to try. – Nikita Nov 06 '16 at 21:51
  • 1
    Thank you @Nikita. it's very good! unfortunately i can't vote the answer – mohsen Nov 07 '16 at 13:25
  • Do you have to free the memory when using NET3.5? – user1000247 Aug 04 '17 at 14:23
  • 1
    @user1000247 Yes, you should manually free the memory. Use `Marshal.FreeCoTaskMem(data);` – Nikita Aug 04 '17 at 16:45