दिलचस्प पोस्ट
एएसपी.नेट में कैश समाप्ति के लिए सबसे अच्छा तरीका क्या है? PHP संकलित या व्याख्या की गई है? स्विफ्ट में एक समयबद्ध ईवेंट को रद्द करें? कोणीय – एकाधिक HTTP कॉल के डेटा तक पहुंच – कैसे वादे को हल करने के लिए Scala में एक मुहर विशेषता पर परिवर्तन? किसी वस्तु को जावा में एक बाइट सरणी में परिवर्तित करना XML फ़ाइलों को कैसे पढ़ा और लिखना है? विभिन्न डेटाबेस में कॉलम का चयन करें "Git branch -r" चलते समय "मूल / सिर" क्यों दिखाया गया है? एंड्रॉइड में सिंगलटन विंडोज़ फार्म में सी # ऊर्ध्वाधर लेबल jQuery के वैध प्लगइन – एक सरल कस्टम नियम बनाने के लिए कैसे? Google मानचित्र API संस्करण अंतर WCF और ASMX वेब सेवाओं के बीच क्या अंतर है? अमेज़ॅन मेगावाट्स – अनुरोध हस्ताक्षर की गणना प्रदान की गई हस्ताक्षर से मेल नहीं खाती

Cdecl कॉल अक्सर "मानक" पी / आग्रह सम्मेलन में क्यों बेमेल है?

मैं एक बहुत बड़ी कोडबेज़ पर काम कर रहा हूं जिसमें C ++ कार्यक्षमता C # से P / Invoked है I

हमारे कोडबेस में कई कॉल हैं जैसे कि …

सी ++:

extern "C" int __stdcall InvokedFunction(int); 

संबंधित सी # के साथ:

 [DllImport("CPlusPlus.dll", ExactSpelling = true, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] private static extern int InvokedFunction(IntPtr intArg); 

मैंने इस तर्क के लिए शुद्ध (अंतराल के रूप में मैं सक्षम हूं) scoured किया है कि यह स्पष्ट बेमेल क्यों मौजूद है। उदाहरण के लिए, सी # के अंदर सीडीईएल क्यों है, और सी ++ के भीतर __stdcall क्यों है? जाहिर है, यह स्टैक में परिणाम दो बार मंजूरी दे दी है, लेकिन, दोनों ही मामलों में, चर उसी रिवर्स ऑर्डर में स्टैक पर धकेल दिया जाता है, जैसे कि मुझे कोई त्रुटि नहीं दिखाई देती है, यद्यपि संभावना है कि वापसी की जानकारी को इस स्थिति में हटा दिया गया है डीबगिंग के दौरान ट्रेस करने का प्रयास?

एमएसडीएन से: http://msdn.microsoft.com/en-us/library/2x8kf7zx%28v=vs.100%29.aspx

 // explicit DLLImport needed here to use P/Invoke marshalling [DllImport("msvcrt.dll", EntryPoint = "printf", CallingConvention = CallingConvention::Cdecl, CharSet = CharSet::Ansi)] // Implicit DLLImport specifying calling convention extern "C" int __stdcall MessageBeep(int); 

एक बार फिर, सी ++ कोड में दोनों extern "C" और CallingConvention.CdeclCallingConvention.Cdecl में सी # है। यह CallingConvention.Stdcall क्यों नहीं है। या, इसके अलावा, सी ++ में __stdcall क्यों है?

अग्रिम में धन्यवाद!

वेब के समाधान से एकत्रित समाधान "Cdecl कॉल अक्सर "मानक" पी / आग्रह सम्मेलन में क्यों बेमेल है?"

यह SO प्रश्नों में बार-बार आता है, मैं इसे (लंबे) संदर्भ उत्तर में बदलने की कोशिश करता हूँ। 32-बिट कोड असंगत कॉलिंग सम्मेलनों के एक लंबा इतिहास के साथ saddled है। फ़ंक्शन कॉल कैसे करें, जो कि लंबे समय से पहले समझ में आया, लेकिन आज के पीछे के अंत में ज्यादातर एक विशाल दर्द है। 64-बिट कोड में केवल एक कॉलिंग सम्मेलन है, जिसे किसी अन्य को जोड़ने वाला है दक्षिण अटलांटिक के छोटे द्वीप में भेजा जा रहा है।

मैं उन विकिपीडिया लेखों में से परे उन इतिहास और प्रासंगिकता की व्याख्या करने का प्रयास करूंगा शुरुआती बिंदु यह है कि फ़ंक्शन कॉल करने के तरीके में किए जाने वाले विकल्प यह हैं कि किस क्रम में तर्कों को पारित किया जाए, कहां जमा करें और कॉल के बाद सफाई कैसे करें।

  • __stdcall को विंडोज़ प्रोग्रामिंग में पुराने 16-बिट पास्कल कॉलिंग सम्मेलन के माध्यम से अपना रास्ता मिल गया, जिसका इस्तेमाल 16-बिट विंडोज़ और ओएस / 2 में किया गया था। यह सभी विंडोज़ एपीआई कार्यों के साथ-साथ कॉम के द्वारा उपयोग किया जाने वाला सम्मेलन है। चूंकि अधिकांश पिनवोक को ओएस कॉल करने का इरादा था, इसलिए यदि आप इसे स्पष्ट रूप से [DllImport] विशेषता में निर्दिष्ट नहीं करते हैं, तो एसडीसीॉल डिफ़ॉल्ट है। इसका एक और अस्तित्व का एकमात्र कारण है कि यह निर्दिष्ट करता है कि बछेड़ा साफ हो जाता है। जो अधिक कॉम्पैक्ट कोड का उत्पादन करता है, दिन में बहुत महत्वपूर्ण है जब उन्हें 640 किलोबाइट रैम में जीयूआई ऑपरेटिंग सिस्टम निचोड़ना पड़ा। इसकी सबसे बड़ी हानि यह है कि यह खतरनाक है । कॉलर की धारणा के बीच एक बेमेल एक फ़ंक्शन के लिए तर्क हैं और कैली को लागू करने के कारण स्टैक असंतुलित हो जाता है। जो बदले में दुर्घटनाओं का निदान करने के लिए बेहद कठिन हो सकता है

  • __cdecl सी भाषा में लिखा कोड के लिए मानक कॉलिंग सम्मेलन है। अस्तित्व का मुख्य कारण यह है कि यह तर्कों की एक चर संख्या के साथ फ़ंक्शन कॉल करने में सहायता करता है। Printf () और scanf () जैसी फ़ंक्शन के साथ सी कोड में सामान्य साइड इफेक्ट के साथ क्योंकि यह कॉलर है जो जानता है कि कितने तर्क वास्तव में पारित किए गए थे, यह कॉलर है जो साफ करता है CallingConvention = कॉलिंग कॉन्फ़्रेंस को भूल जाना। CDEcl [DllImport] घोषणा में एक बहुत ही सामान्य बग है।

  • __fastcall पारस्परिक रूप से असंगत विकल्पों के साथ काफी खराब परिभाषित कॉलिंग सम्मेलन है। बॉरलैंड कम्पाइलर में एक कंपनी थी, जो एक बार कंपाइलर टेक्नोलॉजी में बहुत प्रभावशाली थी जब तक कि वे विघटित नहीं हुईं। इसके अलावा कई माइक्रोसॉफ्ट कर्मचारियों के पूर्व नियोक्ता, जिसमें सी # फेम के एंडर्स हेजल्स्बर्ग शामिल हैं इसका पता लगाया गया था कि उनमें से कुछ को स्टैक के स्थान पर सीपीयू रजिस्टर्स के पास से गुजरने के कारण तर्क को सस्ता करना पड़ता है। यह खराब मानकीकरण के कारण प्रबंधित कोड में समर्थित नहीं है।

  • __thiscall C ++ कोड के लिए आविष्कार कॉलिंग कन्वेंशन है। बहुत __सीडीएसीएल के समान है लेकिन यह भी निर्दिष्ट करता है कि कक्षा के उदाहरण के तरीकों को क्लास ऑब्जेक्ट के लिए छुपी हुई इस पॉइंटर को कैसे पार किया जाता है सी से परे सी ++ में एक अतिरिक्त विवरण। जब यह लागू करने के लिए सरल लग रहा है, तो .NET pinvoke marshaller इसका समर्थन नहीं करता है। एक प्रमुख कारण है कि आप सी + + कोड पिन नहीं कर सकते जटिलता कॉलिंग सम्मेलन नहीं है, यह इस सूचक का उचित मूल्य है बहु उत्तराधिकार के लिए C ++ के समर्थन के कारण बहुत जटिल हो सकता है केवल एक सी + + कंपाइलर यह समझ सकता है कि वास्तव में पारित होने की आवश्यकता क्या है। और केवल वही सी ++ कंपाइलर जिसने सी ++ क्लास के लिए कोड तैयार किया, अलग-अलग कम्पाइलर ने एमआई के कार्यान्वयन के तरीके और इसे अनुकूलित करने के तरीके पर अलग-अलग विकल्प बनाये हैं।

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

आप extern "C" उल्लेख करते हैं, यह समझना महत्वपूर्ण है कि इंटरॉप को बचाना भी महत्वपूर्ण है। भाषा कंपाइलर अक्सर अतिरिक्त वर्णों के साथ निर्यात किए गए फ़ंक्शन के नामों को सजाने के लिए करते हैं। इसके अलावा "नाम मैंगलिंग" भी कहा जाता है यह एक बहुत ही गंदी चाल है जो मुसीबत का कारण कभी नहीं रुकती है और आपको इसे [DllImport] विशेषता के CharSet, EntryPoint और ExactSpelling गुणों के उचित मूल्यों को निर्धारित करने के लिए इसे समझने की आवश्यकता है। कई सम्मेलनों हैं:

  • विंडोज़ एपीआई सजावट विंडोज मूल रूप से गैर-यूनिकोड ऑपरेटिंग सिस्टम था, स्ट्रिंग्स के लिए 8-बिट एन्कोडिंग का उपयोग कर रहा था। विंडोज एनटी पहला था जो अपने मूल में यूनिकोड बन गया। इसके बजाय एक बड़ी संगतता समस्या का कारण बनता है, पुराने कोड नए ऑपरेटिंग सिस्टम पर चलने में सक्षम नहीं होता क्योंकि यह 8-बिट एन्कोडेड स्ट्रिंग को WinAPi फ़ंक्शंस को पास करेगा जो कि यूटीएफ -16 एनकोडेड यूनिकोड स्ट्रिंग की अपेक्षा करते हैं। उन्होनें इसे हर WinAPi फ़ंक्शन के दो संस्करण लिखकर हल किया। एक है जो 8-बिट स्ट्रिंग लेता है, दूसरा जो यूनिकोड स्ट्रिंग लेता है। और नए संस्करण (डब्लू = चौड़े) के अंत में विरासत संस्करण (ए = एनसीआई) और एक डब्ल्यू के नाम के अंत में अक्षर ए को दबाने के बीच दोनों के बीच प्रतिष्ठित। कुछ भी नहीं जोड़ा जाता है यदि फ़ंक्शन स्ट्रिंग नहीं लेता है। पिनवोक मार्शलर आपकी मदद के बिना यह स्वचालित रूप से संभालता है, यह केवल सभी 3 संभावित संस्करणों को खोजने का प्रयास करेगा हालांकि आपको हमेशा CharSet.Auto (या यूनिकोड) निर्दिष्ट करना चाहिए, लीगेसी फ़ंक्शन के ऊपरी हिस्से को एन्डी से यूनिकोड तक स्ट्रिंग का अनुवाद करना अनावश्यक और हानिपूर्ण है।

  • __stdcall फ़ंक्शन के लिए मानक सजावट _foo @ 4 है अग्रणी अंडरस्कोर और @ n पोस्टफ़िक्स जो तर्कों के संयुक्त आकार को इंगित करता है। यह पोस्टफ़िक्स खराब स्टैक असंतुलन समस्या को हल करने में मदद करने के लिए डिज़ाइन किया गया था, अगर कॉलर और कैली तर्कों की संख्या के बारे में सहमत नहीं हैं। अच्छी तरह से काम करता है, हालांकि त्रुटि संदेश अच्छा नहीं है, पिनवोक मार्शलर आपको बताएगा कि यह प्रविष्टि का पता नहीं लगा सकता। उल्लेखनीय है कि __stdcall का उपयोग करते समय, Windows, इस सजावट का उपयोग नहीं करता है। यह जानबूझकर था, प्रोग्रामर्स को GetProcAddress () तर्क सही होने पर एक शॉट देना पिनवोक मार्शलर भी इसे स्वयं का ध्यान रखता है, पहले @n पोस्टफ़िक्स के साथ प्रविष्टि बिंदु को ढूंढने की कोशिश कर रहा है, और बिना किसी एक को कोशिश कर रहा है।

  • __cdecl फ़ंक्शन के लिए मानक सजावट _ फ़ू है I एक एकल अग्रणी अंडरस्कोर पिनवोक मार्शलर यह स्वचालित रूप से बाहर की तरह होता है अफसोस की बात है, __stdcall के लिए वैकल्पिक @ ए पोस्टफिक्स आपको यह बताने की अनुमति नहीं देता है कि आपकी कॉलिंग सम्मलेन सम्पत्ति गलत है, बड़ा नुकसान है।

  • सी ++ कंपाइलर्स नाम मैंगलिंग का उपयोग करते हैं, "वास्तव में विचित्र दिखने वाले नामों का निर्माण करते हैं जैसे"? 2 @ यापैक्सी @ जेड "," ऑपरेटर नया "के लिए निर्यात किया गया नाम। फ़ंक्शन ओवरलोडिंग के लिए अपने समर्थन के कारण यह एक आवश्यक बुराई थी। और यह मूल रूप से एक प्रीप्रोसेसर के रूप में डिजाइन किया गया था जिसने प्रोग्राम बनाया जाने के लिए लीगेसी सी भाषा टूलिंग का इस्तेमाल किया था। void foo(int) उन्हें अलग-अलग नाम देकर, एक void foo(char) और एक void foo(int) अधिभार के बीच अंतर करने के लिए आवश्यक बना दिया। यह वह जगह है जहां extern "C" वाक्यविन्यास खेलने में आता है, यह समारोह नाम करने के लिए नाम mangling लागू नहीं करने के लिए C ++ संकलक बताता है। अधिकांश प्रोग्रामर जो इंटरॉप कोड लिखते हैं, इसे जानबूझकर दूसरी भाषा में घोषणा लिखने में आसान बनाने के लिए इसका इस्तेमाल करते हैं। कौन सा वास्तव में एक गलती है, बेमेल को पकड़ने के लिए सजावट बहुत उपयोगी है आप लिंकर की। मैप फ़ाइल या डंपबिन। एक्सई / एक्सपोर्ट उपयोगिता को सजाए गए नामों को देखने के लिए इस्तेमाल करेंगे। Undname.exe एसडीके उपयोगिता एक गड़बड़ नाम को अपने मूल C ++ घोषणा में परिवर्तित करने के लिए बहुत आसान है।

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

मैं अब थोड़ी देर के लिए मेरी लिफ्टिंग की छाँची हूँ। आपके प्रश्न का शीर्षक स्पष्ट होना चाहिए, Stdcall डिफ़ॉल्ट है, लेकिन सी या सी ++ में लिखे गए कोड के लिए एक बेमेल है। और आपका [DllImport] घोषणा संगत नहीं है। इसे डिबगर में PInvokeStackImbalance प्रबंधित डीबगर सहायक से एक चेतावनी का उत्पादन करना चाहिए, एक डिबगर एक्सटेंशन जो खराब घोषणाओं का पता लगाने के लिए डिज़ाइन किया गया था। और अनियमित रूप से आपके कोड को क्रैश कर सकते हैं, विशेष रूप से रिलीज़ बिल्ड में सुनिश्चित करें कि आपने एमडीए बंद नहीं किया है।

cdecl और stdcall सी ++ और .NET के बीच दोनों वैध और प्रयोग करने योग्य हैं, लेकिन उन्हें दो अप्रबंधित और प्रबंधित संसारों के बीच संगत होना चाहिए। इसलिए InvokedFunction के लिए आपका C # घोषणा अमान्य है होना चाहिए stdcall एमएसडीएन नमूना सिर्फ दो अलग-अलग उदाहरण देता है, एक stdcall (संदेशबिप) के साथ, और एक सीडीईसीएल (प्रिंटफ़) के साथ। वे असंबंधित हैं