Marshalling C struct containing arrays to C#

2020-08-18 15:40发布

问题:

With great help of the stackoverflow community, I've managed to call a native DLL function. However, I can't modify the values of ID or intersects array. No matter what I do with it on the DLL side, the old value remains. It seems read-only.

Here are some code fragments:

C++ struct:

typedef struct _Face {
    int ID;
    int intersects[625];
} Face;

C# mapping:

[StructLayout(LayoutKind.Sequential)]
    public struct Face {
        public int ID;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 625)]
        public int[] intersects;
    }

C++ method (type set to DLL in VS2010):

extern "C" int __declspec(dllexport) __stdcall 
solve(Face *faces, int n){
for(int i =0; i<n; i++){
    for(int r=0; r<625; r++){
        faces[i].intersects[r] = 333;
        faces[i].ID = 666;
        }
    }

C# method signature:

[DllImport("lib.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern int solve(Face[] faces, int len);

C# method invocation:

Face[] faces = new Face[10];
faces[0].intersects = new int[625];
faces[0].ID = -1; //.. and add 9 more ..

solve(faces, faces.Length);

// faces[0].ID still equals -1 and not 666

Kindest regards, e.

回答1:

You have to tell the pinvoke marshaller explicitly that the array needs to be marshaled back. You do this with the [In] and [Out] attributes. Like this:

    [DllImport("...")]
    public static extern int solve([In, Out] Face[] faces, int len);


回答2:

This is an output field only? To get to the bottom of this, I'd try substituting your Face[] parameter with a large-enough a byte[] and see if the byte array gets filled with anything (you'll have to change your [DllExport] a bit).

Also, one other thing I used to experience when doing this with char*'s is that I had to pre-allocate the buffer in C#. For example:

StringBuilder theString=new StringBuilder();
MyUnmanagedFunction(theString);

would not work. But assuming that returned theString was a max 256 characters, I would do this:

StringBuilder theString=new StringBuilder(256);
MyUnmanagedFunction(theString);

And I'd be in business. I'd recommend trying the byte[] substitution, if that doesn't work, try the pre-allocated byte array. Once you are seeing the byte array actually get changed by your C++ code, then you can figure out how to marshal that thing into your C# struct.



标签: c# pinvoke