2012-04-25 20 views
18

के साथ स्ट्रक्चर देता है, इसलिए, इस प्रश्न के कई रूप हैं, और कई को देखने के बाद भी मैं इसे समझ नहीं पाया।सी # कॉलिंग सी फ़ंक्शन जो फिक्स्ड साइज चार सरणी

typedef struct 
{ 
unsigned long Identifier; 
char Name[128]; 
} Frame; 

Frame GetFrame(int index); 

यह सी # कोड है::

इस सी कोड है

struct Frame 
{ 
    public ulong Identifier; 
    [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 128)] 
    public char[] Name; 
} 

[DllImport("XNETDB.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
private static extern Frame GetFrame(int index); 

यह अंतिम प्रयास मैं सी # में करने की कोशिश की है, और यह बहुत तार्किक लगता है, लेकिन मैं मिलता है त्रुटि "विधि का हस्ताक्षर PInvoke संगत नहीं है।" तो, मैं आगे की कोशिश करने के लिए क्या खो गया हूँ। किसी भी मदद की सराहना की है।

धन्यवाद, केविन

अपडेट किया गयाकेविन मेरे जवाब देने के लिए एक संपादन के रूप में इस जोड़ा

मैं बजाय मेरी सी कोड बदलना चाहिए:

void GetFrame(int index, Frame * f); 

और सी # के लिए बजाय का उपयोग :

struct Frame 
{ 
    public uint Identifier; 
    [MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 128)] 
    public string Name; 
} 

[DllImport("XNETDB.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
private static extern void GetFrame(int index, ref Frame f); 
+1

आप इस देखा http://social.msdn.microsoft.com/Forums/en-AU/csharplanguage/thread/6e0ad208-5c8b-48ac-a45e-cfaf7f52221b? – MilkyWayJoe

+0

फ़ंक्शन परिभाषा मौजूद है। सूचीबद्ध सी कोड केवल हेडर फ़ाइल से है। –

+0

मैंने देखा और निजी स्थैतिक बाहरी IntPtr GetFrame (int अनुक्रमणिका) की कोशिश की; लेकिन कॉलिंग ने त्रुटि को फेंक दिया "संरक्षित स्मृति को पढ़ने या लिखने का प्रयास किया।" –

उत्तर

8

समस्या देशी समारोह की वापसी मान के रूप में एक गैर blittable प्रकार देता है कि है।

http://msdn.microsoft.com/en-us/library/ef4c3t39.aspx

पी/आह्वान एक वापसी मान के रूप में गैर blittable प्रकार नहीं हो सकता।

आप नहीं पी/कि विधि आह्वान कर सकते हैं।

मूल्य से 132 बाइट्स रिटर्निंग [संपादितयह वास्तव में संभव है, JaredPar's answer देखें] एक बुरा विचार है। यदि यह मूल कोड आपका है, तो मैं इसे ठीक कर दूंगा। आप 132 बाइट आवंटित करके और पॉइंटर लौटकर इसे ठीक कर सकते हैं। फिर उस स्मृति को रिलीज़ करने के लिए एक फ्रीफ्रेम विधि जोड़ें। अब यह पी/आमंत्रित किया जा सकता है।

वैकल्पिक रूप से, आप फ्रेम स्मृति के लिए सूचक स्वीकार करने के लिए है कि यह भर देंगे करने के लिए JaredPar के सी # तय आकार बफर सुविधा का उपयोग करने के लिए है यह बदल सकता है।

+0

+1, पूरी तरह से वापसी प्रकार के बारे में भूल गया blittable नियम होना चाहिए। सहमत हैं कि यदि आपके पास नियंत्रण है तो एक आसान परत प्रदान करना सबसे अच्छा है। यदि आप नहीं करते हैं तो मैंने एक व्यावहारिक (लेकिन बदसूरत) समाधान जोड़ा है। – JaredPar

+0

@ kevin.key मैं जेरेडपार के जवाब को अब जवाब के रूप में चिह्नित करूंगा क्योंकि यह मूल कोड नहीं बदल सकता है, लेकिन यह सीधे काम करता है। – Tergiver

+0

मैंने इसे उत्तर के रूप में चिह्नित किया था। कुंजी सी # में रेफरी द्वारा संरचना को पार कर रही थी, और एक सूचक पैरामीटर के माध्यम से संरचना को पारित करने के लिए सी फ़ंक्शन को बदल रहा था। –

20

आपके द्वारा चुने गए PInvoke हस्ताक्षर के साथ दो समस्याएं हैं।

पहला तय करना आसान है। आपके पास unsigned long का गलत अनुवाद है। सी unsigned long आमतौर पर केवल 4 बाइट्स है। आपने सी # प्रकार long चुना है जो 8 बाइट्स है। uint का उपयोग करने के लिए सी # कोड बदलना इसे ठीक करेगा।

दूसरा थोड़ा कठिन है। जैसा कि टर्गीवर ने सीएलआर मार्शलर को इंगित किया है कि यह वापसी की स्थिति में केवल एक संरचना का समर्थन करता है यदि यह विचित्र है। ब्लिटेबल कहने का एक शानदार तरीका है कि इसका मूल और प्रबंधित कोड में सटीक स्मृति प्रतिनिधित्व है। आपके द्वारा चुने गए स्ट्रक्चर परिभाषा को ब्लिटेबल नहीं है क्योंकि इसमें घोंसला वाला सरणी है।

यह याद किया जा सकता है कि यदि आपको याद है कि PInvoke एक बहुत ही सरल प्रक्रिया है। CLR Marshaller वास्तव में सिर्फ अपने प्रकार के हस्ताक्षर और PInvoke तरीकों

  • मैं कितने बाइट्स को कॉपी कर रहा हूँ के साथ 2 प्रश्नों के उत्तर देने की जरूरत है?
  • किस दिशा में उन्हें जाने की आवश्यकता है?

इस मामले में बाइट की संख्या sizeof(unsigned long) + 128 == 132 है। तो हमें बस इतना करना है कि एक प्रबंधित प्रकार का निर्माण करें जो कि विचित्र है और इसका आकार 132 बाइट है। यह करने के लिए सबसे आसान तरीका है सरणी भाग

[StructLayout(LayoutKind.Sequential, Size = 128)] 
struct Blob 
{ 
    // Intentionally left empty. It's just a blob 
} 

यह कोई सदस्यों को बताया कि 128 बाइट्स का एक आकार होने के रूप में marshaller के लिए दिखाई देगा के साथ एक struct है संभालने के लिए एक ब्लॉब परिभाषित करने के लिए है (और एक बोनस के रूप में यह blittable है !)। अब हम आसानी से एक uint का एक संयोजन और इस प्रकार

struct Frame 
{ 
    public int Identifier; 
    public Blob NameBlob; 
    ... 
} 

अब हम एक आकार marshaller 132 बाइट के रूप में देखेंगे के साथ एक blittable प्रकार है के रूप में Frame संरचना परिभाषित कर सकते हैं।इसका मतलब है कि यह GetFrame हस्ताक्षर के साथ ठीक काम करेगा जो आपने

एकमात्र हिस्सा छोड़ा है, जो आपको नाम के लिए वास्तविक char[] तक पहुंच प्रदान कर रहा है। यह थोड़ा मुश्किल है लेकिन थोड़ा सा मार्शल जादू के साथ हल किया जा सकता है।

public string GetName() 
{ 
    IntPtr ptr = IntPtr.Zero; 
    try 
    { 
     ptr = Marshal.AllocHGlobal(128); 
     Marshal.StructureToPtr(NameBlob, ptr, false); 
     return Marshal.PtrToStringAnsi(ptr, 128); 
    } 
    finally 
    { 
     if (ptr != IntPtr.Zero) 
     { 
      Marshal.FreeHGlobal(ptr); 
     } 
    } 
} 

नोट: क्योंकि मैं GetFrame एपीआई के साथ अपरिचित हूँ मैं बुला सम्मेलन हिस्से पर टिप्पणी नहीं कर सकता, लेकिन जो कुछ मैं निश्चित रूप से पर जांच करेंगे।

+0

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

+1

@BenVoigt इससे बड़ा अंतर हो सकता है। मूल हस्ताक्षर में मार्शलर मान लेगा कि 'char []' स्ट्रेट बनाम 4 की वास्तविक मूल्य बनाम ऑफसेट 8 से शुरू होता है। परिणामस्वरूप मार्शलर 4 बाइट्स देखता है जो वास्तव में वहां नहीं हैं। यह मूल रूप से मार्शलिंग करते समय इन 4 बाइटों को लिख देगा और देशी से वापस मार्शल करते समय इन 4 बाइट्स को पढ़ेगा। 4 बाइट अनिवार्य रूप से कचरा हैं और इसलिए अपरिभाषित व्यवहार – JaredPar

+0

का कारण बनता है बेशक यह शुद्धता को प्रभावित करता है। लेकिन यह एक संकलन त्रुटि का कारण नहीं होगा। 'Ulong' और' uint' दोनों blittable हैं, पी/invoke उन्हें एक ही फैशन में हैंडल (हालांकि विभिन्न हद तक)। –

3

एक अन्य विकल्प। हालांकि यह आपको असुरक्षित कोड की अनुमति देने के लिए सेटिंग चालू करने की आवश्यकता है, लेकिन यह 2 structs होने से बचाता है।

class Program 
{ 
    private const int SIZE = 128; 

    unsafe public struct Frame 
    { 
     public uint Identifier; 
     public fixed byte Name[SIZE]; 
    } 

    [DllImport("PinvokeTest2.DLL", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] 
    private static extern Frame GetFrame(int index); 

    static unsafe string GetNameFromFrame(Frame frame) 
    { 
     //Option 1: Use if the string in the buffer is always null terminated 
     //return Marshal.PtrToStringAnsi(new IntPtr(frame.Name)); 

     //Option 2: Use if the string might not be null terminated for any reason, 
     //like if were 128 non-null characters, or the buffer has corrupt data. 

     return Marshal.PtrToStringAnsi(new IntPtr(frame.Name), SIZE).Split('\0')[0]; 
    } 

    static void Main() 
    { 
     Frame a = GetFrame(0); 
     Console.WriteLine(GetNameFromFrame(a)); 
    } 
} 
+1

अच्छा है! आप 128 आकार के साथ 'PtrToStringAnsi' अधिभार का उपयोग कर सकते हैं ताकि कोई अंतिम अक्षर समाप्त न हो तो यह अधिक नहीं होगा। – Tergiver

+0

यदि कोई संभावना है कि कोई शून्य चरित्र नहीं होगा, तो आपको दूसरे विकल्प का उपयोग करना चाहिए, जो उस अधिभार का उपयोग करता है। दुर्भाग्यवश AFAICT कि अधिभार हमेशा पहले नल पर रोकने के बजाय 128 वर्णों की एक स्ट्रिंग बनाएगा। यही कारण है कि मैंने शून्य चरित्र पर विभाजन जोड़ा, और पहले परिणाम का चयन किया। –

+0

आप सही हैं, जिसके परिणामस्वरूप स्ट्रिंग लंबाई आपके द्वारा पारित आकार होगी। – Tergiver

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