Release Build UWP C# Struct One Byte Larger than Debug

39 Views Asked by At

I have a class hierarchy consisting of an abstract base class with a few helper methods and no fields, and a handful of derived classes with a variety of fields. Here is MCRE code describing the classes in question:

namespace MarshalSizes {
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public abstract class BaseClass {
        public abstract int MySize { get; }
        public int BaseSize { get {
                return Marshal.SizeOf(GetType());
            } 
        }
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public class DerivedClass : BaseClass {
        public UInt32 a32bits;
        public UInt16 a16bits;
        public Byte onebyte;
        public Byte twobyte;
        public override int MySize {
            get {
                return Marshal.SizeOf(GetType());
            }
        }
    }
}

This is in a UWP app. I have simple code that generates a popup box thusly:

    DerivedClass classThing = new DerivedClass();
    Windows.UI.Popups.MessageDialog dialog = new Windows.UI.Popups.MessageDialog(
        String.Format("DerivedClass.BaseSize = {0}\nDerivedClass.MySize = {1}",
           classThing.BaseSize, classThing.MySize));

    await dialog.ShowAsync();

Simple enough. When I run a debug build: Dialog from debug build When I run a release build: Dialog from release build

This is wreaking havoc in my real project, where everything is carefully designed to be sized in multiples of 32 bytes (has to do with a communication protocol with a device).

What's going on here? Why does the size grow by a byte in a release build?

EDIT: Since it's been asked, here's a somewhat simplified example of how this type gets marshaled, in a method in the ABC:

// Given Byte[] outbuffer of appropriate size
int size = Marshal.SizeOf(GetType());
IntPtr iptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(this, iptr, false);
Marshal.Copy(iptr, outbuffer, 0, size);
Marshal.FreeHGlobal(iptr);

The outbuffer is part of a packet envelope class that includes CRC values generated over the byte array.

The above is for transmission; for reception, code like this is used:

// Given Byte[] buffer of enough inbound bytes
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
var packetType = Marshal.PtrToStructure<MyPacketClass>(handle.AddrOfPinnedObject());
handle.Free();

I'm open to suggestions of "better" ways of doing this, but I would still very much like to have the original question here answered.

0

There are 0 best solutions below