简体   繁体   中英

DllImport, C/C++ -> C#, out array of doubles

I want to get array of doubles from C++.
I tested with array of ints, it works fine, but I don't know array of doubles cannot work.
The first and the second elements of array of doubles are wrong.

There is another weird thing, I comment out FreeCoTaskMem, because if I don't, the application will be crashed, ints and doubles are the same.

Any hint for me, thank you.

C/C++ code

#define DllExport __declspec(dllexport)
extern "C"
{       
   DllExport void TestOutOfInts(int** ints);
   DllExport void TestOutOfDoubles(double** doubles);
}

DllExport void TestOutOfInts(int** ints)
{
    int xyzs[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    (*ints) = xyzs;
}

DllExport void TestOutOfDoubles(double** doubles)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   
    (*doubles) = xyzs;
}

C# code

[DllImport("MeshToolsWrapper.dll")]
static extern void TestOutOfInts(out IntPtr ints);

[DllImport("MeshToolsWrapper.dll")]
static extern void TestOutOfDoubles(out IntPtr doubles);

TestOutOfInts(out IntPtr ints);
int[] test1 = new int[9];
Marshal.Copy(ints, test1, 0, 9);
//Marshal.FreeCoTaskMem(ints);

TestOutOfDoubles(out IntPtr doubles); 
double[] test2 = new double[9];
Marshal.Copy(doubles, test2, 0, 9);
//Marshal.FreeCoTaskMem(doubles);

C# result

# ints
1
2
3
4
5
6
7
8
9

# doubles
1.16501519399776E-311
6.95245618745976E-310
3.3
4.4
5.5
6.6
7.7
8.8
9.9

This:

DllExport void TestOutOfDoubles(double** doubles)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   
    (*doubles) = xyzs;
}

Is undefined behavior even without the C# interop. You are returning a pointer (array) to stack memory. Next function call and the contents of xyzs gets corrupted. Almost guaranteed.

The same holds true for your TestOutOfInts function. You're getting lucky that it seems to work.

This is the fix:

DllExport void TestOutOfDoubles(double** doubles)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   
    double* result = new double[9];
    std::copy(xyzs, xysz+9, result);
    
    (*doubles) = result;
}

But you will need to provide another function to release the allocated memory for the C# code to invoke.

A better approach is to force the caller to do the allocation. There's different techniques, but I suggest:

DllExport void TestOutOfDoubles(double* doubles, unsigned int size)
{   
    double xyzs[9] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };   

    if (size > 9)
    {
        size = 9;
    }

    std::copy(doubles, xyzs, xysz+size);

}

And another function to retrieve the size ahead of time.

DllExport unsigned int GetDoubleArrayLength()
{   
   return 9; // or whatever the actual size is    
}

Then it's up to the C# code to do the array allocation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM