2010-01-22 20 views
7

Şunun gibi bir C# yapısına sahip olduğumu varsayalım:Yapı ofseti almak için C# komutu?

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

Şimdi bir dosyadan veri okuduğumu varsayalım:

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

Şimdi okuduğumdan emin olmak için n test etmek istiyorum e_lfanew değerini almak için yeterli bayt. Yeniden yazmadan 60 değerini (FieldOffset) alabileceğim bir yol var mı? Bunun gibi bir şey arıyorum:

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

Böyle bir komut var mı? Benim gerçek kodumda, bu testlerin birçoğunu yapmak zorundayım ve bu sayıları, yapıdaki önceki alanları ekleyerek ya da FieldOffset özniteliklerinin değerlerini kopyalarken, el ile rakamları elle yazmak sıkıcı ve hataya eğilimli görünüyor. Daha iyi bir yolu var mı?

+0

Vay, ben (nobugz yıllardan hariç) herhangi bir gerçek cevap almak sanmıyordum ve burada üç seçenek var! Hangisini seçeceğimi pek bilmedim, bu yüzden hepsini oy kullandım. Sadece sabitleri tanımlamak mantıklı bir yaklaşımdır, ancak yapının düzenini nasıl etkilediğini sinirlendiriyor. Ben hala yönetilen/yönetilmeyen inceliklerini öğreniyorum, ama wj32 haklı olduğunu düşünüyorum çünkü derleyici bana yapıya bir işaretçi göstermeme izin veriyor, yönetilen/yönetilmeyen ofsetlerin aynı olduğunu biliyorum. Bu yüzden onun cevabı ile gidiyorum çünkü okunması en kolay kodu üretiyor gibi görünüyor. Böyle harika cevaplar için hepinize teşekkür ederim! –

cevap

17

Kullanım Marshal.OffsetOf:

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

+1. Vay, bu tam olarak ne istediğini (hala pratik olduğunu varsayarak, nobugz'un cevabı ile daha rahat hissediyorum rağmen). – Brian

+1

Bunun hakkında bir şey bilmiyorum; 'Marshal' yönetilen ve yönetilmeyen arasında gidip geliyor.Böylece "OffsetOf" dokümantasyonu, ofsetin yönetilen yapı düzeni ** açısından telafi edilmesini sağlar. Bu durum, yönetilen yapı düzeninin ** ofsetine karşılık gelmez. Yani bazı durumlarda bu yanlış cevap verebilir. – jason

+5

Ve neden yönetilen yapı ofsetini bilmek istersiniz? Bunu herhangi bir şekilde kullanamayacağınızı biliyor olsanız bile, yapıya bir işaretçi bile elde etmek bloke edilebilir demektir. Gözlemin pratik amaçlar için tamamen ilgisiz. – wj32

6

Evet, bunu yansıma kullanarak yapabilirsiniz.

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

Hatta güzel bir yöntem haline bu açabilirsiniz:

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

Kullanımı:

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

+1 Kabul edildi. Yansıma, bu konuya yaklaşmanın en güvenli (determinist ve sürdürülebilir) yoludur. –

+0

@Downvoter: Lütfen açıklayın. Teşekkürler! – jason

+0

Kod tekrarından kaçınmak bağlamında, bir sabit tanımlamak (nobugz tarafından önerildiği gibi) hemen hemen her zaman yansıma kullanmaktan çok daha iyi bir seçimdir. Yansımanın uygun olduğu durumlar vardır, ancak kod tekrarından kaçınmak genellikle başka yollarla da yapılabilir. Bununla birlikte, bu sayıların bir çoğu olduğu için yansıma daha basit olabilir. Not: Ben downvoter değilim. – Brian

6

Eh, zaten yapı bildiriminde sihirli sayı kullanılır. hem bunu Olabilir:

private const int LfaNewOffset = 60; 

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

Yapı yapısının tanımına erişemezse ne olur? Kabul edip etmediği net değil. – jason

+0

@ Jason: Eğer yapmazsa, bu işe yaramaz. Ancak, bu yöntemde bir kusur olarak bunu göstermeye pek gerek duymuyorum, çünkü kendini belli ediyor. – Brian