2011-06-09 17 views
17

मैं सी # से निम्नलिखित तुच्छ सी फ़ंक्शन को कॉल करने का प्रयास कर रहा हूँ:रिलीज बिल्ड में मैं सी ++ से सी # तक एक char * स्ट्रिंग क्यों नहीं लौटा सकता?

SIMPLEDLL_API const char* ReturnString() 
{ 
    return "Returning a static string!"; 
} 
निम्नलिखित पी के साथ

/घोषणा आह्वान (के साथ या वापसी विशेषता बिना, यह कोई फर्क नहीं पड़ता):

[DllImport("SimpleDll")] 
[return: MarshalAs(UnmanagedType.LPStr)] 
public static extern string ReturnString(); 

यह काम करता है अगर डीएलएल एक डीबग बिल्ड है लेकिन रिलीज बिल्ड (AccessViolationException) में दुर्घटनाग्रस्त हो जाता है।

मैं एक दर्जन से अधिक अन्य साधारण कार्यों पर बोल रहा हूँ और यह केवल एक ही है कि विफल रहता है (यहाँ है दूसरों :)

[DllImport("SimpleDll")] public static extern int NextInt(); 
[DllImport("SimpleDll")] public static extern void SetNextInt(int x); 
[DllImport("SimpleDll")] public static extern int AddInts(int a, int b); 
[DllImport("SimpleDll")] public static extern int AddFourInts(int a, int b, int c, int d); 
[DllImport("SimpleDll")] public static extern double AddDoubles(double x, double y); 
[DllImport("SimpleDll")] public static extern IntPtr AddDoublesIndirect(ref double x, ref double y); 
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.U1)] 
public static extern char CharStringArgument([MarshalAs(UnmanagedType.LPStr)]string s); 
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.U2)] 
public static extern char WCharStringArgument([MarshalAs(UnmanagedType.LPWStr)]string s); 
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.LPWStr)] 
public static extern string ReturnWString(); 
[DllImport("SimpleDll")] [return: MarshalAs(UnmanagedType.BStr)] 
public static extern string ReturnBSTR(); 
[DllImport("SimpleDll")] public static extern System.Drawing.Point MakePoint(int x, int y); 
[DllImport("SimpleDll")] public static extern IntPtr MakePointIndirect(int x, int y); 
[DllImport("SimpleDll")] public static extern int GetPointY(System.Drawing.Point p); 
[DllImport("SimpleDll")] public static extern int GetPointYIndirect(ref System.Drawing.Point pp); 
[DllImport("SimpleDll")] public static extern int SumIntegers(ref int firstElem, int size); 
+0

कैसे आप सी द्वारा आबंटित स्मृति पुनःआवंटन लिए .net कोड उम्मीद कर रहे हैं ++ रनटाइम ? –

+2

यह http://stackoverflow.com/questions/5308584/how-to-return-text-from-native-c-code –

+0

इस मामले में C++ रनटाइम द्वारा आवंटित नहीं किया गया है। – Qwertie

उत्तर

21

या हो सकता है

[DllImport("SimpleDll")] 
public static extern IntPtr ReturnString(); 

और अपने बुला कोड में इस्तेमाल करने की कोशिश, मार्शल कक्षा का उपयोग

string ret = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(PInvoke.ReturnString()); 
+0

समस्या हल हो गई, धन्यवाद! मैं .NET Framework में कुछ अजीब बग को इस बिंदु का अनुमान लगा रहा हूं। – Qwertie

+10

@ क्वर्टी, यह .NET ढांचे में एक बग नहीं है। 'स्ट्रिंग' मार्शलिंग, डिफ़ॉल्ट रूप से, 'स्ट्रिंग' की एक प्रबंधित प्रतिलिपि बनाएगी और 'char *' द्वारा उपयोग की जाने वाली 'सी' मेमोरी को डी-आवंटित करने का प्रयास करेगी। 'IntPtr' का उपयोग स्वचालित रूप से किसी ऑब्जेक्ट की एक प्रबंधित प्रति नहीं बनाएगा और इसलिए स्वचालित रूप से स्मृति को डी-आवंटित करने का प्रयास नहीं करेगा।जैसा कि मुझे यकीन है कि आप सहमत होंगे, एक स्ट्रिंग शाब्दिक से स्मृति आवंटित करने का प्रयास करना एक अच्छा विचार नहीं है :) रुचि के मामले में, आप अधिक जानकारी के लिए इस पोस्ट को देखना चाहेंगे: http: // stackoverflow। com/a/370519/1244630 – Pooven

+3

@Pooven, आपकी टिप्पणी एक उत्तर होना चाहिए:) ... 'CoTaskMemFree' को कॉल करना मेरे लिए बहुत अजीब डिफ़ॉल्ट व्यवहार प्रतीत होता है, क्योंकि मैंने विंडोज़ पर अपने पूरे करियर पर प्रोग्राम किया है और इसे कभी नहीं कहा है। CoTaskMemFree को COM API के रूप में प्रलेखित किया गया है, लेकिन अगर मैं COM को तारों को पार करना चाहता था, तो मैं SysAllocString और SysFreeString और BSTR के रूप में मार्शल का उपयोग करता हूं। – Qwertie

0

मैं एक ऐसी ही समस्या का सामना किया और निर्दिष्ट करने "CharSet" ठीक करने के लिए लग रहा था कर रहे हैं यह।

[DllImport("SimpleDll", CharSet = CharSet.Ansi)] 

यह सुनिश्चित नहीं है कि यह डीबग और रिलीज़ में क्यों अलग होगा।

+0

मैंने आपके सुझाव की कोशिश की लेकिन यह काम नहीं किया। सबसे अधिक संभावना है कि मार्शल एट्रिब्यूट का एक ही प्रभाव है। – Qwertie

2

मेरे पी/Invoke अनुभव में, आपके पास आमतौर पर 2 पैरामीटर होते हैं: 1. पूर्व-आवंटित बफर, और 2, बफर की लंबाई। रिटर्न मान वापस लौटाए गए डेटा की लंबाई है (मूल लंबाई से अधिक नहीं)।

आमतौर पर डीईबीयूजी रिलीज स्मृति के चारों ओर स्मृति को स्थानांतरित नहीं करेगा।

बीटीडब्ल्यू, आप प्री-आवंटित स्ट्रिंगबिल्डर में पास कर सकते हैं, फिर एसबी.लेन्ग = सी फ़ंक्शन का रिटर्न वैल्यू सेट कर सकते हैं, तो आपके स्ट्रिंग के अंत में आपके पास \ n nulls का गुच्छा नहीं होगा।

3

रिलीज मोड में सी ++ संकलक डेटा पेज है, जो में निरंतर रहा है संरक्षित; सी # को इसे बंद करने का प्रयास समस्या पैदा कर रहा है। डीबग मोड में, कंपाइलर डेटा पेज में स्थिरता को अनुकूलित नहीं कर रहा है, और इसलिए कोई सुरक्षा समस्या नहीं है।

+3

डेटा पेज "संरक्षित" कैसे है? निश्चित रूप से यह पढ़ने से सुरक्षित नहीं है, और .NET मार्शलर को स्ट्रिंग पर लिखना नहीं चाहिए, है ना? – Qwertie

5

यह भी है कि क्या करने के लिए एक कस्टम Marshaler साथ संभव है:

class ConstCharPtrMarshaler : ICustomMarshaler 
{ 
    public object MarshalNativeToManaged(IntPtr pNativeData) 
    { 
     return Marshal.PtrToStringAnsi(pNativeData); 
    } 

    public IntPtr MarshalManagedToNative(object ManagedObj) 
    { 
     return IntPtr.Zero; 
    } 

    public void CleanUpNativeData(IntPtr pNativeData) 
    { 
    } 

    public void CleanUpManagedData(object ManagedObj) 
    { 
    } 

    public int GetNativeDataSize() 
    { 
     return IntPtr.Size; 
    } 

    static readonly ConstCharPtrMarshaler instance = new ConstCharPtrMarshaler(); 

    public static ICustomMarshaler GetInstance(string cookie) 
    { 
     return instance; 
    } 
} 

और इस तरह इसका इस्तेमाल:

[DllImport("SimpleDll")] 
[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ConstCharPtrMarshaler))] 
public static extern string ReturnString(); 
+0

यह जाने का रास्ता है :) – Aybe

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