20

I can't seem to figure out how to return an array from an exported C++ DLL to my C# program. The only thing I've found from googling was using Marshal.Copy() to copy the array into a buffer but that doesn't give me the values I'm trying to return, I don't know what it's giving me.

Here's what I've been trying:

Exported function:

extern "C" __declspec(dllexport) int* Test() 
{
    int arr[] = {1,2,3,4,5};
    return arr;
}

C# portion:

    [DllImport("Dump.dll")]
    public extern static int[] test();

    static void Main(string[] args)
    {

        Console.WriteLine(test()[0]); 
        Console.ReadKey();


    }

I know the return type int[] is probably wrong because of the managed/unmanaged differences, I just have no idea where to go from here. I can't seem to find an answer for anything but returning character arrays to strings, not integer arrays.

I figured the reason the values I'm getting with Marshal.Copy are not the ones I'm returning is because the 'arr' array in the exported function gets deleted but I'm not 100% sure, if anyone can clear this up that would be great.

3
  • stackoverflow.com/questions/3776485/… This may help you Commented Jul 13, 2013 at 21:50
  • 1
    Not good. Commented Jul 13, 2013 at 21:59
  • Thanks Chris, I was so caught up in this I forgot what I was doing. I'm going to change my code around a bit,this way I have my exported function take in the address to a buffer and fill the buffer from there. Commented Jul 13, 2013 at 22:16

2 Answers 2

20

I have implemented the solution Sriram has proposed. In case someone wants it here it is.

In C++ you create a DLL with this code:

extern "C" __declspec(dllexport) int* test() 
{
    int len = 5;
    int * arr=new int[len+1];
    arr[0]=len;
    arr[1]=1;
    arr[2]=2;
    arr[3]=3;
    arr[4]=4;
    arr[5]=5;
        return arr;
}

extern "C" __declspec(dllexport) int ReleaseMemory(int* pArray)
{
    delete[] pArray;
    return 0;
}

The DLL will be called InteropTestApp.

Then you create a console application in C#.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace DLLCall
{
    class Program
    {
        [DllImport("C:\\Devs\\C++\\Projects\\Interop\\InteropTestApp\\Debug\\InteropTestApp.dll")]
        public static extern IntPtr test();

        [DllImport("C:\\Devs\\C++\\Projects\\Interop\\InteropTestApp\\Debug\\InteropTestApp.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int ReleaseMemory(IntPtr ptr);

        static void Main(string[] args)
        {
            IntPtr ptr = test();
            int arrayLength = Marshal.ReadInt32(ptr);
            // points to arr[1], which is first value
            IntPtr start = IntPtr.Add(ptr, 4);
            int[] result = new int[arrayLength];
            Marshal.Copy(start, result, 0, arrayLength);

            ReleaseMemory(ptr);

            Console.ReadKey();
        }
    }
}

result now contains the values 1,2,3,4,5.

Hope that helps.

Sign up to request clarification or add additional context in comments.

18 Comments

It doesn't help. You'll need to show how you know the magic "5" and how you are going to release the array so there is no permanent memory leak.
@Hans, the question was "Return C++ array to C#". So, yes, it does help.
Okay, then just consider the comment a cautionary warning to the innumerable googlers that will find your answer some day and have no clue that the code leaks memory like a sieve and arbitrarily bombs with access violations.
@Gabriel The code doesn't leak memory. Memory allocated in C++ has to be deleted inside C++. C#'s GC has no clue about memory generated outside itself.
@Gabriel thanks, I think it was because I called ReleaseMemory before I was finished with it in the C# code.
|
0

More or less the same as the answer above, but this worked for me. For an array returned from a c method like this:

EXPORT char** methodname(void);

I did not succeed in getting this to work with the MarshalAs attribute, but this did the trick: (condensed code)

[DllImport(libName, EntryPoint = "methodname", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr methodname();

var listPtr = methodname();
var list = new List<string>();
IntPtr itemPtr = IntPtr.Zero;
var offset = 0;
while ((itemPtr = Marshal.ReadIntPtr(listPtr, offset)) != IntPtr.Zero) 
{
    Console.WriteLine(Marshal.PtrToStringAnsi(itemPtr));
    offset += 4;
}

Comments

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.