locked
PtrToStructure failed to copy bytes if struct contains Boolean inside RRS feed

  • Question

  • I have following code

          static byte[] testBytes2 = { 49, 56, 58, 0, 123, 0, 0, 0, 1, 1, 1 };
    
          [StructLayout(LayoutKind.Explicit, Pack = 1)]
          internal unsafe struct fields_test
          {
             [FieldOffset(0)]
             public fixed byte a0[4];
             [FieldOffset(4)]
             public System.UInt32 a1;
             //[FieldOffset(8)]   // if you uncomment these lines variable a0 will not be filled!! only one byte will be copied!
             //public System.Boolean a2;
          }; 
    
          GCHandle handle = GCHandle.Alloc(testBytes2, GCHandleType.Pinned);
          fields_test test = (fields_test)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(fields_test));
          handle.Free();

    When you run it, bytes will be copied to structure correctly. BUT. If you uncomment last 2 lines, essentially adding one Boolean in the end of struct, first byte[] will not be filled with values anymore!!! only one byte will be copied!!

    This seems like a bug inside PtrToStructure function !!!

    here is result of first run:

    test.a0 == [49,56,58,0 ]
    test.a1 == 123

    here is result of second run:

    test.a0 == [49,0,0,0 ]
    test.a1 == 123
    test.a2 == True

    Friday, August 29, 2014 5:20 PM

Answers

  • Well, I don't know why you need to use fixed but the usual alternative is to use MarshalAs(UnmanagedType.ByValArray, SizeConst = ...)]:

    [FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] a0;
    

    Of course, this require the allocation of a byte array during marsahaling, that may be a bit overkill for only 4 bytes.

    You could try to report this as a bug on Connect: https://connect.microsoft.com/VisualStudio

    I don't know if they'll consider this a bug or "by design". It's possible that fixed arrays where intended to be used with fixed objects, exactly like you're doing in your workaround and not with marshaling which has its own of dealing with arrays embedded in objects.

    • Marked as answer by Fred Bao Monday, September 8, 2014 8:12 AM
    Saturday, August 30, 2014 5:37 AM

All replies

  • It's not the bool, it's the fixed array. Fixed arrays are implemented by using a "hidden" struct which contains only one byte field but has StructLayout.Size = 4. The marshaler treats the structs as being 4 bytes in side but copies only one byte because that's the only field it sees in that struct. It's strange that removing the bool changes behavior but anyway, the problem is fixed.

    You can add a int field at offset 0 to force the marshaler to copy all 4 bytes:

    [StructLayout(LayoutKind.Explicit, Pack = 1)] internal unsafe struct fields_test { [FieldOffset(0)] public fixed byte a0[4]; [FieldOffset(0)] int a04; // force marshaler to copy 4 bytes instead of just 1 [FieldOffset(4)] public System.UInt32 a1;

    ...


    Friday, August 29, 2014 5:52 PM
  • the problem is what my example is very simplified; in real life it is 
    byte [80], struct can contain hundreds of bytes etc

    I wanted to have real solution which can copy byte array to struct no matter what

    so far I'm thinking about

                unsafe
                {
                   fixed (void* vp = &fields_)
                   {
                      byte* p = (byte*)vp;
                      for (int i = 0; i < msgBytes.Length; i++)
                         *p++ = msgBytes[i];
                   }
                }

    it is so sad what library function PrtToStructure is not working.

    Friday, August 29, 2014 8:10 PM
  • Well, I don't know why you need to use fixed but the usual alternative is to use MarshalAs(UnmanagedType.ByValArray, SizeConst = ...)]:

    [FieldOffset(0), MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
    public byte[] a0;
    

    Of course, this require the allocation of a byte array during marsahaling, that may be a bit overkill for only 4 bytes.

    You could try to report this as a bug on Connect: https://connect.microsoft.com/VisualStudio

    I don't know if they'll consider this a bug or "by design". It's possible that fixed arrays where intended to be used with fixed objects, exactly like you're doing in your workaround and not with marshaling which has its own of dealing with arrays embedded in objects.

    • Marked as answer by Fred Bao Monday, September 8, 2014 8:12 AM
    Saturday, August 30, 2014 5:37 AM