4

I have a struct like this in C#:

[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct MyStruct
{
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]
    public string StringValue;
    public uint IUintValue;
}

And a corresponding struct in native code

struct MyStruct
{
    char StringValue[17];
    ulong UintValue;
}

My goal is to pass an array of this structs from c# side to c++(native side) using pinvoke. Here is how I use it in c#

[DllImport(@"MyLibrary.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int SendArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]ref MyStruct[] arr, int recordsCount);

and a calling code is:

var array = new MyStruct[10];
//initialize
SendArray(ref array, array.Length);

On native side I have the following function signature:

extern "C" __declspec(dllexport) int SendArray(MyStruct** Arr, int recordsCount);

And it appears o work only for the first element in array. On c++ side I get this array, but only first element is correctly marshaled. The rest of them appears to be trash.

Where is my mistake?

steavy
  • 1,483
  • 6
  • 19
  • 42
  • That definitely should be MyStruct* Arr, matched by MyStruct[] on the C# side *without* ref. Add some test code and ensure sizeof(MyStruct) on the C side matches Marshal.SizeOf(MyStruct) on the C# side. If it doesn't then the 2nd and subsequent elements will be trash. – Hans Passant Nov 27 '13 at 13:21
  • Is a C# string the same as a char [17]? – doctorlove Nov 27 '13 at 13:43
  • If that sizes don`t match, than why the first element is marshaled correctly? – steavy Nov 27 '13 at 13:46

1 Answers1

4

Your C++ code does not receive an array of structs. It receives an array of pointers to struct. Change the C++ code to be like so:

int SendArray(MyStruct* Arr, int recordsCount);

or perhaps

int SendArray(MyStruct Arr[], int recordsCount);

And then your p/invoke should be

[DllImport(...)]
public static extern int SendArray([In] MyStruct[] arr, int recordsCount);

I am also suspicious of Pack=8. Are you quite sure?

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I have tried both your solutions and unfortunately none of them works for me =/ – steavy Nov 27 '13 at 13:05
  • btw, should I explicitly set [In] attribute? it is a default one, isn`t it? – steavy Nov 27 '13 at 13:12
  • 1
    I think you could omit it. I like to be explicit here: http://stackoverflow.com/questions/14366066/are-p-invoke-in-out-attributes-optional-for-marshaling-arrays – David Heffernan Nov 27 '13 at 13:15
  • 1
    Yes, your answer is correct. Appeared that there was a mistake on c++ side.(After receiving a valid array they managed to corrupt it somehow) I say "they" because I was responsible only for .net part of the job. Killed almost 2 days because of this =S idiots :D – steavy Nov 27 '13 at 13:31
  • As far as I understood about that link to another question, it applies only to in/out combo of attributes. To enforce copy when getting a struct back from unmanaged to managed. Right? – steavy Nov 27 '13 at 13:43
  • No, I think Hans is talking more generally than that. – David Heffernan Nov 27 '13 at 13:58