2013-04-05 2 views
0

나는 .NET 2.0을 타겟으로하는 SafeBuffer과 비슷한 클래스를 만들고있다. 함수 중 하나는 배열로 (blittable) 일련의 구조를 읽고 쓰는 void ReadArray<T>(long position, T[] array, int offset, int count) (또는 WriteArray)입니다.CLR Blittable 구조체의 정렬 된 크기를 얻는 방법?

내 첫 번째 추측은 과 함께 Marshal.PtrToStructure/StructureToPtr을 사용하는 것입니다. 그러나 SafeBuffer.ReadArray의 일리노이를 보면 발전 (내부 방법)에 Marshal.AlignedSizeOf<T>()을 사용한다는 것을 알 수 있습니다. 이 함수는 다음과 같이 정의됩니다.

uint s = Marshal.SizeOf<T>(); 
if (s == 1u || s == 2u || IntPtr.Size == 8 && s == 4u) { return s; } 
return Marshal.AlignedSizeOfType(typeof(T)); // an internalcall 

이 메서드는 .NET 4.0에서만 정의되어 있으므로 나 (Rotor에서도 사용할 수 없음)로 정의됩니다.

제 아이디어는 배열의 인접 요소에 Marshal.UnsafeAddrOfPinnedArrayElement을 사용하는 것이지만 작동하지 않습니다. 여기 내 테스트 코드입니다 : (? 기본 AlignedSizeOf가 어떻게 다른지 알) 86 또는 64에 6, 6, 6,8를 출력

using System; 
using System.Reflection; 
using System.Runtime.InteropServices; 

namespace test 
{ 
    class Program 
    { 
     [StructLayout(LayoutKind.Sequential)] 
     struct A 
     { 
      byte a; 
      short x; 
      byte b; 
     } 

     private static MethodInfo MarshalAlignedSizeOf; 
     static int MAlignedSizeOf(Type t) 
     { 
      if (MarshalAlignedSizeOf == null) { MarshalAlignedSizeOf = typeof(Marshal).GetMethod("AlignedSizeOf", BindingFlags.NonPublic | BindingFlags.Static); } 
      return (int)(uint)MarshalAlignedSizeOf.MakeGenericMethod(t).Invoke(null, null); 
     } 

     static int AlignedSizeOf(Type t) 
     { 
      Array a = Array.CreateInstance(t, 0); 
      GCHandle pin = GCHandle.Alloc(a, GCHandleType.Pinned); 
      try 
      { 
       return (int)(Marshal.UnsafeAddrOfPinnedArrayElement(a, 1).ToInt64() - Marshal.UnsafeAddrOfPinnedArrayElement(a, 0).ToInt64()); 
      } 
      finally { pin.Free(); } 
     } 

     unsafe static void Main(string[] args) 
     { 
      Console.WriteLine("sizeof:  " + sizeof(A)); 
      Console.WriteLine("SizeOf:  " + Marshal.SizeOf(typeof(A))); 
      Console.WriteLine("aligned size: " + AlignedSizeOf(typeof(A))); 
      Console.WriteLine("mars algn sz: " + MAlignedSizeOf(typeof(A))); 
     } 
    } 
} 

.

그래서 질문은 다음과 같습니다

  1. 토론 : 이유는 크기가 정상 크기보다 다른 정렬? C/C++에서 sizeof()는 완전히 정렬 된 값입니다. (sizeof(arr)/sizeof(arr[0]) 항상 작동)
  2. blittable 일반 구조체의 정렬 된 크기를 얻으려면 관리되는 방법 (안전하지 않은 코드 사용 또는 사용 안함)이 있습니까?
  3. 방금 ​​SizeOf()을 사용해야하며이 추가 정렬에 신경 쓰지 않아도 되나요? 그럴 경우 그냥 블록 전송을 할 수 있습니다 ...

답변

1

옵션 3을 선택해야하며, StructureToPtrNative()에도 액세스 할 수 없습니다. Marshal.StructureToPtr()을 사용하면 값 형식 값을 복사 할 수 있으며 Marshal.SizeOf()를 사용하여 값을 측정해야합니다.

그건 어디서 멈출까요. 다른 질문과 관련하여 CLR이 내부 저장소에 사용하는 정확한 레이아웃 규칙은 매우 독특하고 리버스 엔지니어링이 매우 어렵습니다. 배열에 저장 될 때 예제 구조체의 크기는 실제로 8입니다. 패딩의 두 여분의 바이트가 추가됩니다. 모든 필드를 올바르게 정렬하려면 6 개만 필요합니다. CLR 내부에 묻혀있는 이유는 무엇인지 모릅니다. 이것을 실제로 보려고 디버거를 시험해보고 싶다면 this answer이 그것을 어떻게하는지 보여줍니다.

static void Main(string[] args) { 
     var arr = new A[] { new A() { a = 1, x = 2, b = 3}, new A { a = 0x11, x = 0x12, b = 0x13 }}; 
    } // <== set breakpoint here 

그리고 흥미롭게도

0x000000001D67DE70 98 73 14 03 00 00 00 00 01 00 02 00 03 00 00 00 11 00 12 00 13 00 00 00 
              ^arr[0]    ^arr[1] 
+0

를 얻기 위해 디버거 메모리 창의 주소 상자에 "& 편곡"를 넣어, 나는 11 00 12 00 13 00''01 00 02 00 03 00를 참조하십시오. 내가 왜 다른 것을 볼 수 있는지 모르겠다 ... (x86과 x64, 디버그 모드, .NET 4에서 테스트 됨). 원래 당신이 가지고있는 것과 같은 것을 볼 것으로 예상 되었기 때문에 나는 배열 요소 위치의 차이점을 시험해 보았습니다. – coderforlife

+0

그건. NET 3.5. 제가 지적했듯이 엔지니어를 리버스하는 것은 매우 어렵습니다. –

+0

여전히 정렬 패턴을 재현 할 수 없지만 CLR 엔진에서 "구현 마술"이라고 생각합니다 ... 나는 Marshal.SizeOf'를 사용하여 그런 식으로 저장합니다. – coderforlife