2011-01-04 6 views
9

मैं कोड के साथ इंटरफ़ेस रहा हूँ कि लेता है एक char** (अर्थात, एक स्ट्रिंग के लिए सूचक है) मार्शलिंग:सी # में एक चार **

int DoSomething(Whatever* handle, char** error); 

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

हालांकि, मुझे यकीन नहीं है कि सी # में कैसे संभालना है। क्या मैं वर्तमान में है:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static unsafe extern int DoSomething(IntPtr handle, byte** error); 

public static unsafe int DoSomething(IntPtr handle, out string error) { 
    byte* buff; 

    int ret = DoSomething(handle, &buff); 

    if(buff != 0) { 
     // ??? 
    } else { 
     error = ""; 
    } 

    return ret; 
} 

मैं चारों ओर poked है, लेकिन मुझे लगता है कि सही रास्ते पर UTF8Encoding.UTF8.GetString()

एम आई को खिलाने के लिए चालू करने के लिए कैसे एक byte[] में, उपयुक्त को समझ नहीं सकता?

संपादित करें: अधिक स्पष्ट करने के लिए, पुस्तकालय समारोह स्मृति, जो बुला एक और पुस्तकालय समारोह द्वारा मुक्त किया जाना चाहिए आवंटित करता है। यदि कोई समाधान मुझे पॉइंटर के साथ नहीं छोड़ता है तो मैं मुक्त कर सकता हूं, समाधान अस्वीकार्य है।

बोनस प्रश्न: जैसा कि ऊपर बताया गया है, यह पुस्तकालय अपने तारों के लिए यूटीएफ -8 का उपयोग करता है। क्या मुझे अपने पी/इनवॉक्स में कुछ भी विशेष करने की ज़रूरत है, या सामान्य const char* पैरामीटर के लिए बस string का उपयोग करें?

+0

क्या आपने स्ट्रिंगबिल्डर 'को बाहर करने की कोशिश की है? – leppie

+0

मैंने रूट समस्या को स्पष्ट करने के लिए एक अतिरिक्त संपादन जोड़ा है। –

उत्तर

2

आपको केवल ref string का उपयोग करने में सक्षम होना चाहिए और रनटाइम डिफ़ॉल्ट मार्शलर आपके लिए इस रूपांतरण का ख्याल रख सकता है। यह सुनिश्चित करने के लिए कि आप 8-बिट वर्णों का उपयोग कर रहे हैं, आप [MarshalAs(UnmanagedType.LPStr)] के साथ पैरामीटर पर चार चौड़ाई को संकेत दे सकते हैं।

चूंकि आपके पास कॉल करने के लिए एक विशेष डीलोकेशन विधि है, इसलिए आपको पॉइंटर रखना होगा, जैसा कि आपने पहले ही अपने प्रश्न के उदाहरण में दिखाया है।

यहां बताया गया है मैं लिखते हैं:

var errorMsg = Marshal.PtrToStringAnsi(new IntPtr(*error)); 

और सफाई:

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static unsafe extern int DoSomething(
    MySafeHandle handle, void** error); // byte** should work, too, I'm just lazy 

तो फिर तुम एक स्ट्रिंग प्राप्त कर सकते हैं

[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)] 
private static extern int FreeMyMemory(IntPtr h); 

// ... 

FreeMyMemory(new IntPtr(error)); 

और अब हम मार्शल त्रुटि है , तो बस इसे वापस करें।

return errorMsg; 

इसके अलावा MySafeHandle प्रकार है, जो System.Runtime.InteropServices.SafeHandle से विरासत हैं ध्यान दें। जबकि कड़ाई से जरूरी नहीं है (आप इंटप्राट का उपयोग कर सकते हैं), यह देशी कोड के साथ इंटरऑप करते समय आपको एक बेहतर हैंडल प्रबंधन देता है। इसके बारे में यहां पढ़ें: http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.safehandle.aspx

+0

मैं मेमोरी को कैसे संभाल सकता हूं? दोहराने के लिए, 'DoSomething' स्मृति आवंटित करेगा और इसे '* त्रुटि' में संग्रहीत करेगा। मुझे उस स्मृति को मुक्त करने की ज़रूरत है ... –

+0

आपको पता होना चाहिए कि इसे कैसे आवंटित किया जाता है: वैश्विक ढेर या स्थानीय? – codekaizen

+0

यह ढेर है, लेकिन लाइब्रेरी द्वारा आवंटित। मुझे इसे मुक्त करने के लिए 'mydll.dll' में' FreeMyMemory' को कॉल करने की आवश्यकता है। ऐसा करने के लिए मुझे 'रेफ स्ट्रिंग' से पता नहीं मिल सकता है। –

0

संदर्भ के लिए, यहां कोड है जो संकलित करता है ( लेकिन अभी तक परीक्षण नहीं किया गया है, अगले परीक्षण पर काम करता है, 100% काम करता है) जो मुझे चाहिए। डी

public static unsafe int DoSomething(IntPtr handle, out string error) { 
    byte* buff; 

    int ret = DoSomething(handle, &buff); 

    if(buff != null) { 
     int i = 0; 

     //count the number of bytes in the error message 
     while (buff[++i] != 0) ; 

     //allocate a managed array to store the data 
     byte[] tmp = new byte[i]; 

     //(Marshal only works with IntPtrs) 
     IntPtr errPtr = new IntPtr(buff); 

     //copy the unmanaged array over 
     Marshal.Copy(buff, tmp, 0, i); 

     //get the string from the managed array 
     error = UTF8Encoding.UTF8.GetString(buff); 

     //free the unmanaged array 
     //omitted, since it's not important 

     //take a shot of whiskey 
    } else { 
     error = ""; 
    } 

    return ret; 
} 

संपादित करें:: किसी को भी बेहतर कर सकते हैं, उसके बाद मैं क्या कर रहा हूँ है while पाश में तर्क तय है, यह एक त्रुटि के एक बंद था।