2016-11-08 136 views
0

我正在做一個項目,我需要將結構化數據寫入二進制文件。首先,我需要編寫一個頭文件,然後從某處獲取數據,填充並將結構化數據塊寫入到所述文件中。我移植C的結構爲C#如下:使用C#將連續結構化數據寫入二進制文件

C頭的結構:

typedef struct 
{ 
    DWORD uSignature;  
    DWORD uRecordLength;  
} Header; 

C數據結構:

typedef struct 
{ 
    DWORD uCode; // a two character identifier    
    char uLabel[10]; 
    int uDate; 
    float uData[37];    
} MyData; 

這裏是C#頭結構:

struct Header 
    { 
     public uint uSignature;  
     public uint uRecordLength;  
    } 

這裏是C#數據結構:

struct MyData 
    { 
     public MyData (int Count) : this() 
     { 
      uData = new Single[Count]; 
     } 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 
     public byte[] uCode;    
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] 
     public byte[] uLabel; 
     public int uDate; 
     [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] 
     public Single [] uData;  
    } 

如果數據格式正確,該文件將由另一個可從二進制文件讀取的應用程序讀取。我打印出兩種結構類型的大小,它們看起來不錯。但是,輸出文件不能被所述應用程序讀取。

所以我有兩個問題:

  1. 是我正確地使用C到C#轉換的數據類型和法警?

  2. 我使用FileStream和BinaryWriter來寫入二進制文件。所有數據(標題和後續數據)必須按順序(連續)。正如我在飛行中創建和寫入數據結構,我不知道如何使用類似分配連續的內存:

public static byte[] GetData(object obj) 
{ 

    var size = Marshal.SizeOf(obj.GetType());    
    var data = new byte[size];    
    IntPtr pnt = Marshal.AllocHGlobal(size); 

     try 
     { 
      Marshal.StructureToPtr(obj, pnt, true); 
      // Copy the array to unmanaged memory. 
      Marshal.Copy(pnt, data, 0, size); 
      return data; 
     } 
     finally 
     { 
      // Free the unmanaged memory. 
      Marshal.FreeHGlobal(pnt); 
     } 
    } 

任何幫助將不勝感激!

[編輯] 我添加了兩種方法來具體的結構數據轉換成字節數組,但文件仍然不能讀:

private byte[] DataToByteArray(MyData data) 
    { 
     int len = 0; 
     var size = Marshal.SizeOf(data.GetType()); 
     var barray = new byte[size]; 
     data.uCode.CopyTo(barray, 0); 
     len += data.uCode.Length; 
     data.uLabel.CopyTo(barray, len); 
     len += data.uLabel.Length; 
     BitConverter.GetBytes(0).CopyTo(barray, len); 
     len += data.uData.Length; 
     Buffer.BlockCopy(data.uData, 0, barray, len, data.uData.Length); 

     return barray; 
    } 

    private byte[] HeadToByteArray(Header data) 
    { 
     var size = Marshal.SizeOf(data.GetType()); 
     var barray = new byte[size]; 
     BitConverter.GetBytes(data.uSignature).CopyTo(barray, 0); 
     BitConverter.GetBytes(data.uRecordLength).CopyTo(barray, 4); 

     return barray; 
    } 

【EDIT2】 這裏是如何工作在C:

#define  NQ_EX   'QN' 
FILE *fout; 
fopen_s(&fout, "path_to_the_file", "wb"); 
Header head = { val1, sizeof(MyData) }; 
fwrite(&head, sizeof(Header), 1, fout); 

while (!stop && data_is_coming) 
{ 
    MyData data; 
    memset(&data, 0, sizeof(data)); 
    data.uCode = NQ_EX; 
    sprintf_s(data.uLabel, "%s", getVal("field1")); 
    data.uData[0] = getVal("field2"); 
    data.uData[1] = getVal("field3"); 
    .... 
    fwrite(&data, sizeof(MyData), 1, fout); 
} 
+5

你正在編寫C#,不是C.如果你不需要直接與同一進程中運行的C代碼的接口,你通常不希望打擾編組,因爲現在你有兩個問題(理解C做什麼,並以某種方式將其轉換爲C#)。如果你使用'BitConverter.GetBytes()'的順序調用,你只有一個問題(理解C的作用),並且這會減少對齊,字節順序和C處理字符串的方式('\ 0'終止)。 –

+1

也許強制佈局是你錯過了什麼?即 [StructLayout(LayoutKind。顯式)] struct MyData {MyData(int Count) :this() { uData = new Single [Count]; } [FieldOffset(0)] [MarshalAs(UnmanagedType.ByValArray,SizeConst = 4)] public byte [] uCode; [FieldOffset(4)] [MarshalAs(UnmanagedType.ByValArray,SizeConst = 10)] public byte [] uLabel; etc ... –

+2

很難誇大@Jeroen評論中提供的建議的重要性和有用性。嘗試通過p/invoke層來讀取和寫入流中的二進制數據真的很少或沒有理由。 .NET有可用的類,如'BinaryReader','BinaryWriter'和'BitConverter'來處理這種場景。它們更易於使用,併產生可靠的結果。坦率地說,即使在C/C++中,使用內存中的佈局來進行序列化也是一個壞主意;太脆弱了,明顯要序列化好得多。不要在C#中保留習慣的錯誤 –

回答

0

排列順序似乎很好。在Jeroen和其他人的幫助下進行了一些更改和測試之後,我能夠使其工作。這個問題是由Block.copy方法造成的。我將其更改爲Array.copy如下:

private byte[] DataToByteArray(MyData data) 
{ 
    int len = 0; 
    var size = Marshal.SizeOf(data.GetType()); 
    var barray = new byte[size]; 
    data.uCode.CopyTo(barray, 0); 
    len += data.uCode.Length; 
    data.uLabel.CopyTo(barray, len); 
    len += data.uLabel.Length; 
    BitConverter.GetBytes(0).CopyTo(barray, len); 
    len += data.uData.Length; 

    for (int i = 0; i < data.uData.Length; i++) 
     Array.Copy(BitConverter.GetBytes(data.uData[i]), 0, barray, len+i * 4, 4); 

    return barray; 
}