2011-12-30 8 views
16

मैं सी ++/सीएलआई प्रतिनिधियों के साथ प्रयोग कर रहा हूं (जैसा कि मैं एक .NET संदर्भ पुस्तकालय बनाने की कोशिश कर रहा हूं), और मुझे निम्न समस्याएं आ रही हैं।सी ++/सीएलआई प्रतिनिधि फंक्शन पॉइंटर (System.AccessViolationException)

मैं सी ++/सीएलआई में एक प्रतिनिधि को परिभाषित करता हूं, और उसके बाद सी # में प्रतिनिधि का एक उदाहरण बना देता हूं, और उसके बाद एक फंक्शन पॉइंटर के माध्यम से अप्रबंधित सी ++ के माध्यम से प्रतिनिधि का उदाहरण कॉल करता हूं। यह सभी उम्मीद के अनुसार काम करता है।

कोड इस (पहले मेरी सी #) को वर्णन करने

using System; 

namespace TestProgram 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Library.Test.MessageDelegate messageDelegate = new Library.Test.MessageDelegate(Message); 
      Library.Test test = new Library.Test(messageDelegate); 
      test.WriteMessage(); 
      Console.Read(); 
     } 

     static void Message() 
     { 
      Console.WriteLine(1024); 
     } 
    } 
} 

अगला अपने प्रबंधित C++ फ़ाइल (Managed.cpp)

#include "Unmanaged.hpp" 
#include <string> 

namespace Library 
{ 
    using namespace System; 
    using namespace System::Runtime::InteropServices; 

    public ref class Test 
    { 
    public: 
     delegate void MessageDelegate(); 
    internal: 
     MessageDelegate^ Message; 
     void* delegatePointer; 

    public: 
     Test(MessageDelegate^ messageDelegate) 
     { 
      delegatePointer = (void*)Marshal::GetFunctionPointerForDelegate(messageDelegate).ToPointer(); 
     } 

     void WriteMessage() 
     { 
      Unmanaged::WriteMessage(delegatePointer); 
     } 
    }; 
} 

और मेरे अप्रबंधित सी ++ फ़ाइल (Unmanaged.cpp)

#include "Unmanaged.hpp" 

namespace Unmanaged 
{ 
    typedef void (*WriteMessageType)(); 
    WriteMessageType WriteMessageFunc; 

    void WriteMessage(void* Function) 
    { 
     WriteMessageType WriteMessageFunc = (WriteMessageType)(Function); 
     WriteMessageFunc(); 
    } 
} 

यह कोड सभी अपेक्षित काम करता है, और आउटपुट "1024" है, क्योंकि विधि() को फ़ंक्शन पॉइंटर द्वारा डेल में बुलाया जाता है egate विधि।

तर्क के साथ एक प्रतिनिधि के साथ एक ही विधि को लागू करने का प्रयास करते समय मेरी समस्या उत्पन्न होती है, जो है: प्रतिनिधि शून्य संदेश डिलीगेट (int संख्या);

मेरे कोड अब इस प्रकार (सी #) है:

using System; 

namespace AddProgram 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Library.Test.MessageDelegate messageDelegate = new Library.Test.MessageDelegate(Message); 
      Library.Test test = new Library.Test(messageDelegate); 
      test.WriteMessage(1024); 
      Console.Read(); 
     } 

     static void Message(int number) 
     { 
      Console.WriteLine(number); 
     } 
    } 
} 

मेरे कामयाब सी ++ फ़ाइल:

#include "Unmanaged.hpp" 
#include <string> 

namespace Library 
{ 
    using namespace System; 
    using namespace System::Runtime::InteropServices; 

    public ref class Test 
    { 
    public: 
     delegate void MessageDelegate(int number); 
    internal: 
     MessageDelegate^ Message; 
     void* delegatePointer; 

    public: 
     Test(MessageDelegate^ messageDelegate) 
     { 
      delegatePointer = (void*)Marshal::GetFunctionPointerForDelegate(messageDelegate).ToPointer(); 
     } 

     void WriteMessage(int number) 
     { 
      Unmanaged::WriteMessage(delegatePointer, number); 
     } 
    }; 
} 

और मेरे अप्रबंधित सी ++ फ़ाइल:

#include "Unmanaged.hpp" 

namespace Unmanaged 
{ 
    typedef void (*WriteMessageType)(int number); 
    WriteMessageType WriteMessageFunc; 

    void WriteMessage(void* Function, int number) 
    { 
     WriteMessageType WriteMessageFunc = (WriteMessageType)(Function); 
     WriteMessageFunc(number); 
    } 
} 

जब मैं निष्पादित प्रोग्राम मुझे निम्न त्रुटि मिलती है:

अप्रबंधित लाइब्रेरी Test.dll

अतिरिक्त जानकारी: संरक्षित स्मृति को पढ़ने या लिखने का प्रयास किया गया है, के प्रकार का एक अनचाहे अपवाद 'System.AccessViolationException' का एक अनचाहे अपवाद हुआ। यह अक्सर एक संकेत है कि अन्य स्मृति भ्रष्ट है।

संयोग से, कंसोल विंडो 1024 दिखाती है, लेकिन उसके बाद एक यादृच्छिक int (~ 1000000) होती है, और फिर मुझे त्रुटि मिलती है।

मैं इस त्रुटि को प्राप्त करने के कुछ कारणों की कल्पना करना शुरू कर सकता हूं, लेकिन मुझे यकीन नहीं है और मुझे पता लगाने में कठिनाई हो रही है। अगर कोई मुझे बता सकता है कि मुझे यह त्रुटि क्यों मिल रही है, और मैं इसे ठीक करने के लिए क्या कर सकता हूं, तो मैं इसकी सराहना करता हूं।

+0

+1 एक अच्छी तरह से वर्णित प्रश्न –

उत्तर

11
void WriteMessage(void* Function, int number) 

शून्य के रूप में फ़ंक्शन पॉइंटर्स पास करना * एक बहुत बुरा विचार है। यह संकलक से जांचने से रोकता है कि आप कुछ भी गलत कर रहे हैं। कुछ गड़बड़ है, हालांकि संकलक इस विशिष्ट मामले में इसका पता नहीं लगा सकता है। प्रतिनिधि को एक फ़ंक्शन पॉइंटर के रूप में मार्शल किया जाता है जो __stdcall कॉलिंग सम्मेलन का उपयोग करता है, आपका वास्तविक फ़ंक्शन पॉइंटर __cdecl कॉलिंग कन्वेंशन का उपयोग करता है, मूल कोड के लिए डिफ़ॉल्ट। जो स्टैक को कॉल पर असंतुलित होने का कारण बनता है।

आप प्रतिनिधि घोषणा में [UnmanagedFunctionPointer] attribute लागू करके इसे ठीक कर सकते हैं, कॉलिंग कॉन्फ़्रेंस :: सीडीसीएल निर्दिष्ट करें।

+0

आपको बहुत धन्यवाद, ऐसा क्यों है कि तर्क के बिना यह आवश्यक नहीं है? – xcvd

+4

यह अभी भी जरूरी है, यह कोई फर्क नहीं पड़ता क्योंकि स्टैक पर कुछ भी नहीं धकेल रहा है। –

1

एक प्रतिनिधि से बनाया गया एक फ़ंक्शन पॉइंटर कचरा कलेक्टर के लिए अदृश्य है, और पहुंच क्षमता के दौरान गिना जाता है।

the documentation से:

You must manually keep the delegate from being collected by the garbage collector from managed code. The garbage collector does not track reference [sic] to unmanaged code.

प्रतिनिधि एकत्र किया जाता है, तो समारोह सूचक झूलने छोड़ दिया है, और अपने कार्यक्रम बुरी तरह से व्यवहार करेंगे। एक प्रवेश उल्लंघन अधिक संभावित परिणामों में से एक है, लेकिन एकमात्र संभावना नहीं है। यदि किसी अन्य डेटा के लिए देशी/प्रबंधित ट्रैम्पोलिन को शामिल करने वाली स्मृति का पुन: उपयोग किया जाता है, तो CPU इसे निर्देशों के रूप में समझने का प्रयास कर सकता है, जिसका अर्थ कुछ भी हो सकता है।

समाधान प्रतिनिधि को पहुंचने योग्य है, उदाहरण के लिए सी ++/सीएलआई gcroot कक्षा के माध्यम से, जो .NET GCHandle के आसपास एक पतली आवरण है।

+0

के लिए +1 मैं GCHandle का उपयोग कर समस्या को हल करने में सक्षम नहीं हूं। ऐसा लगता है कि प्रतिनिधि को जीसीएचंडल के साथ आवंटित किया जाता है, वही त्रुटि होती है, इसलिए मुझे नहीं लगता कि यह कचरा कलेक्टर के साथ एक मुद्दा है। – xcvd

+0

मुझे यह भी जोड़ना चाहिए कि जैसे ही मैं Unmanaged.cpp को/cli के साथ संकलित करता हूं, समस्या गायब हो जाती है – xcvd