2010-01-22 3 views
7

Пусть у меня есть C#-структуру, как это:Команда C# для получения смещения структуры?

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

Теперь предположим, что я прочитал в данных из файла, например:

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

Теперь я хочу проверить n, чтобы убедиться, что я имею достаточно прочитать байты, чтобы получить значение e_lfanew. Есть ли способ получить значение 60 (FieldOffset) без его повторного ввода? Я ищу что-то вроде этого:

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

Есть ли такая команда? В моем фактическом коде я должен выполнить несколько из этих тестов, и кажется утомительным и подверженным ошибкам вводить числа вручную, либо путем добавления предыдущих полей в структуре, либо путем копирования значений из атрибутов FieldOffset. Есть ли способ лучше?

+0

Ничего себе, я не думаю, что я бы получить какой-либо реальный ответ (кроме nobugz-х), а вот у меня есть три варианта! Я не знал, что выбрать, поэтому я все-таки проголосовал за всех. Просто определение констант - разумный подход, но это раздражает то, как он запутывает структуру структуры. Я все еще изучаю тонкости управляемых/неуправляемых, но я думаю, что wj32 прав, поскольку, поскольку компилятор уже позволяет мне получить указатель на структуру, я знаю, что управляемые/неуправляемые оффтоны одинаковы. Поэтому я собираюсь с его ответом, потому что он, кажется, производит самый легкий для чтения код. Спасибо всем за такие замечательные ответы! –

ответ

17

Используйте Marshal.OffsetOf:

Marshal.OffsetOf(typeof(IMAGE_DOS_HEADER), "e_lfanew") 
+0

+1. Вау, это именно то, что он просил (хотя я все еще чувствую себя более комфортно с ответом благородца, предполагая, что это практично). – Brian

+1

Я не знаю об этом; «Маршал» находится между управляемыми и неуправляемыми.Таким образом, из документации «OffsetOf» обеспечивает смещение в терминах неуправляемого макета структуры **, что не обязательно соответствует смещению шаблона управляемой структуры ** ». Поэтому в некоторых случаях это может дать неправильный ответ. – jason

+5

И почему вы хотите знать смещение управляемой структуры? Даже если бы вы знали, что не сможете использовать его каким-либо образом, потому что даже получение указателя на структуру означает, что он неспособен. Ваше наблюдение совершенно не имеет значения для каких-либо практических целей. – wj32

6

Да, вы можете сделать это, используя отражение.

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

Вы даже можете превратить это хороший метод:

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; 
} 

Использование:

Console.WriteLine(FieldOffset<IMAGE_DOS_HEADER>("e_lfanew")); 
+0

+1 Согласовано. Отражение - самый безопасный (детерминированный и поддерживаемый) способ подхода к этой проблеме. –

+0

@Downvoter: Пожалуйста, объясните. Благодаря! – jason

+0

В контексте избежания повторения кода определение константы (как было предложено nobugz) почти всегда намного лучше, чем использование рефлексии. Бывают случаи, когда отражение подходит, но избежать повторения кода обычно можно сделать другими способами. Тем не менее, отражение может быть проще, поскольку их много. Примечание. Я не сторонник. – Brian

6

Ну, вы уже использовали магическое число в объявлении структуры. Мог бы также сделать это:

private const int LfaNewOffset = 60; 

[StructLayout(LayoutKind.Explicit)] 
struct IMAGE_DOS_HEADER { 
    [FieldOffset(LfaNewOffset)] public int e_lfanew; 
} 
... 
if (n >= LfaNewOffset + sizeof(int)) { 
    ... 
} 
+0

Что делать, если у него нет доступа к определению структуры? Я согласен, что не ясно, поступает или нет. – jason

+0

@ Джейсон: Если он этого не сделает, значит, это не сработает. Но я не вижу большого смысла указывать на это как на недостаток этого метода, поскольку он самоочевиден. – Brian

Смежные вопросы