10

Suppose I have a C# struct like this:

[StructLayout(LayoutKind.Explicit)]
struct IMAGE_DOS_HEADER {
    [FieldOffset(60)] public int e_lfanew;
}

Now suppose I read in data from a file, like this:

byte[] data = new byte[4096];
FileStream f = new FileInfo(filename).Open(FileMode.Open, FileAccess.Read);
int n = f.Read(data, 0, 4096);

Now I want to test n to make sure I've read enough bytes to get the value of e_lfanew. Is there any way I can get the value 60 (the FieldOffset) without retyping it? I'm looking for something like this:

if (n >= offsetof(IMAGE_DOS_HEADER.e_lfanew) + sizeof(int)) {
    ...
}

Is there any such command? In my actual code, I have to make several of these tests, and it seems tedious and error-prone to type the numbers in by hand, either by adding up the prior fields in the struct or by copying values out of the FieldOffset attributes. Is there a better way?

Paul A Jungwirth
  • 23,504
  • 14
  • 74
  • 93
  • Wow, I didn't think I'd get any real answer (except nobugz's), and here I have three choices! I hardly knew which to choose, so I up-voted them all. Simply defining constants is a reasonable approach, but it's sort of annoying how it obfuscates the struct's layout. I'm still learning the subtleties of managed/unmanaged, but I think wj32 is right that since the compiler is already letting me get a pointer to the struct, I know the managed/unmanaged offets are the same. So I'm going with his answer because it seems to produce the easiest-to-read code. Thank you all for such great replies! – Paul A Jungwirth Jan 23 '10 at 00:33

3 Answers3

20

Use Marshal.OffsetOf:

Marshal.OffsetOf(typeof(IMAGE_DOS_HEADER), "e_lfanew")
wj32
  • 8,053
  • 3
  • 28
  • 37
  • +1. Wow, this is exactly what he asked for (though I still feel more comfortable with nobugz's answer, assuming it is practical). – Brian Jan 22 '10 at 21:09
  • 3
    I don't know about this; `Marshal` is for going between managed and unmanaged. Thus from the documentation "`OffsetOf` provides the offset in terms of the unmanaged structure layout, **which does not necessarily correspond to the offset of the managed structure layout**." So in some cases this could give the wrong answer. – jason Jan 22 '10 at 21:24
  • 7
    And why would you want to know the managed structure offset? Even if you knew you wouldn't be able to use it in any way, because even obtaining a pointer to the structure means it's blittable. Your observation is completely irrelevant for any practical purposes. – wj32 Jan 22 '10 at 23:07
  • 1
    This works for the specific case in the question. It will not work in general. For example, if you have a struct with one or more bool-typed fields, sizeof(bool) ==1, but Marshal.SizeOf(type(bool)) = 4. Offsets will be similarly incorrect from Marshal.OffsetOf. In other words, the Marshal.OffsetOf function is giving the unmanaged offset of the *marshalled* struct, which may be different than the unmarshalled offsets. – Frank Smith Apr 22 '16 at 15:13
  • 2
    Can Marshal.OffsetIf be used for fields/props in structs of structs? How this needs to be named? lets say for: Struct1.Struct2.Prop1 Whats the Identifier for Prop1 in Strct1? – CleanCoder Oct 13 '20 at 17:15
8

Yes you can do this using reflection.

FieldOffsetAttribute fieldOffset = 
    (FieldOffsetAttribute)typeof(IMAGE_DOS_HEADER)
        .GetField("e_lfanew")
        .GetCustomAttributes(
            typeof(FieldOffsetAttribute),
            true
        )[0];
Console.WriteLine(fieldOffset.Value);

You can even turn this into a nice method:

static int FieldOffset<T>(string fieldName) {
    if (typeof(T).IsValueType == false) {
        throw new ArgumentOutOfRangeException("T");
    }
    FieldInfo field = typeof(T).GetField(fieldName);
    if (field == null) {
        throw new ArgumentOutOfRangeException("fieldName");
    }
    object[] attributes = field.GetCustomAttributes(
        typeof(FieldOffsetAttribute),
        true
    );
    if (attributes.Length == 0) {
        throw new ArgumentException();
    }
    FieldOffsetAttribute fieldOffset = (FieldOffsetAttribute)attributes[0];
    return fieldOffset.Value;
}

Usage:

Console.WriteLine(FieldOffset<IMAGE_DOS_HEADER>("e_lfanew"));
jason
  • 236,483
  • 35
  • 423
  • 525
  • +1 Agreed. Reflection is the safest (deterministic and maintainable) way to approach this issue. – Paul Sasik Jan 22 '10 at 20:53
  • In the context of avoiding code repetition, defining a constant (as suggested by nobugz) is almost always a much better choice than using reflection. There are cases where reflection is appropriate but avoiding code repetition can usually be done in other ways. That said, reflection might be simpler since there are a lot of these numbers. Note: I'm not the downvoter. – Brian Jan 22 '10 at 20:58
  • @Brian: It's not clear if he has access to the definition of the struct or not. If he does, I agree. If he does not, another approach is needed. – jason Jan 22 '10 at 21:00
  • @Jason: And now, having seen wj32's answer, your answer strikes me as way overcomplicated in that case. – Brian Jan 22 '10 at 21:07
  • @Brian: See my comment to wj32's answer. – jason Jan 22 '10 at 21:24
  • @Jason: And see my reply to your comment. – wj32 Jan 22 '10 at 23:55
7

Well, you already used a magic number in the structure declaration. Might as well do this:

private const int LfaNewOffset = 60;

[StructLayout(LayoutKind.Explicit)]
struct IMAGE_DOS_HEADER {
    [FieldOffset(LfaNewOffset)] public int e_lfanew;
}
...
if (n >= LfaNewOffset + sizeof(int)) {
    ...
}
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    What if he doesn't have access to the definition of the struct? I agree it's not clear if he does or does not. – jason Jan 22 '10 at 21:02
  • 1
    @Jason: If he doesn't, then this won't work. But I don't see much need to point that out as a flaw in this method, since it is self-evident. – Brian Jan 22 '10 at 21:04