2008-08-05 17 views
65

से सी # सी में एक सी/सी ++ डेटा संरचना पढ़ना एक बाइट [] सरणी से सी # स्ट्रक्चर भरने का सबसे अच्छा तरीका क्या होगा जहां डेटा सी/सी ++ संरचना से था? सी struct कुछ इस तरह दिखेगा (मेरी सी बहुत जंग लगी है):एक बाइट सरणी

typedef OldStuff { 
    CHAR Name[8]; 
    UInt32 User; 
    CHAR Location[8]; 
    UInt32 TimeStamp; 
    UInt32 Sequence; 
    CHAR Tracking[16]; 
    CHAR Filler[12]; 
} 

और कुछ इस तरह भरने जाएगा:

[StructLayout(LayoutKind.Explicit, Size = 56, Pack = 1)] 
public struct NewStuff 
{ 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] 
    [FieldOffset(0)] 
    public string Name; 

    [MarshalAs(UnmanagedType.U4)] 
    [FieldOffset(8)] 
    public uint User; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)] 
    [FieldOffset(12)] 
    public string Location; 

    [MarshalAs(UnmanagedType.U4)] 
    [FieldOffset(20)] 
    public uint TimeStamp; 

    [MarshalAs(UnmanagedType.U4)] 
    [FieldOffset(24)] 
    public uint Sequence; 

    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)] 
    [FieldOffset(28)] 
    public string Tracking; 
} 

NewStuff को OldStuff कॉपी करने के लिए सबसे अच्छा तरीका क्या है, अगर OldStuff था बाइट [] सरणी के रूप में पारित किया?

मैं वर्तमान में निम्नलिखित की तरह कुछ कर रहा हूं, लेकिन यह तरह का घबराहट लगता है।

GCHandle handle; 
NewStuff MyStuff; 

int BufferSize = Marshal.SizeOf(typeof(NewStuff)); 
byte[] buff = new byte[BufferSize]; 

Array.Copy(SomeByteArray, 0, buff, 0, BufferSize); 

handle = GCHandle.Alloc(buff, GCHandleType.Pinned); 

MyStuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff)); 

handle.Free(); 

क्या इसे पूरा करने का कोई बेहतर तरीका है?


का उपयोग करते हुए BinaryReader वर्ग स्मृति को पिन करने और Marshal.PtrStructure का उपयोग करने पर किसी भी निष्पादन लाभ की पेशकश?

+1

FYI करें, अपने कार्यक्रम विभिन्न मशीनों आप थोड़ा बनाम बड़ा endian को संभालने के लिए आवश्यकता हो सकती है पर चलाता है। – KPexEA

+1

संरचना के स्तर पर आप इसे कैसे संभालेंगे, यानी संरचना में प्रत्येक मान के लिए अलग-अलग बाइट्स को अलग किए बिना? – Pat

उत्तर

87

उस संदर्भ में जो मैं देख सकता हूं उससे आपको SomeByteArray को एक बफर में कॉपी करने की आवश्यकता नहीं है। आपको बस SomeByteArray से हैंडल प्राप्त करने की आवश्यकता है, इसे पिन करें, का उपयोग करके IntPtr डेटा कॉपी करें और फिर रिलीज़ करें। एक प्रतिलिपि की कोई ज़रूरत नहीं है।

यही होगा:

NewStuff ByteArrayToNewStuff(byte[] bytes) 
{ 
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); 
    try 
    { 
     NewStuff stuff = (NewStuff)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(NewStuff)); 
    } 
    finally 
    { 
     handle.Free(); 
    } 
    return stuff; 
} 

जेनेरिक संस्करण:

unsafe T ByteArrayToStructure<T>(byte[] bytes) where T : struct 
{ 
    fixed (byte* ptr = &bytes[0]) 
    { 
     return (T)Marshal.PtrToStructure((IntPtr)ptr, typeof(T)); 
    } 
} 
+0

CS0411 \t विधि 'ByteArrayToStructure (बाइट [], int)' के लिए प्रकार के तर्क उपयोग से अनुमानित नहीं किए जा सकते हैं। प्रकार तर्कों को स्पष्ट रूप से निर्दिष्ट करने का प्रयास करें। (मैंने बाइट सरणी की int अनुक्रमणिका जोड़ा) इसे करने के लिए। – SSpoke

+0

अपवादों की उपस्थिति में स्मृति को रिसाव करेगा। देखें: एक सुरक्षित संस्करण के लिए http://stackoverflow.com/a/41836532/184528। – cdiggins

+0

4.5 के रूप में।1, पीआरटीओस्ट्रक्चर का एक सामान्य संस्करण है, इसलिए उपरोक्त जेनेरिक संस्करण में दूसरी पंक्ति बन सकती है: '' var var = Marshal.PtrToStructure (handle.AddrOfPinnedObject()); '' – RobinHood70

0

यदि आपके पास बाइट है [] आप उपलब्ध रीडएक्स विधियों का उपयोग करके BinaryReader क्लास का उपयोग करने और न्यूस्टफ पर मान सेट करने में सक्षम होना चाहिए।

4

पैकिंग मुद्दों से सावधान रहें:

T ByteArrayToStructure<T>(byte[] bytes) where T: struct 
{ 
    GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); 
    try 
    { 
     T stuff = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
    } 
    finally 
    { 
     handle.Free(); 
    } 
    return stuff; 
} 

सरल संस्करण (unsafe स्विच की आवश्यकता है)। उदाहरण में आपने सभी फ़ील्ड स्पष्ट ऑफसेट पर हैं क्योंकि सब कुछ 4 बाइट सीमाओं पर है लेकिन यह हमेशा मामला नहीं होगा। डिफ़ॉल्ट रूप से 8 बाइट सीमाओं पर दृश्य सी ++ पैक।

+0

"विजुअल सी ++ डिफ़ॉल्ट रूप से ** 8 बाइट ** सीमाओं पर पैक। " यह मेरी समस्या हल हो गया, बहुत बहुत धन्यवाद! –

3
object ByteArrayToStructure(byte[] bytearray, object structureObj, int position) 
{ 
    int length = Marshal.SizeOf(structureObj); 
    IntPtr ptr = Marshal.AllocHGlobal(length); 
    Marshal.Copy(bytearray, 0, ptr, length); 
    structureObj = Marshal.PtrToStructure(Marshal.UnsafeAddrOfPinnedArrayElement(bytearray, position), structureObj.GetType()); 
    Marshal.FreeHGlobal(ptr); 
    return structureObj; 
} 

इस

4

यहाँ accepted answer की एक अपवाद लिए सुरक्षित संस्करण है:

public static T ByteArrayToStructure<T>(byte[] bytes) where T : struct 
{ 
    var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); 
    try { 
     return (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
    } 
    finally { 
     handle.Free(); 
    } 
}