Question

I have a function in C# that is passing an array of structures into a DLL written in C++. The struct is a group of ints and when I read out the data in the DLL all the values come out fine. However if I try to write to the elements from C++ the values never show up when I try to read then back in C#.

C#

[StructLayout(LayoutKind.Sequential)]
struct Box
{
  public int x;
  public int y;
  public int width;
  public int height;
}

[StructLayout(LayoutKind.Sequential)]
struct Spot
{
  public int x;
  public int y;
}

static void TestCvStructs()
{
  int len = 100;
  Box[] r = new Box[len];
  Spot[] p = new Spot[len];

  for (int i = 0; i < len; i++)
  {
    r[i].x = i*10;
    r[i].y = i * 200;
    r[i].width = r[i].x * 10;
    r[i].height = r[i].y * 100 + r[i].x * 5;

    p[i].x = i * 8;
    p[i].y = i * 12;
  }

  PassCvStructs(len, r, p);

  for (int i = 0; i < len; i++)
  {
    Console.WriteLine("Point/x:{0} Boxes/x{1}", p[i].x, r[i].x );
  }
}

[DllImport(dll)]
private static extern int PassSomeStructs(int count, Box[] boxes, Spot[] points);

C++

typedef struct Box
{
  int x;
  int y;
  int width;
  int height;
} Box;

typedef struct Spot
{
  int x;
  int y;
} Spot;

CPPDLL_API int PassSomeStructs(int arrayLength, Box *boxes, Spot *points)
{
  for(int i = 0; i < arrayLength; ++i)
  {
    printf("Group:%i\n", i);
    printf("Rect - x:%i y:%i width:%i length:%i\n", boxes[i].x, boxes[i].y, boxes[i].width, boxes[i].height);
    printf("Point - x:%i y:%i\n", points[i].x, points[i].y);
    printf("\n");

    points[i].x = 3;
    boxes[i].x = 1;
  }
  return 0;
}
Was it helpful?

Solution 3

I found the answer posted to another question: How to marshall array of structs in C#?

When marshaling apparently the default is to marshal the parameters as In. Otherwise they need to be explicitly declared as Out or In, Out. After specifying that explicitly my code the example now works.

Thanks to his Skeetness for the answer and to antonmarkov for the exposure to the MarshalAs.

private static extern int PassSomeStructs(int count, [In, Out] Box[] boxes, [In, Out] Spot[] points);

OTHER TIPS

From an MDSN article on marshalling arrays: try setting the following attribute on your array types. This is normally used for calling into C# from C++, but it may also be required for getting updated values back into C#.

[DllImport(dll)]
private static extern int PassSomeStructs(int count, 
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Box[] boxes, 
    [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] Spot[] points);

Also see this article for an example of successful two-way marshalling:

http://social.msdn.microsoft.com/forums/en-US/csharplanguage/thread/ff0123d0-506b-4de2-bfb5-f690c9358826/

Have you tried adding the ref/out keyword to your C# extern?

Another idea is try passing in an IntPtr instead of the class itself, or passing it in -as- an IntPtr...

I believe that structs are copied, not passed by reference by default.

Honestly stabbing in the dark, but you don't have any answers yet, so hopefully this helps...

Interop is still in the "it's magic" stage for me...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top