Search code examples
c#c++pinvokemarshallingcom-interop

P/Invoking int[] from c++ to C# shows random large numbers instead of original array members


I have following c++ code:

int nCount[5] = {0, 1, 2, 3, 4};    
return &nCount[0];

I am receiving this code in C# P/Invoke layer with following code:

[DllImport(@"/sharedlibrary.so",CallingConvention = CallingConvention.Cdecl)]                                
 public extern static IntPtr read_array_adapter();

Which works well. But when I run c# code below:

int[] result = new int[5];
Marshal.Copy(ptr, result, 0, 5); 

It populates the array result with some random large numbers which looks like below:

int[]{1663918692,1852139884,1970351988,1936417641,244554078}

which has no relation with my original array in C++ which looks like {0,1,2,3,4} Any idea what Marshal.Copy may be doing to populate these kind of results?


Solution

  • If you want memory to survive a function scope, and don't want to use global variables, then you must allocate it from elsewhere than the stack (local variable).

    You can use any allocator, you can use one that .NET already knows, or if you use another one specific to C++ or your platform, then you must also provide another P/Invokable function for deallocation, something like this:

    C++

    int* read_array_adapter()
    {
        int nCount[5] = {0, 1, 2, 3, 4};    
        return AllocateAndCopy(...);
    }
    
    void free_array_adapter(int *) // or a generic pointer of course...
    {
        Free(...);
    }
    

    C#

    static void Main(string[] args)
    {
        var ptr = read_array_adapter();
        var result = new int[5];
        Marshal.Copy(ptr, result, 0, 5);
        free_array_adapter(ptr);
    }
    
    [DllImport(@"/sharedlibrary.so",CallingConvention = CallingConvention.Cdecl)]                                
    public extern static IntPtr read_array_adapter();
    
    [DllImport(@"/sharedlibrary.so",CallingConvention = CallingConvention.Cdecl)]                                
    public extern static void free_array_adapter(IntPtr ptr);
    

    You can also use a known allocator between .NET and C/C++, but this depends on the platform (Windows, linux, etc.): https://www.mono-project.com/docs/advanced/pinvoke/

    Here is a sample implementation with C's malloc/free duo:

    int* read_array_adapter()
    {
        int nCount[5] = { 0, 1, 2, 3, 4 };
        int* p = (int*)malloc(5 * 4);
        memcpy(p, nCount, 5 * 4);
        return p;
    }
    
    void free_array_adapter(void * ptr)
    {
        free(ptr);
    }