2012-11-05 12 views
5

I mają następujące strukturyMarshal.SizeOf struktura powraca nadmierna liczba

[StructLayout(LayoutKind.Sequential)] 
public struct SFHeader 
{ 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] 
    public string FileName; 

    public int Offset; 

    public short Size; 

    public byte Flags; 

    public byte Source; 


    public long LastWriteTime; 


    public byte[] GetBytes() 
    { 
     int size = Marshal.SizeOf(this); 
     var buffer = new byte[size]; 
     IntPtr ptr = Marshal.AllocHGlobal(size); 

     Marshal.StructureToPtr(this, ptr, true); 
     Marshal.Copy(ptr, buffer, 0, size); 
     Marshal.FreeHGlobal(ptr); 

     return buffer; 
    } 


    public static SFHeader FromBytes(byte[] buffer) 
    { 
     var str = new SFHeader(); 
     int size = Marshal.SizeOf(str); 

     IntPtr ptr = Marshal.AllocHGlobal(size); 
     Marshal.Copy(buffer, 0, ptr, size); 
     str = (SFHeader)Marshal.PtrToStructure(ptr, str.GetType()); 
     Marshal.FreeHGlobal(ptr); 

     return str; 
    } 

} 

trzeba przekonwertować strukturę tablicy bajt (do wysłania w pakiecie z gniazda), więc stosowanie metody GetBytes, ale zwraca tablicę 24 bajtów zamiast tablicę 21 bajtów:

  • pliku (łańcuch): 5 bajtów
  • przesunięcia (int) 4 bajty
  • Rozmiar (krótki): 2 bajty
  • Flagi (bajt): 1 bajt
  • Źródło (bajt): 1 bajt
  • LastWriteTime (długie): 8 bajtów

Więc: 5 + 4 + 2 + 1 + 1 + 8 = 21 bajtów.
Dzieje się tak, ponieważ Marshal.SizeOf zwraca 24, dlaczego? i wydaje się, że bajty w nadmiarze są umieszczone po bajtów łańcucha, w rzeczywistości, na przykład poniższą strukturę:

var header = new SFHeader() 
{ 
    FileName = "aaaa", 
    Offset = 1, 
    Size = 1 
}; 

przekształca się w następującym buforze:

[0] = 97 
[1] = 97 
[2] = 97 
[3] = 97 
[4] = 0 
[5] = 0 
[6] = 0 
[7] = 0 
[8] = 1 
[9] = 0 
[10] = 0 
[11] = 0 
[12] = 1 
[13] = 0 
... The following are all zero (0) 

Piąty szósta i siódma to bajty w nadmiarze. Jak mogę rozwiązać ten problem?

+0

Łańcuch ma dziwną długość, więc pozostałe pola zostały wyrównane. Użycie Jawnego układu może to naprawić. "Sekwencyjny" nie oznacza "ciągły". – harold

+0

Czy ciąg ma zawsze tę samą długość? – SynerCoder

+0

@SynerCoder tak, maksymalnie 5 bajtów. – Nick

Odpowiedz

7

Występuje problem z wyrównaniem bajtów. Próbując zachować pola na granicach słów dla szybkości dostępu, kompilator podsuwa twoje string z 3 dodatkowymi bajtami. Aby rozwiązać ten problem, użyj pola Pack z .

[StructLayout(LayoutKind.Sequential, Pack=1)] // notice the packing here 
public struct SFHeader 
{ 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 5)] 
    public string FileName; 

    public int Offset; 

    public short Size; 

    public byte Flags; 

    public byte Source; 

    public long LastWriteTime; 
} 
+0

Zgadza się. Działa idealnie. Dzięki. – Nick

0

Zamiast ciągów można użyć fixed size buffer.

[StructLayout(LayoutKind.Sequential)] 
public unsafe struct SFHeader 
{ 
    public fixed char FileName[5]; 
    public int Offset; 
    public short Size; 
    public byte Flags; 
    public byte Source; 
    public long LastWriteTime; 

    public byte[] GetBytes() 
    { 
     //omitted 
    } 

    public static SFHeader FromBytes(byte[] buffer) 
    { 
     //omitted 
    } 
} 
+0

Cóż, to jest akward, pytanie już odpowiedział ... – SynerCoder

+2

To daje rozmiar 32, a nie 21. Nawet jeśli zastąpisz 'char' z' byte', nadal otrzymasz 24 od wywołania 'Marshal.SizeOf()'. –

+0

@MattDavis w mojej obronie Nigdy nie pracowałem zbyt wiele przy pomocy struktur: p – SynerCoder