2010-01-22 5 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 년대 제외) 진짜 대답을 얻을 것이라고 생각하지 않았고, 나는 여기 세 가지 선택이있다! 나는 선택할 것을 거의 알지 못했기 때문에 그들을 모두 뽑았습니다. 단순히 상수를 정의하는 것은 합리적인 접근법이지만 struct의 레이아웃을 모호하게 만드는 것은 짜증나게합니다. 여전히 관리 형/관리되지 않음의 미묘한 점을 배우고 있지만 wj32는 컴파일러가 이미 구조체에 대한 포인터를 가져다 주었기 때문에 관리 형/비 관리 형 오프셋이 동일하다는 것을 알고 있다고 생각합니다. 가장 쉬운 방법으로 읽는 코드를 생성하는 것으로 보입니다. 모두에게 감사드립니다. –

답변

17

사용 Marshal.OffsetOf을 :

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

+1. 와우, 이것이 정확히 그가 요구 한 것입니다. (여전히 실용적이라고 가정 할 때, 나는 여전히 고귀한 대답을 더 편하게 느낍니다.) – Brian

+1

나는 이것을 모른다. '마샬 (Marshal) '은 관리 대상과 비 관리 대상 간 이동을위한 것입니다.따라서 문서에서 "OffsetOf는 관리되지 않는 구조 레이아웃 측면에서 오프셋을 제공합니다 **는 반드시 관리 구조 레이아웃 **의 오프셋과 일치하지 않습니다." 그래서 어떤 경우에는 이것은 잘못된 대답을 줄 수 있습니다. – jason

+5

왜 관리 구조 오프셋을 알고 싶습니까? 심지어 어떤 식 으로든 사용할 수 없다는 것을 알았더라도 구조체에 대한 포인터를 얻는다고해도 blittable을 의미하기 때문입니다. 귀하의 관찰은 실제적인 목적과는 전혀 무관합니다. – 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가 제안한 것처럼) 거의 항상 반사를 사용하는 것보다 훨씬 더 나은 선택입니다. 반사가 적절하지만 코드 반복을 피하는 경우가 일반적으로 다른 방법으로 수행 될 수있는 경우가 있습니다. 즉, 이러한 숫자가 많으므로 반영이 더 간단 할 수 있습니다. 참고 : 저는 downvoter가 아닙니다. – 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

@ Jason : 그렇지 않으면 작동하지 않습니다. 그러나 나는이 방법의 결함으로 지적 할 필요가별로 없다. 자명하다. – Brian