2013-03-10 28 views
0

我要將DataTable保存爲* .dbf文件(dBase IV)。所以我有這樣的結構:`Marshal.StructureToPtr`拋出異常,試圖獲取結構的字節數組表示

[StructLayout(LayoutKind.Explicit, Pack = 1)] 
public struct DbfHeader 
{ 
    [FieldOffset(0)] 
    private byte versionNumber; 

    [FieldOffset(1)] 
    private byte yearOfLastUpdate; 

    [FieldOffset(2)] 
    private byte monthOfLastUpdate; 

    [FieldOffset(3)] 
    private byte dayOfLastUpdate; 

    [FieldOffset(4)] 
    private Int32 numberOfRecords; 

    [FieldOffset(8)] 
    private Int16 lengthOfHeader; 

    [FieldOffset(10)] 
    private Int16 lengthOfEachRecord; 

    [FieldOffset(12)] 
    private Int16 reserved1; 

    [FieldOffset(14)] 
    private byte incompleteTransaction; 

    [FieldOffset(15)] 
    private byte encryptionFlag; 

    [FieldOffset(16)] 
    private byte[] freeRecordThread; 

    [FieldOffset(20)] 
    private byte[] reserved2; 

    [FieldOffset(28)] 
    private byte mdxFlag; 

    [FieldOffset(29)] 
    private byte languageDriver; 

    [FieldOffset(30)] 
    private Int16 reserved3; 

    public DbfHeader(int numberOfRecords, int numberOfFields, short recordLength, Encoding encoding) 
    { 
     // some code that initialize each field 
    } 

} 

而且,我必須DbfHeader變量轉換爲byte[]陣列像這樣的方法:

public static byte[] StructureToByteArray<T>(T structure) 
{ 
    int len = Marshal.SizeOf(structure); 
    byte[] result = new byte[len]; 
    IntPtr ptr = Marshal.AllocHGlobal(len); 
    Marshal.StructureToPtr(structure, ptr, true); 
    Marshal.Copy(ptr, result, 0, len); 
    Marshal.FreeHGlobal(ptr); 
    return result; 
} 

但是這種方法是行不通的。在Marshal.StructureToPtr(structure, ptr, true)線,我得到這個異常:

Attempted to read or write protected memory. 
This is often an indication that other memory is corrupt. 

任何人都知道是怎麼回事了?任何幫助將不勝感激。

+1

雖然這是一個在C標準的做法,在C#在字節數組和結構之間複製並不常見。編組旨在用於調用非託管代碼,這是您在此處未執行的操作。改爲查看[BinaryWriter類](http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx)。 – dtb 2013-03-10 08:23:26

+0

@dtb是的。那就對了。但是用BinaryReader/BinaryWriter類讀取/寫入所有這些字段看起來是硬編碼。當你必須爲幾個結構做這件事時,它會變得更加困難。所以我認爲編寫一個單一的函數可以解決我所有的結構。 – 2013-03-10 08:36:22

+0

@dtb是否有任何其他方式使用單個函數映射字節數組和結構(在C#中)? – 2013-03-10 08:41:02

回答

3

爲什麼你通過fDeleteOld爲真Marshal.StructureToPtr()

就我所見,您應該通過false

我想你也應該複製內存後調用Marshal.DestroyStructure():

public static byte[] StructureToByteArray<T>(T structure) 
{ 
    int len = Marshal.SizeOf(structure); 
    byte[] result = new byte[len]; 
    IntPtr ptr = Marshal.AllocHGlobal(len); 
    Marshal.StructureToPtr(structure, ptr, false); 
    Marshal.Copy(ptr, result, 0, len); 
    Marshal.DestroyStructure(ptr, typeof(T)); 
    Marshal.FreeHGlobal(ptr); 
    return result; 
} 

它之前崩潰的原因是因爲經過fDeleteOld爲真假設你有已經稱爲Marshal.StructureToPtr()爲那個IntPtr。因爲沒有,它崩潰了(內存塊沒有按照StructureToPtr()期望的方式初始化)。

但是,您仍然需要按照上面的示例調用Marshal.DestroyStructure()來清理內存。這是清理用於包含引用的結構的數據所必需的。 (您的特定示例結構不包含引用,但您可以將這樣的結構傳遞給您的StructureToByteArray())。

最後要注意的是,如果你這樣做,它不會崩潰(此代碼是沒有意義以外的其他證明fDeleteOld標誌使用方式):

Marshal.StructureToPtr(structure, ptr, false); // First time; must be false. 
Marshal.StructureToPtr(structure, ptr, true); // Second time: Now it can be true. 
+0

它解決了問題!但是你能解釋爲什麼當'fDeleteOld'設置爲true時它會拋出異常嗎? – 2013-03-10 10:47:15

+0

我將編輯我的答案 – 2013-03-10 10:47:34

+0

謝謝。它幫助了我很多。 – 2013-03-10 11:17:05