2009-09-28 13 views
11

मैं Marshal.AllocHGlobal के माध्यम से अपने आवेदन में कुछ अप्रबंधित स्मृति आवंटित कर रहा हूं। मैं फिर Marshal.FreeHGlobal के माध्यम से स्मृति को मुक्त करने से पहले इस स्थान पर बाइट्स का एक सेट कॉपी कर रहा हूं और मेमोरी के परिणामी सेगमेंट को struct में परिवर्तित कर रहा हूं।Marshal.AllocHGlobal द्वारा आवंटित स्मृति को शून्य कैसे करें?

यहाँ विधि है:

public static T Deserialize<T>(byte[] messageBytes, int start, int length) 
    where T : struct 
{ 
    if (start + length > messageBytes.Length) 
     throw new ArgumentOutOfRangeException(); 

    int typeSize = Marshal.SizeOf(typeof(T)); 
    int bytesToCopy = Math.Min(typeSize, length); 

    IntPtr targetBytes = Marshal.AllocHGlobal(typeSize); 
    Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy); 

    if (length < typeSize) 
    { 
     // Zero out additional bytes at the end of the struct 
    } 

    T item = (T)Marshal.PtrToStructure(targetBytes, typeof(T)); 
    Marshal.FreeHGlobal(targetBytes); 
    return item; 
} 

यह सबसे अधिक भाग के लिए काम करता है, फिर भी अगर मैं कम बाइट की तुलना में struct के आकार, आवश्यकता है तो 'यादृच्छिक' मान पिछले क्षेत्रों के लिए (मैं कर रहा हूँ आवंटित कर रहे हैं लक्ष्य संरचना पर LayoutKind.Sequential का उपयोग कर)। मैं इन लटकते क्षेत्रों को यथासंभव कुशलतापूर्वक शून्य करना चाहता हूं।

संदर्भ के लिए, यह कोड लिनक्स पर सी ++ से भेजे गए उच्च आवृत्ति मल्टीकास्ट संदेशों को deserializing है।

// Give only one byte, which is too few for the struct 
var s3 = MessageSerializer.Deserialize<S3>(new[] { (byte)0x21 }); 
Assert.AreEqual(0x21, s3.Byte); 
Assert.AreEqual(0x0000, s3.Int); // hanging field should be zero, but isn't 

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
private struct S3 
{ 
    public byte Byte; 
    public int Int; 
} 

इस परीक्षण चल रहा है बार-बार एक अलग मूल्य के साथ हर बार विफल दूसरा ज़ोर का कारण बनता है:

यहाँ एक असफल परीक्षण का मामला है।


संपादित

अंत में, मैं unsafe जा रहा है और stackalloc का उपयोग करने का leppie's suggestion इस्तेमाल किया। इसने एक बाइट सरणी आवंटित की जिसे आवश्यकतानुसार शून्य किया गया था, और संदेश आकार के आधार पर 50% से 100% के बीच थ्रूपुट में सुधार हुआ (बड़े संदेशों में अधिक लाभ देखा गया)।

public static T Deserialize<T>(byte[] messageBytes, int startIndex, int length) 
    where T : struct 
{ 
    if (length <= 0) 
     throw new ArgumentOutOfRangeException("length", length, "Must be greater than zero."); 
    if (startIndex < 0) 
     throw new ArgumentOutOfRangeException("startIndex", startIndex, "Must be greater than or equal to zero."); 
    if (startIndex + length > messageBytes.Length) 
     throw new ArgumentOutOfRangeException("length", length, "startIndex + length must be <= messageBytes.Length"); 

    int typeSize = Marshal.SizeOf(typeof(T)); 
    unsafe 
    { 
     byte* basePtr = stackalloc byte[typeSize]; 
     byte* b = basePtr; 
     int end = startIndex + Math.Min(length, typeSize); 
     for (int srcPos = startIndex; srcPos < end; srcPos++) 
      *b++ = messageBytes[srcPos]; 
     return (T)Marshal.PtrToStructure(new IntPtr(basePtr), typeof(T)); 
    } 
} 

दुर्भाग्य से यह अभी भी Marshal.PtrToStructure के लिए एक कॉल की आवश्यकता है लक्ष्य प्रकार में बाइट्स कन्वर्ट करने के लिए:

अंतिम विधि जैसी समाप्त हो गया।

उत्तर

2

क्यों न केवल यह जांचें कि start + lengthtypesize के भीतर है या नहीं?

बीटीडब्लू: मैं बस unsafe पर जाऊंगा और अतिरिक्त मेमोरी को शून्य करने के लिए लूप के लिए उपयोग करता हूं।

वह आपको stackalloc का उपयोग करने का लाभ भी देगा जो AllocGlobal से अधिक सुरक्षित और तेज़ है।

+0

@leppie - उपयोगी जानकारी के लिए धन्यवाद। मैं 'स्टैकलोक' भी देखूंगा। मुझे अलग-अलग संदेश आकारों को पूरा करना होगा क्योंकि दोनों टीमें कभी-कभी सिंक्रनाइज़ रिलीज़ से बचने के लिए प्रबंधन कर सकती हैं अगर हम अंत में फ़ील्ड्स जोड़ते हैं जो दूसरे छोर को अनदेखा करते हैं। इसी प्रकार, यदि आपको मूल्यों की आवश्यकता नहीं है, तो आप उन्हें उम्मीद कर सकते हैं और इसके बजाय शून्य प्राप्त कर सकते हैं, जो मामला मैं यहां हासिल करने की कोशिश कर रहा हूं। –

+0

@leppie, मैं इस दृष्टिकोण की ओर झुका रहा हूँ। क्या आप कुछ और विस्तार में जा सकते हैं क्योंकि 'stackalloc' का उपयोग करके _why_ सुरक्षित और तेज़ है? एक बार मेरे पास 'बाइट *' हो, तो इसमें प्रतिलिपि बनाने का सबसे अच्छा तरीका क्या होगा? –

+0

मैंने एक संस्करण एक साथ रखा है जो स्टैक पर एक सरणी को पॉप्युलेट करने के लिए 'stackalloc' के साथ काम करता है। मुझे नहीं लगता कि 'मार्शल.प्रेटोस्ट्रक्चर' को कॉल के आसपास जाना संभव है, है ना? –

2

मैंने कभी भी इस सामग्री को सी # में नहीं किया है, लेकिन मुझे एमएसडीएन में मार्शल। राइटबाइट (इंटप्रेट, इंट 32, बाइट) मिला। कोशिश करो।

2

हाँ के रूप में Jon Seigel ने कहा, आप इसे बाहर Marshal.WriteByte

का उपयोग कर निम्न उदाहरण में शून्य कर सकते हैं, मैं struct प्रतिलिपि करने से पहले बफर को शून्य।

if (start + length > messageBytes.Length) 
    throw new ArgumentOutOfRangeException(); 
int typeSize = Marshal.SizeOf(typeof(T));  
int bytesToCopy = Math.Min(typeSize, length); 
IntPtr targetBytes = Marshal.AllocHGlobal(typeSize); 
//zero out buffer 
for(int i=0; i < typeSize; i++) 
{ 
    Marshal.WriteByte(targetBytes, i, 0); 
} 
Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy); 
+7

Marshal.WriteByte को प्रत्येक कॉल में कामयाब रहे और मूल कोड और वापस, जो एक निश्चित भूमि के ऊपर है। एक पाश में ऐसा करने से अक्षम प्राप्त कर सकते हैं के बीच एक संक्रमण का कारण होगा के साथ। अगर आप चाहते हैं मार्शल वर्ग से चिपक, मैं ऐसा करें चाहते हैं: Marshal.Copy (नई बाइट [typeSize], 0, targetBytes, typeSize) –

+0

अन्य वैकल्पिक मैं के बारे में सोच रहा था पी/LocalAlloc समारोह आह्वान और LPTR में पारित । झंडा –

11
[DllImport("kernel32.dll")] 
static extern void RtlZeroMemory(IntPtr dst, int length); 
... 
RtlZeroMemory(targetBytes, typeSize); 
+0

doscs यह एक मैक्रो का कहना है कि –

+1

Dumpbin.exe kernel32.dll पर कहते हैं कि यह सिर्फ एक मैक्रो नहीं है –

+0

@MattiasS -।। मैं 'डीएसटी पर को शून्य करने की जरूरत है + N'। 'IntPtr' अंकगणित का समर्थन नहीं करता है, तो मैं इस ऑफसेट को कैसे संबोधित कर सकता हूं? –

6

यह विंडोज पर ठीक से काम करेगा:

namespace KernelPInvoke 
{ 
    /// <summary> 
    /// Implements some of the C functions declared in string.h 
    /// </summary> 
    public static class MemoryWrapper 
    { 
     [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)] 
     static extern void CopyMemory(IntPtr destination, IntPtr source, uint length); 

     [DllImport("kernel32.dll", EntryPoint = "MoveMemory", SetLastError = false)] 
     static extern void MoveMemory(IntPtr destination, IntPtr source, uint length); 

     [DllImport("kernel32.dll", EntryPoint = "RtlFillMemory", SetLastError = false)] 
     static extern void FillMemory(IntPtr destination, uint length, byte fill); 
    } 

    var ptr = Marshal.AllocHGlobal(size); 
    try 
    { 
     MemoryWrapper.FillMemory(ptr, size, 0); 
     // further work... 
    } 
    finally 
    { 
     Marshal.FreeHGlobal(ptr); 
    } 
} 
0

मुझे लगता है कि एक बफर को शून्य करने के लिए सबसे अच्छा तरीका यह है, अगर आप नहीं चाहते हैं, या नहीं जा सकते दूसरा रास्ता:

for(int i=0; i<buffSize; i++) 
{ 
    Marshal.WriteByte(buffer, i, 0x00); 
} 
2
for(int i=0; i < buffSize/8; i += 8) 
{ 
    Marshal.WriteInt64(buffer, i, 0x00); 
} 

for(int i= buffSize % 8 ; i < -1 ; i--) 
{ 
    Marshal.WriteByte (buffer, buffSize - i, 0x00); 
} 

मुझे लगता है कि आप इसे मिलेगा कई गुना तेजी से हमें होने के लिए 8 बिट wrights के बजाय 64 बिट wrights में प्रवेश (जो आपको अभी भी पिछले कुछ बाइट्स की आवश्यकता है)।

संबंधित मुद्दे