2013-08-31 15 views
1

因此,我有一個Delphi應用程序,它將各種類型的記錄,通過stream.Write(record, sizeof(record))扔到內存流中,並通過命名管道發送它們。1:1 C#結構與流式Delphi記錄的對齊可能嗎?

藉此Delphi的記錄:

Type TAboutData = record 
    Version : array[0..4] of Byte; 
    Build : Word; 
    BuildDate : TDateTime; 
    SVNChangeset : Word; 
end; 

當這樣在命名管道送出,它出來像這樣在一個byte []數組:

長度:22個字節

0x06,0x00,0x00,0x00,4字節用於陣列

0x00,0x00,0x00,0x00,2個字節用於構建,2個字節用於對齊?

爲0x15,0xA3執行,0x86可以,0x3F的,用於雙

0xBC,0×44,0xE4,0x40的8個字節,

0xA3執行,0×02,0x00時,0×00,2個字節用於SVNChangeSet,2字節對齊?

0x00,0x00,2個字節的其他東西?

對齊問題

  1. 我相信這就是所謂的定位在4個字節的boundries,是否正確?
  2. 什麼是最後兩個字節?

現在我試圖(不成功)將這個元素編入到C#結構中。

[StructLayout(LayoutKind.Sequential)] 
    struct TAboutInfo 
    { 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 
     public byte[] Version; 
     public ushort Build; 
     public double BuildDate; 
     public ushort SVNChangeSet; 
    }  

    IntPtr ptr = Marshal.AllocHGlobal(bytebuffer.Length); 
    Marshal.Copy(ptr, bytebuffer, 0, bytebuffer.Length); 
    TAboutInfo ta = (TAboutInfo)Marshal.PtrToStructure(ptr, typeof(TAboutInfo)); 
    Marshal.FreeHGlobal(ptr); 

C#問題

  1. 這根本不起作用,我真的不能弄清楚如何解釋對齊。我已經嘗試了明確的偏移量,但是我會縮短。
  2. 我有很多記錄類型,其中一些成員是其他記錄的動態數組。我寧願想出一個強大的解決方案來將這些字節數組轉換爲結構或對象。
+0

順便說一下,Byte'的array [0..4]包含5個字節,而不是4.什麼是Delphi中的SizeOf(記錄)?我猜這是20,而不是22. – Inspired

+0

Delphi 2007和XE4中的SizeOf(TAboutData)都給出了24,這與填充有關。 –

回答

3

對齊方式通常被編譯器用作優化。實際上,每個結構都填充到4的倍數(或8我不記得確切)。

更新:我上面說的對齊不準確。閱讀David的回答,瞭解編譯器如何處理對齊記錄的詳細信息。 Wikipedia文章包含合理的概述:http://en.wikipedia.org/wiki/Data_structure_alignment

無論如何,您可以使用Pack參數指定對齊。 Pack值爲1將返回結構的確切大小。

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 

關於數組,它的正確使用方法:

[MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] 
public char[] someCharArray; 

當你從結構轉換爲字節數組,數組必須聲明的大小相匹配只是照顧。在轉換之前,如果內容較短,則應使用Array.Resize

struct.someCharArray = "Hello".ToCharArray(); 
Array.Resize<char>(ref struct.someCharArray, 20); 

關於編組我用這個方法從字節[]到結構:

public static T ParseStructure<T>(byte[] array, int offset) where T : struct 
    { 
     if (array == null) throw new ArgumentNullException("array", "Input parameter cannot be null"); 

     if (array.Length - offset < Marshal.SizeOf(typeof(T))) 
      Array.Resize<byte>(ref array, Marshal.SizeOf(typeof(T)) + offset); 

     int arraySize = array.Length - offset; 
     T returnItem; 

     // Allocate some unmanaged memory. 
     IntPtr buffer = Marshal.AllocHGlobal(arraySize); 

     // Copy the read byte array (byte[]) into the unmanaged memory block. 
     Marshal.Copy(array, offset, buffer, arraySize); 

     // Marshal the unmanaged memory block to a structure. 
     returnItem = (T)Marshal.PtrToStructure(buffer, typeof(T)); 

     // Free the unmanaged memory block. 
     Marshal.FreeHGlobal(buffer); 

     return returnItem; 
} 

而這種方法則正好相反:

public static byte[] StructureToArray<T>(T structure) where T : struct 
    { 
     int objectSize = Marshal.SizeOf(structure); 
     byte[] result = new byte[objectSize]; 
     IntPtr buffer = Marshal.AllocHGlobal(objectSize); 

     object dataStructure = (object)structure; 

     Marshal.StructureToPtr(dataStructure, buffer, true); 

     Marshal.Copy(buffer, result, 0, objectSize); 
     Marshal.FreeHGlobal(buffer); 
     return result; 
    } 

另外,請使用下面的代碼來檢查由框架計算的尺寸:

int objectSize = Marshal.SizeOf(structure); 

最後,我發現這個不錯的article關於編組。

+0

我發誓,當你的一個答案的綠洲完成了我的工作時,我正勉強地弄明白這一點。謝謝你!你是男人! –

+0

不需要Pack = 1。你不應該這樣做,沒有理由。所以爲什麼?而你的第一段完全不正確。檢查包含單個字節的結構的大小。 –

+0

我很贊同我的觀點。但你的答案不是這樣。從第一段開始,這是完全錯誤的。 –

2

我打算假設你的Delphi編譯器在默認模式下運行,所以對齊記錄。我還假設您的Pascal代碼包含一個簡單的錯字,其中您的數組有5個元素,而不是4個。

對齊記錄的對齊取決於具有最大對齊的成員。最大的成員是8字節雙。所以該記錄具有對齊8.其大小是其對齊的精確倍數。

每個單獨的字段都對齊以匹配字段的對齊方式。字節數組有對齊方式1,可以出現在任何地方。 2個字節的整數必須出現在2個字節的邊界上,依此類推。記錄最後可以填充以確保記錄的數組也將對齊。所以記錄的大小是8的倍數。你的記錄大小爲24.

在你的情況下,填充位於8字節雙倍之前,並且在記錄的末尾。如果未包含末尾的填充,則8字節雙精度值將在您記錄的任何數組中錯誤對齊。

沒有必要在你的Delphi記錄和C#結構聲明中做任何修改。它們已經被正確地聲明,並且完美匹配。

您可以使用Delphi中的SizeOf和C#中的Marshal.SizeOf來檢查結構的大小。你會發現它們與問題中的代碼相匹配。

我不能評論你的代碼失敗,因爲你沒有描述失敗。我的主要觀點是,任何失敗都不是因爲你的結構之間的不匹配。關於數字22的來源沒有理性的解釋。我想看看這個數字來自哪裏。

最後,您接受的答案建議使用包裝結構。沒有必要這樣做,我看不到有這樣做的理由,也沒有解釋你的問題。

+0

大小「22」是我從德爾福得到的數據包的長度。 TAboutData被填充,然後通過TPipeServer.SendStream()發送[它沒有被序列化,它只是被讀入一串字節]。由於某種原因,它看起來缺少最後兩個填充字節? Delphi應用程序是我無法訪問的封閉源碼應用程序。 我應該評論說Pack參數是不需要的。我很高興能有一個實際上可以工作的StructureToPtr()的替代方案。 –

+0

我沒有看到替代方案。 PtrToStructure是它。 –

+0

對不起,「執行」不是「替代」 –