tags:

views:

247

answers:

3

Dear ladies and sirs.

Observe the following sample code:

struct DDD
{
  [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 512, ArraySubType = UnmanagedType.I1)]
  byte[] x;
}

struct BBB
{
  DDD x;
}

struct CCC
{
  DDD x;
  ulong y;
  ulong z;
}

[StructLayout(LayoutKind.Explicit)]
struct AAA
{
  [FieldOffsetAttribute(0)]
  BBB a;
  [FieldOffsetAttribute(0)]
  CCC b;
}

Unfortunately, AAA cannot be loaded, trying to execute new AAA() fails with System.TypeLoadException: Could not load type 'AAA' from assembly 'Shunra.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=807fc02bc4ce69db' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.

How do one deal with it?

Thanks.

EDIT:

BTW, This is a stripped down version of MINIDUMP_CALLBACK_INPUT struct interop created by PInvokeTool (the original struct is defined in DbgHelp.h)

A: 

It looks to me that you cannot have two FieldOffsetAttribute with an offset of 0.

Make sure you have a look at its definition in MSDN library: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.fieldoffsetattribute.aspx

Tony
Wrong. One can have two FieldOffestAttributes with an offset of 0.
mark
A: 

I think you need to add a constructor within the Struct in order to instantiate types BBB and CCC, in turn, in each of the other structs BBB and CCC, you also need a constructor to instantiate type DDD.

struct DDD
{
  [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 512, ArraySubType = UnmanagedType.I1)]
  byte[] x;
}

struct BBB
{
  DDD x;
  public BBB(){
     x = new DDD();
  }
}

struct CCC
{
  DDD x;
  ulong y;
  ulong z;
  public CCC(){
     x = new DDD();
  }
}

[StructLayout(LayoutKind.Explicit)]
struct AAA
{
  [FieldOffsetAttribute(0)]
  BBB a;
  [FieldOffsetAttribute(0)]
  CCC b;
  public AAA(){
     a = new BBB();
     b = new CCC();
  }
}

The only gotcha in this is the instantiation of struct DDD which is unknown, as you have declared a field x which is a byte[] array and the size is unknown, so you have to handle this one based on your own requirements. Maybe pass in the parameter, perhaps an int indicating the size of the array...

Hope this helps you, Best regards, Tom.

tommieb75
No, this is just so wrong. Did you read the error message?
erikkallen
@erikkallen: Thanks for the heads up...on another note...The reason I have pointed out my posting was because I am in the middle of writing a managed PE Dumper which uses struct's and I came across the gotcha as one of the structs contained a byte array in which the length is unknown and had difficulty instantiating it hence I felt it was to be pointed out in case someone had difficulty..But thanks anyway. :)
tommieb75
+2  A: 

The problem is that no matter what you specify for MarshalAsAttribute, an array is an array is a managed object. To make your code work you will have to get rid of the managed array. To do this you have two options:

Option 1:

Convert the array to a fixed size buffer, which means change your definition of DDD to this:

unsafe struct DDD {
    [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst = 512, ArraySubType = UnmanagedType.I1)]
    fixed byte x[512];
}

(I'm not sure whether the MarshalAsAttribute is needed, but I doubt so.)

Now you are using an unsafe struct, so you have to compile with the /unsafe switch.

Option 2:

Convert the array to 512 bytes of members. The easiest way would be to use 64 longs:

struct DDD {
    long x1;
    long x2;
    long x3;
    ...
}

Edit: Clarified.

erikkallen
You would need to instantiate x in order for this to work.
tommieb75
Thanks, but I wish to avoid unsafe code. Otherwise, I might just write pointers and copy the C structs verbatim.
mark
That will not work definitely I am afraid.
tommieb75
There seems to be a conflict with BBB which in turn has member of type DDD and CCC in the struct AAA. It could be the compiler is padding out the structs hence not matching up for it to be able to have a field offset of 0?
tommieb75
BTW you have two ulongs in the struct CCC along with DDD struct as a member, and in struct AAA, there is two members as a union for structs BBB and CCC residing at the field offset of 0. I think that is a illegal structure layout which cannot be compiled by C# as it is limited for your case.
tommieb75
To confirm my previous comment, comment out the code for the two ulong members y and z, and it will compile happily.
tommieb75
Well, unfortunately, I cannot get rid of the two ulongs. Also, I did not understand exactly what is forbidden. Is it forbidden to aggregate struct in a struct in a union? Because, there are examples that do run cleanly. Anyway, if there are limitations they should appear in the language spec and I will glad to see one.
mark
OK, I updated to include the unsafe keyword. And if you don't want to use unsafe code, you're out of luck.
erikkallen
Good suggestion Mark, that worked on my end. As for the obvious safecode way, one could create 512 bytes of storage via 512 byte fields on the struct... I imagine that would be unwieldly to direcly work with and would require a helper class to see and use the bytes as a byte array...
Jason D