3

I'm debugging code in C# written by a 3rd party. The project is an old C++ project that was rewritten in C# by a contractor, and I have no access to the contractor. I authored the original C++ version.

The issue is when the C# code gets the size of a structure that represents data received over a UDP connection.

The struct is defined as:

[StructLayout(LayoutKind.Sequential,Pack=1)]
internal class PROXY_HDR {
    public ushort pad;
    public ushort label;
    public char flags;
    public ushort length;
    public char[] ip = new char[4];
    public ushort port;
}

The size of this struct is retrieved as:

int size = Marshal.Sizeof(typeof(PROXY_HDR));

and the value returned is 17 instead of the expected 13. With a 4-byte difference, I suspect the ip member, but only because it's expressed differently than the other members (with 'new'), but I have no other basis to decide.

I don't typically use this type of marshaling in my C# code that parses received packets without a problem, so I don't know how to modify this struct definition to make it 'line up' with the original version size-wise.

I could replace the Marshal line with

int size = 13;

but that's cheating, right?

Can I modify this layout somehow to get the size to come out right?

buzzard51
  • 1,372
  • 2
  • 23
  • 40
  • I don't have an actual answer, but you might try playing around with setting the field offsets explicitly as I do in [this answer](https://stackoverflow.com/a/58413042/448932) to an unrelated question. – 500 - Internal Server Error Aug 18 '20 at 20:07
  • Why would you expect it to be 13? – Ian Kemp Aug 18 '20 at 20:10
  • @IanKemp because it does return 13 for x86 (32bit) and +4 if compiled x64... – Alexei Levenkov Aug 18 '20 at 20:15
  • 2
    @AlexeiLevenkov -- if you marshal it properly, it returns 13 bytes under 64 bit *and* 32 bit – Andy Aug 18 '20 at 20:16
  • @Andy indeed - also there would be no question to ask :) Clearly no one who does no do this stuff daily expected `char` to have sizeof = 1... And OP did not know about reference types... so plenty of things to explain. – Alexei Levenkov Aug 18 '20 at 20:26

1 Answers1

5

Add this to the structure:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal class PROXY_HDR
{
    public ushort pad;
    public ushort label;
    public byte flags;
    public ushort length;
    [MarshalAs(UnmanagedType.ByValArray,
        SizeConst = 4, ArraySubType = UnmanagedType.U1)]
    public byte[] ip;
    public ushort port;
}

This will tell the compiler to treat it as a typical C-style array, not a pointer. This should be byte, as an IP address is an unsigned char array. char typically isn't used in these types of headers, it's usually byte (i.e: unsigned char)

Andy
  • 12,859
  • 5
  • 41
  • 56
  • 1
    @Bathsheba -- I *think* it's right.. My explanation shows that I am not 100% sure *why* it's right. I will have to research this and come up with a better explaination. – Andy Aug 18 '20 at 20:21
  • 1
    @Andy: Yes, this worked. As far as the 'char' definition, I think the C# developers were trying to be as faithful to the original C++ code as possible, and this particular struct was never updated to using 'byte', as most others were. Thank you!! – buzzard51 Aug 18 '20 at 20:28
  • 1
    Note that the original issue is only due to treating `ip` as a pointer (32 or 64 bit, depending on how you compile code). Apparently `char` is single byte for default marshalling - I don't have link to confirm, but you can easily try yourself (and see it directly with size =13 or 17 which can only be explained by size of char = 1). – Alexei Levenkov Aug 18 '20 at 20:31
  • @user3235770 -- Just a note that you can make this more specific by adding `ArraySubType = UnmanagedType.U1` or whatever to the attribute. I don't know if that would help. – Andy Aug 18 '20 at 20:34
  • Actually I just noticed I only added the MarshalAs line, not the change from CHAR to BYTE in the line after. So apparently it's the interpretation as a pointer that did it. – buzzard51 Aug 18 '20 at 20:37