Lauren,
It shouldn't matter, there must be something wrong in the way you fill the
structure in your C code.
If you consider the buffer (array) filled with structures
"ListeCommandesXComm", like this:
|------------->|------------>|-------------->
struct1 struct2 struct3
where (in your case), each struct takes 29 bytes, this means that each
odd index (int) element would be mis-aligned on an odd address, something
that is taken care off by the CLR and the JIT on 32 bit versions of the
OS, but this would fail on some 64 bit systems!
I would suggest you inspect the C code and check how the array gets
filled.
My guess is that:
- Your packing is set to 1 for the compiland, or....
- that structures are getting copied (using memcpy) to addresses in a
buffer calculated using the length of the structure as displacement value.
This leads to non-portable code and a lot of maintenance issues,
especially when you have to transport the buffer to non C code
applications.
Take a look at the following sample (try to compile and run it if you
like), you'll see it works with the SizeOf value returned from the
marshaler using the default C++ alignment, but it will fail when the
alignment is set to anthing other than 4, 8, 16, 32.
//compile using MSFT C++: cl /EHsc /LD strmarsh.cpp
#include <windows.h>
// try this, align to byte (one byte multiples) and see the program fail
// __declspec(align(1)) struct str
// using default alignment
struct str
{
int index;
char num[10];
char dat[15];
};
extern "C" {
__declspec( dllexport ) void __stdcall PassStructArr(str *st[], int
*number);
}
str *st;
#define SIZEOFARRAY 5
void __stdcall PassStructArr(str *strarr[], int *number)
{
st = new str[SIZEOFARRAY];
for(int i = 0; i < SIZEOFARRAY; i++)
{
st
.index = i;
strcpy( st.num, "123456789" );
strcpy( st.dat, "12345678901234" );
}
*strarr = st;
*number = SIZEOFARRAY;
}
using System;
using System.Runtime.InteropServices;
namespace Willys
{
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public class ListeCommandesXComm
{
public int index;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string numeroCommande;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)]
public string dateCommande;
}
class Tester
{
[DllImport("strmarsh")]
extern static void PassStructArr(out IntPtr pt, out int number);
static void Main()
{
IntPtr lc = IntPtr.Zero;
int number;
PassStructArr(out lc, out number);
// Allocate the array.
ListeCommandesXComm[] outStructs = new ListeCommandesXComm[number];
// snippet taken from Nicholas reply on the other thread...
// Cycle through unmanaged memory, and marshal.
for (int index = 0; index < number; index++)
{
// Get the pointer of the location in memory.
IntPtr elementPointer = new IntPtr(lc.ToInt64() + (index *
Marshal.SizeOf(typeof(ListeCommandesXComm))));
// Now marshal that value.
outStructs[index] =
(ListeCommandesXComm)Marshal.PtrToStructure(elementPointer,
typeof(ListeCommandesXComm));
}
DumpArray(outStructs);
}
static void DumpArray(ListeCommandesXComm[] outStructs)
{
foreach(ListeCommandesXComm lc in outStructs)
Console.WriteLine("index {0}, numero {1}, date {2}", lc.index,
lc.numeroCommande, lc.dateCommande);
}
}
}
Willy.
Laurent said:
Hi Willy,
Thanks for your answer. When I say that the code does not work without
the size attribute, it's because I use this class as a parameter for a
function embeded in a C++ DLL, which return me a array of my class (using
double pointers).
So to read this array, I use the pointer returned by the function and,
with the exact size of the array which must be the same as the array
returned by the DLL, I can put element by element the values (I'm not
sure to be undersandable enough, but thanks to Nicholas Paldino for this
tip).
That's why I need to have 29, not 32 !
Willy Denoyette said:
What exactly do you mean with:
"To make it work, I had to modify the declaration of my class like this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Size=29)]
Your code works without the size attribute too!
The 32 comes from the fact that the interop marshaler reserves a buffer
for the structure that is the size of the structure padded to a multiple
of the size of the first element in the structure.
So in your case 4 +10+15 = 29 padded to a multiple of 4 which gives you
32.
This buffer size is only relevant for the marshaler, you shouldn't care
about it, what counts is that the structure is correctly marshaled to
the native side, that is, correctly aligned and padded.
Note that it's your responsability to balance the alignment of the
structures you pass to unmanaged code.
Willy.
Hello,
This is probably a dumb question, but I just would like to
understand how the C# compiler computes the size of the managed
structure or classes. I'm working on this class:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)]
public class MyClass
{
public int index;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] public string
numeroCommande;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)] public string
dateCommande;
}
For me, the size of this class is 29 octets (4 for the integer and
10+15 for the strings). Now, when I debug my source code, the compiler
gives me this information: Marshal.SizeOf(typeof(MyClass)) = 32... Why
? To make it work, I had to modify the declaration of my class like
this:
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi, Size=29)]
public class ListeCommandesXComm
{
public int index;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)] public string
numeroCommande;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 15)] public string
dateCommande;
}
It works, but I'd prefer the compiler to compute the real size
instead of hard coding it... Does anyone knows why this happens and
what I should do to make it works correctly ?
Thanks !