2012-09-05 11 views
10

संक्षिप्त शब्दों में प्रश्न यह है: प्रबंधित डीएलएल से प्रबंधित डीएलएल से प्रबंधित स्मृति में आईटीआरपीआरटी के रूप में वापस आने वाली मेमोरी कैसे मुक्त करें?प्रबंधित सी # से पॉइंटर के साथ प्रबंधित सी # से रिलीज अप्रबंधित स्मृति

विवरण: मान लें कि हमारे पास सरल कार्य है OUTPUT के रूप में दो पैरामीटर लेता है, पहला बाइट सरणी के लिए संदर्भ सूचक है और दूसरा संदर्भ संदर्भ है। फ़ंक्शन कुछ नियमों के आधार पर बाइट्स की मात्रा आवंटित करेगा और स्मृति के सूचक और बाइट्स के आकार और वापसी मूल्य (सफलता के लिए 1 और असफल होने के लिए 0) वापस करेगा।

नीचे कोड ठीक काम करता है और मैं बाइट सरणी सही ढंग से और बाइट्स की गिनती और वापसी मान प्राप्त कर सकते हैं, लेकिन जब मैं स्मृति सूचक का उपयोग कर (IntPtr) मुक्त करने की कोशिश मैं अपवाद:

विंडोज ने TestCppDllCall.exe में ब्रेकपॉइंट ट्रिगर किया है।

यह ढेर के भ्रष्टाचार के कारण हो सकता है, जो TestCppDllCall.exe में किसी भी बग को इंगित करता है या किसी भी डीएलएल को लोड किया गया है।

यह उपयोगकर्ता F12 दबाकर भी हो सकता है जबकि TestCppDllCall.exe पर ध्यान केंद्रित किया गया है।

आउटपुट विंडो में अधिक नैदानिक ​​जानकारी हो सकती है।

स्पष्ट बातें करने के लिए:

  1. अगले सी # कोड अन्य DLL समारोह के साथ सही ढंग से काम एक ही हस्ताक्षर है और स्मृति को मुक्त कराने के किसी भी समस्या के बिना काम करता है।

  2. यदि आपको आवंटन मेमोरी विधि बदलने या किसी अन्य कोड को जोड़ने की आवश्यकता है तो (सी) कोड में कोई भी संशोधन स्वीकार किया जाता है।

  3. मुझे आवश्यक सभी कार्यक्षमता मूल डीएलएल फ़ंक्शन संदर्भ द्वारा दो पैरामीटर (बाइट सरणी और int, सी # [बाइट सरणी और int के इंटपट्रेट] स्वीकार करते हैं) कुछ नियमों के आधार पर उन्हें कुछ मानों के साथ भरें और फ़ंक्शन परिणाम लौटाएं (सफलता या विफल)।


CppDll.h

#ifdef CPPDLL_EXPORTS 
#define CPPDLL_API __declspec(dllexport) 
#else 
#define CPPDLL_API __declspec(dllimport) 
#endif 

extern "C" CPPDLL_API int writeToBuffer(unsigned char *&myBuffer, int& mySize); 

CppDll।सीपीपी

#include "stdafx.h" 
#include "CppDll.h" 

extern "C" CPPDLL_API int writeToBuffer(unsigned char*& myBuffer, int& mySize) 
{ 
    mySize = 26; 

    unsigned char* pTemp = new unsigned char[26]; 
    for(int i = 0; i < 26; i++) 
    { 
     pTemp[i] = 65 + i; 
    } 
    myBuffer = pTemp; 
    return 1; 
} 

सी # कोड:

using System; 
using System.Text; 
using System.Runtime.InteropServices; 

namespace TestCppDllCall 
{ 
    class Program 
    { 
     const string KERNEL32 = @"kernel32.dll"; 
     const string _dllLocation = @"D:\CppDll\Bin\CppDll.dll"; 
     const string funEntryPoint = @"writeToBuffer"; 

     [DllImport(KERNEL32, SetLastError = true)] 
     public static extern IntPtr GetProcessHeap(); 
     [DllImport(KERNEL32, SetLastError = true)] 
     public static extern bool HeapFree(IntPtr hHeap, uint dwFlags, IntPtr lpMem); 
     [DllImport(_dllLocation, EntryPoint = funEntryPoint, CallingConvention = CallingConvention.Cdecl)] 
     public static extern int writeToBuffer(out IntPtr myBuffer, out int mySize); 

     static void Main(string[] args) 
     { 
      IntPtr byteArrayPointer = IntPtr.Zero; 
      int arraySize; 
      try 
      { 
       int retValue = writeToBuffer(out byteArrayPointer, out arraySize); 
       if (retValue == 1 && byteArrayPointer != IntPtr.Zero) 
       { 
        byte[] byteArrayBuffer = new byte[arraySize]; 
        Marshal.Copy(byteArrayPointer, byteArrayBuffer, 0, byteArrayBuffer.Length); 
        string strMyBuffer = Encoding.Default.GetString(byteArrayBuffer); 
        Console.WriteLine("Return Value : {0}\r\nArray Size : {1}\r\nReturn String : {2}", 
         retValue, arraySize, strMyBuffer); 
       } 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine("Error calling DLL \r\n {0}", ex.Message); 
      } 
      finally 
      { 
       if (byteArrayPointer != IntPtr.Zero) 
        HeapFree(GetProcessHeap(), 0, byteArrayPointer); 
      } 
      Console.ReadKey(); 
     } 
    } 
} 

मुझे इस कोड मैं सेट लाइन में बिंदु को तोड़ने डिबग (वापसी 1) और बफर के मूल्य था:

myBuffer = 0x031b4fc0 "ABCDEFGHIJKLMNOPQRSTUVWXYZ‎‎‎‎««««««««î‏" 

और मुझे सी # कोड में वही मान मिला जब फ़ंक्शन कॉल रिटर्न और मान था:

52121536 

परिणाम मुझे सही मेमोरी पॉइंटर मिला और मैं बाइट सरणी मान प्राप्त करने में सक्षम हूं, इस पॉइंटर के साथ इन मेमोरी ब्लॉक को सी # में कैसे मुक्त किया जाए?

कृपया मुझे बताएं कि क्या कुछ स्पष्ट नहीं है या यदि कोई टाइपो है, तो मैं मूल अंग्रेजी स्पीकर नहीं हूं।

उत्तर

13

संक्षिप्त उत्तर: आपको DLL में एक अलग विधि जोड़नी चाहिए जो आपके लिए स्मृति को मुक्त करती है।

लंबा उत्तर: आपके डीएलएल कार्यान्वयन के अंदर स्मृति को आवंटित किया जा सकता है। जिस तरह से आप स्मृति को मुक्त करते हैं, उस तरीके से मेल खाना चाहिए जिसमें आपने स्मृति आवंटित की है। उदाहरण के लिए, new[] (स्क्वायर ब्रैकेट के साथ) को आवंटित स्मृति delete[] (delete या free के विपरीत) से मुक्त होने की आवश्यकता है। सी # ऐसा करने के लिए आपके लिए एक तंत्र प्रदान नहीं करता है; आपको पॉइंटर को वापस C++ पर भेजने की आवश्यकता है।

extern "C" CPPDLL_API void freeBuffer(unsigned char* myBuffer) { 
    delete[] myBuffer; 
} 
+0

आपकी त्वरित प्रतिक्रिया के लिए धन्यवाद। मुझे उम्मीद थी कि सी # में अप्रबंधित स्मृति को मुक्त करने का एक तरीका है। – khaled

+0

मैं dasblinkenlight से सहमत हूं: * सबसे साफ * तरीका आपके .dll में "मुक्त" विधि जोड़ने के लिए तर्कसंगत है। लेकिन पीटर रिची भी सही है: सी # आवंटित (उदाहरण के लिए 'CoTaskMemAlloc() ') के लिए सी # # (" इंटरऑप के माध्यम से) एक "मुक्त" (उदा।' फ्रीकोटास्कम() ') के लिए निश्चित रूप से संभव है। एक चीज जो आप * सी # से नहीं कर सकते, "हटाएं" है, अगर। डीएल एक सी ++ "नया" करता है। "मॉलोक/फ्री": ठीक है। CoTaskMemAlloc/FreeCoTaskMem: भी ठीक है। "नया/मुफ्त": निश्चित रूप से * नहीं *। – paulsm4

+0

@ paulsm4 कृपया बताएं कि सी # मॉलोक/मुफ्त में कैसे आना है? – awattar

3
HeapFree(GetProcessHeap(), 0, byteArrayPointer); 

नहीं, यह काम नहीं कर सकता। ढेर हैंडल गलत है, सीआरटी हीपक्रेट() के साथ अपना खुद का ढेर बनाता है। इसे सीआरटी डेटा में दफनाया गया है, आप इसे प्राप्त नहीं कर सकते हैं। आप तकनीकी रूप से GetProcessHeaps() से संभाल पा सकते हैं, सिवाय इसके कि आप नहीं जानते कि यह कौन सा है।

सूचक भी गलत हो सकता है, सीआरटी ने डीबगिंग डेटा स्टोर करने के लिए हेपएलोक() द्वारा लौटाए गए पॉइंटर से कुछ अतिरिक्त जानकारी जोड़ दी हो सकती है।

आपको बफर को रिलीज़ करने के लिए हटाए गए फ़ंक्शन को निर्यात करने की आवश्यकता होगी []। या एक सी ++/सीएलआई रैपर लिखें ताकि आप रैपर में हटाएं [] का उपयोग कर सकें। अतिरिक्त आवश्यकता के साथ कि सी ++ कोड और रैपर सटीक सीआरटी डीएलएल (/ एमडी आवश्यक) के समान संस्करण का उपयोग करें। यह लगभग हमेशा आवश्यक है कि आप सी ++ कोड को पुन: संकलित कर सकें।

7

यदि आप मूल कोड में अपनी याददाश्त आवंटित कर रहे हैं, तो CoTaskMemAlloc का उपयोग करें और आप Marshal.FreeCoTaskMem के साथ प्रबंधित कोड में पॉइंटर को मुक्त कर सकते हैं। CoTaskMemAlloc "एक COM- आधारित आवेदन में स्मृति साझा करने के लिए एक ही रास्ता" (http://msdn.microsoft.com/en-us/library/windows/desktop/aa366533(v=vs.85).aspx देखें)

आप को नियुक्ति नई उपयोग कर सकते हैं स्मृति एक देशी सी ++ वस्तु के साथ CoTaskMemAlloc साथ आवंटित उपयोग करने की आवश्यकता है, तो के रूप में वर्णन किया गया है स्मृति को आरंभ करें जैसे कि new ऑपरेटर का उपयोग किया गया था। उदाहरण के लिए:

void * p = CoTaskMemAlloc(sizeof(MyType)); 
MyType * pMyType = new (p) MyType; 

new साथ इस स्मृति को आबंटित नहीं है सिर्फ पूर्व आबंटित स्मृति पर निर्माता कहता है।

कॉलिंग Marshal.FreeCoTaskMem इस प्रकार के विनाशक को कॉल नहीं करता है (यदि आपको केवल स्मृति मुक्त करने की आवश्यकता नहीं है); यदि आपको विनाशक को बुलाकर मुफ्त मेमोरी से अधिक करने की आवश्यकता है तो आपको एक देशी विधि प्रदान करनी होगी जो वह करता है और पी/इसे आमंत्रित करता है। प्रबंधित कोड में देशी वर्ग के उदाहरणों को पास करना किसी भी तरह समर्थित नहीं है।

यदि आपको किसी अन्य एपीआई के साथ मेमोरी आवंटित करने की आवश्यकता है, तो आपको प्रबंधित कोड में इसे मुक्त करने के लिए इसे पी/Invoke के माध्यम से प्रबंधित कोड में बेनकाब करना होगा।

+0

+1 - CoTaskMemAlloc विंडोज़ में प्रबंधित/अप्रबंधित (या किसी अन्य क्रॉस भाषा आवंटन) से मेल खाने का आधिकारिक तरीका है। –

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