tags:

views:

144

answers:

6

I have a struct that looks something like this:

[StructLayout(LayoutKind.Sequential)]
public struct in_addr {
 public Anonymous1 S_un;

 [StructLayoutAttribute(LayoutKind.Explicit)]
 public struct Anonymous1 {
  [FieldOffsetAttribute(0)]
  public Anonymous2 S_un_b;

  [FieldOffsetAttribute(0)]
  public Anonymous3 S_un_w;

  [FieldOffsetAttribute(0)]
  public uint S_addr;
 }

 [StructLayoutAttribute(LayoutKind.Sequential)]
 public struct Anonymous2 {
  public byte s_b1;
  public byte s_b2;
  public byte s_b3;
  public byte s_b4;
 }

 [StructLayoutAttribute(LayoutKind.Sequential)]
 public struct Anonymous3 {
  public ushort s_w1;
  public ushort s_w2;
 }

 public in_addr(byte[] address) {
  S_un.S_addr = (uint)BitConverter.ToInt32(address, 0);

  S_un.S_un_b.s_b1 = address[0];
  S_un.S_un_b.s_b2 = address[1];
  S_un.S_un_b.s_b3 = address[2];
  S_un.S_un_b.s_b4 = address[3];

  S_un.S_un_w.s_w1 = 0;
  S_un.S_un_w.s_w2 = 0;
 }
}

When I try to create a new instance of this struct, every byte field is set to 0.

in_addr temp = new in_addr(bytes);

I've stepped through the struct's constructor and verified that the bytes are indeed getting assigned to the fields. But when I step out of the constructor and check the value of temp, everything is 0.

What's causing this?

+3  A: 

I can't reproduce this; your code works for me. I’m using Mono but I very much doubt that this is a bug in the Microsoft C# compiler, it's more likely that you've got an error elsewhere.

Konrad Rudolph
Same test and same result on a Mac with mono. +1
Sklivvz
A: 

If you are using C# 3.0, just try the following:

MyStruct ms = new MyStruct
{
    MyByte = 1,
    MyNestedStruct.NestedStryctByte = 2
}

That should work, and would alleviate the need to have a constructor in your struct. If you can't directly initialize the NestedStructByte, create it first:

MyStruct ms = new MyStruct
{
    MyByte = 1,
    MyNestedStruct = new MyStruct.NestedStruct
    {
        NestedStryctByte = 2
    }
}
jrista
That's how I had it originally, but the same problem occurred. So, I changed it to using a constructor because I thought maybe object initializers weren't allowed on structs.
David Brown
I've tried to replicate your problem both ways, and it seems to work fine. Try creating a simple console application, and Console.WriteLine the values. Don't use the debugger, and see if the output is correct. Something is interfering with your results, so try to eliminate as many factors as possible. From what I know, it should work either way.
jrista
+1  A: 

I just tested this in VisualStudio 2008 using C# and got the expected output (1, 2).

Try posting the actual example code that you're having an issue with.

EDIT This is why example code can be bad ;)

Your issue was with the StructLayout.Explicit on Anonymous1. Is there a particular reason that

  1. You did explicit instead of sequential and putting them in the order you wanted
  2. You left the indices as 0 for all of them

When I changed it to Sequential and removed the attributes, it worked fine for me.

EDIT 2 (Deleted)

EDIT 3

Your issue is with the assignment in the constructor. Not sure how I missed this. You don't need to set all of those variables, because your FieldOffset attribute makes them all stored in the same location. The struct itself is only occupying 4 bytes of memory (in theory, anyway). Whether you're accessing it via the int, the bytes, or the two shorts, they all go to the same place. As a result, your first two sets of assignments (to the int and to the bytes) are redundant, and the last set (to the shorts setting them to 0) clears out what you just did.

Unfortunately the C# compiler doesn't know this, so I'm sure you added the last assignment because it complained about the struct not being fully assigned. Add the dummy assignments first for the shorts and the int, then assign the bytes explicitly from the array.

Adam Robinson
The new code is above.
David Brown
According to the Pinvoke Interop Assistant, that's how it's supposed to look. Will the struct be incompatible with native versions of in_addr if I make those changes?
David Brown
See the link in the edit.
Adam Robinson
That was my original question. The code is essentially pasted directly from the answer, except the Anonymous structs are nested and I have a constructor.
David Brown
@Adam: No, example code is fine. But one should **always*** test that the example code still has the problem one is trying to correct. And if it doesn't, you probably have a good hint about what you did wrong :)
Brian
@Brian: Well said ;)
Adam Robinson
+1  A: 

I'm using .NET 3.5 and I cannot reproduce the problem.

Brian Gideon
A: 

I tested your code - everything is OK.

public struct MyStruct
    {
        public byte MyByte;
        public NestedStruct MyNestedStruct;

        public struct NestedStruct
        {
            public byte NestedStructByte;
        }

        public MyStruct(byte[] bytes)
        {
            MyByte = bytes[0];
            MyNestedStruct.NestedStructByte = bytes[1];
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            MyStruct ms = new MyStruct(new byte[] { 1, 2 });
            //ms.MyByte; // 0, but should be 1
            //ms.MyNestedStruct.NestedStructByte; // 0, but should be 2

        }
    }
den123
+4  A: 

Because of these lines:

S_un.S_un_w.s_w1 = 0;
S_un.S_un_w.s_w2 = 0;

They are mapped to your 4 bytes via

[FieldOffsetAttribute(0)]
public Anonymous2 S_un_b;

[FieldOffsetAttribute(0)]
public Anonymous3 S_un_w;
Mike Polen
How do those lines affects S_addr and the values of S_un_b, though?
David Brown
That is mimic'ing a union, so since S_un_b, S_un_w, and S_addr are share the same space, clearing any one of them clears all of the others!
sixlettervariables
Oh, I see. (Not a P/Invoke expert) Thanks!
David Brown
+1 Nice catch. :)
jrista