1

I am trying to pass some arrays from C++ to C# and they are exposed as C arrays. These arrays are received using a callback in C#. This is how they are defined in the C++ side:

struct Image
{
    unsigned char* image_ptr;
    int rows;
    int cols;
};
typedef void(*CCallbackFn)(bool[], const char* [], Image[], Image, int length);

And this is how I exposed them in C#:

[StructLayout(LayoutKind.Sequential)]
struct ImageStruct
{
   public IntPtr image_ptr;
   public int rows;
   public int cols;
}
delegate void CallbackDelegate( bool[] status, string[] id, ImageStruct[] img_face, ImageStruct img_org, int length);

This compiled and seemed to work fine, until I noticed it only returns the first element of each array! and since the length is bigger than the array sizes, the program will crash with the index out of range error.

I then tried to change them to:

delegate void CallbackDelegate([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1)] bool[] status,
                               [param: MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] id,
                               [param: MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPArray)] ImageStruct[] img_face,
                               ImageStruct img_org, int length);

as suggested in a similar question here, but this didn't have any effect either. Still only the first element is returned. What am I missing here?

Hossein
  • 24,202
  • 35
  • 119
  • 224
  • Have you also seen [this question](https://stackoverflow.com/questions/4389950/how-to-map-a-c-array-to-c)? I couldn't answer either but they seem related more closely than the one you linked. – Joelius May 26 '20 at 08:28
  • 2
    If the arrays are allocated on the C++ side and are provided in a callback, then also specify the [`SizeParamIndex`](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.marshalasattribute.sizeparamindex?view=netcore-3.1) in the `MarshalAs` for the arrays. – GSerg May 26 '20 at 08:34
  • @GSerg : Thanks, but when I do so I get ```System.Runtime.InteropServices.MarshalDirectiveException: 'Array size control parameter type not supported.' ``` error! – Hossein May 26 '20 at 09:08
  • @ Joelius Thanks, I'll have a look at it – Hossein May 26 '20 at 09:14
  • I'm pretty sure you need to remove the `ArraySubType = UnmanagedType.LPArray`. – GSerg May 26 '20 at 09:15
  • @GSerg : I removed them as well, but no luck. ```delegate void CallbackDelegate([MarshalAs(UnmanagedType.LPArray, SizeParamIndex =1)] bool[] status, [param: MarshalAs(UnmanagedType.LPArray)] string[] id, [param: MarshalAs(UnmanagedType.LPArray)] ImageStruct[] img_face, ImageStruct img_org, int length);``` – Hossein May 26 '20 at 09:22
  • 2
    The `SizeParamIndex` must be in all three `MarshalAs`, and it should be `= 4`. You should have not removed the `ArraySubType = UnmanagedType.I1` and the `ArraySubType = UnmanagedType.LPStr`. – GSerg May 26 '20 at 09:27
  • Thanks a lot. That solved it. would you post that as answer? Also I assumed since bools are 1 byte, I should be using `SizeParamIndex` of 1 and not 4! why are we using 4 ? – Hossein May 26 '20 at 09:34
  • Did you click the link? Because the size of the array is provided in your fifth function argument, `int length`, which has the zero-based index of 4. – GSerg May 26 '20 at 09:37
  • Aha! so that was it! I actually did click the linked and read the whole article as well! but I completely thought something else! I didnt even think about the "parameters index" of my method!! oh my God! sorry for the hassle! and thanks for your generous help really appreciate it – Hossein May 26 '20 at 09:41

1 Answers1

4

The marshaler needs to know how many elements the unmanaged array has. The array itself does not contain this information.

The callback tells you how many elements there are in the 5th argument, int length, which has the zero-based index of 4. So tell the marshaler to use this information:

delegate void CallbackDelegate(
  [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 4)] bool[] status,
  [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 4)] string[] id,
  [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] ImageStruct[] img_face,
  ImageStruct img_org,
  int length
);
GSerg
  • 76,472
  • 17
  • 159
  • 346