2010-01-22 18 views
7

Supponiamo che io sono un C# struct come questo:Comando C# per ottenere lo struct offset?

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

Ora supponiamo che ho letto nei dati da un file, in questo modo:

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

Ora voglio provare n per assicurarsi che ho leggere abbastanza byte per ottenere il valore di e_lfanew. C'è un modo per ottenere il valore 60 (FieldOffset) senza doverlo ridigitare? Sto cercando qualcosa di simile:

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

Esiste un comando del genere? Nel mio codice attuale, devo eseguire molti di questi test, e sembra noioso e soggetto a errori digitare i numeri manualmente, aggiungendo i campi precedenti nella struct o copiando i valori dagli attributi FieldOffset. C'è un modo migliore?

+0

Wow, non pensavo che avrei avuto una risposta reale (ad eccezione di nobugz), e qui ho tre scelte! Sapevo a malapena quale scegliere, quindi li ho votati tutti. Definire semplicemente le costanti è un approccio ragionevole, ma è un po 'fastidioso il modo in cui offusca il layout della struttura. Sto ancora imparando le sottigliezze del managed/unmanaged, ma penso che wj32 abbia ragione, dal momento che il compilatore mi sta già permettendo di ottenere un puntatore alla struct, so che gli offset gestiti/non gestiti sono gli stessi. Quindi vado con la sua risposta perché sembra produrre il codice più facile da leggere. Grazie a tutti per le risposte così fantastiche! –

risposta

17

Uso Marshal.OffsetOf:

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

+1. Wow, questo è esattamente ciò che ha chiesto (anche se mi sento ancora più a mio agio con la risposta di nobugz, assumendo che sia pratico). – Brian

+1

Non so di questo; 'Marshal' è per andare tra gestito e non gestito.Pertanto, dalla documentazione "OffsetOf' fornisce l'offset in termini di layout della struttura non gestita, ** che non corrisponde necessariamente all'offset del layout della struttura gestita **." Quindi in alcuni casi questo potrebbe dare la risposta sbagliata. – jason

+5

E perché vorresti conoscere l'offset della struttura gestita? Anche se sapessi che non saresti in grado di usarlo in alcun modo, perché anche ottenere un puntatore alla struttura significa che è blittabile. La tua osservazione è completamente irrilevante per qualsiasi scopo pratico. – wj32

6

Sì, è possibile farlo utilizzando la riflessione.

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

Si può anche trasformare questo in un metodo piacevole:

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")); 
+0

+1 concordato. La riflessione è il modo più sicuro (deterministico e gestibile) per affrontare questo problema. –

+0

@Downvoter: per favore spiega. Grazie! – jason

+0

Nel contesto di evitare la ripetizione del codice, definire una costante (come suggerito da nobugz) è quasi sempre una scelta molto migliore rispetto all'utilizzo del reflection. Ci sono casi in cui la riflessione è appropriata ma evitare la ripetizione del codice può essere fatto in altri modi. Detto questo, la riflessione potrebbe essere più semplice poiché ci sono molti di questi numeri. Nota: non sono il downvoter. – Brian

6

Beh, è ​​già utilizzato un numero magico nella dichiarazione della struttura. Tanto vale fare questo:

private const int LfaNewOffset = 60; 

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

Cosa succede se non ha accesso alla definizione della struttura? Sono d'accordo non è chiaro se lo fa o no. – jason

+0

@ Jason: se non lo fa, non funzionerà. Ma non vedo molto bisogno di evidenziarlo come un difetto in questo metodo, dal momento che è evidente. – Brian