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

3
  • 4
    Possible duplicate of Returning an int array in C++/CLI to c# .NET Commented Nov 3, 2016 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. Commented Nov 3, 2016 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 Commented Nov 3, 2016 at 10:48

1 Answer 1

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]);  
  }
}
Sign up to request clarification or add additional context in comments.

15 Comments

@mohsen Is it fixed your problem or more help required?
thank you @Nikita and sorry. i try this code and get Cannot marshal 'parameter #1': Cannot use SizeParamIndex for ByRef array parameters error.
@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);
Thank you @Nikita. it's very good! unfortunately i can't vote the answer
@user1000247 Yes, you should manually free the memory. Use Marshal.FreeCoTaskMem(data);
|

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.