2010-10-05 62 views
0

我想填充一個結構(不一定是一個實際的結構),與從一個字節[]加載數據。加載二進制數據到一個結構

有在字節[]許多不同的數據結構,其中之一是一個字符串,它被聲明爲:

UInt16 stringLenght 
byte[stringLenght] zeroTerminatedString 

I「C」語言這可以通過聲明一個固定尺寸結構來處理,而不是包含實際字符串的結構,請指向該字符串。

喜歡的東西:

UInt16 stringLength 
char* zeroTerminatedString 

是否有(智能)的方式做在C#中類似的東西?我的意思是從文件/內存加載二進制數據並將其填充到結構中?

問候 雅各布Justesen

+2

您提出的C代碼有問題,因此您將其轉換爲C#的機會註定要失敗! – 2010-10-05 11:32:47

+0

這取決於。多少條數據記錄?他們多大? – 2010-10-05 11:34:44

回答

0

Marshal應該能夠爲你做這個。

請注意,只能對結構進行此操作,您可能需要使用StructLayout屬性。

我不是100%確定如何處理字符串或數組,但BStrWrapperArrayWithOffset可能的幫助,也要留意尋找類似的類/屬性(我知道我已經完成了像這樣的東西來綁定到本地功能)。

4

這不是如果文件中的記錄包含一個字符串,那麼你聲明的結構相似,你會如何聲明它在C:

struct Example { 
    int mumble; // Anything, not necessarily a string length 
    char text[42]; 
    // etc... 
}; 

等價的C#聲明會是什麼樣子:

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] 
    private struct Example { 
     public int mumble; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 42)] 
     public string text; 
     // etc... 
    } 

您通常會使用BinaryReader來讀取數據。但它不能直接處理這樣的字符串,你必須將它們作爲byte []讀取,然後自己進行字符串轉換。你也不能利用聲明語法,你必須爲結構的每個成員編寫一個調用。

有一個解決方法,Marshal類已經知道如何使用PtrToStructure()方法將非託管結構轉換爲託管結構。這是一個通用的實現,它適用於任何blittable類型。兩個版本,一個是從byte []讀取的靜態文件,另一個是從流中重複讀取的實例方法。你會使用一個FileStream或MemoryStream。

using System; 
using System.IO; 
using System.Runtime.InteropServices; 

class StructTranslator { 
    public static bool Read<T>(byte[] buffer, int index, ref T retval) { 
     if (index == buffer.Length) return false; 
     int size = Marshal.SizeOf(typeof(T)); 
     if (index + size > buffer.Length) throw new IndexOutOfRangeException(); 
     var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
     try { 
      IntPtr addr = (IntPtr)((long)handle.AddrOfPinnedObject() + index); 
      retval = (T)Marshal.PtrToStructure(addr, typeof(T)); 
     } 
     finally { 
      handle.Free(); 
     } 
     return true; 
    } 

    public bool Read<T>(Stream stream, ref T retval) { 
     int size = Marshal.SizeOf(typeof(T)); 
     if (buffer == null || size > buffer.Length) buffer = new byte[size]; 
     int len = stream.Read(buffer, 0, size); 
     if (len == 0) return false; 
     if (len != size) throw new EndOfStreamException(); 
     var handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
     try { 
      retval = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
     } 
     finally { 
      handle.Free(); 
     } 
     return true; 
    } 

    private byte[] buffer; 
} 

未經測試,希望它能正常工作。