विंडोज पर __cdecl या __stdcall?


84

मैं वर्तमान में Windows के लिए C ++ लाइब्रेरी विकसित कर रहा हूं जिसे DLL के रूप में वितरित किया जाएगा। मेरा लक्ष्य बाइनरी इंटरऑपरेबिलिटी को अधिकतम करना है; अधिक सटीक रूप से, मेरे DLL में होने वाले कार्यों को DLVC को फिर से जोड़ने के बिना MSVC ++ और MinGW के कई संस्करणों के साथ संकलित कोड से उपयोग करने योग्य होना चाहिए। हालाँकि, मैं इस बात को लेकर उलझन में हूँ कि किस सम्मेलन को बुलाना सबसे अच्छा है, cdeclया stdcall

कभी-कभी मुझे ऐसे कथन सुनाई देते हैं जैसे "सी कॉलिंग कन्वेंशन केवल एक ही एग्रॉस कंपाइलर होने की गारंटी है", जो कि " विशेष रूप से कैसे मानों को वापस करने के लिए " की व्याख्या में कुछ भिन्नताएंcdecl जैसे बयानों के विपरीत हैं । ऐसा प्रतीत होता है कि कुछ पुस्तकालय डेवलपर्स (जैसे कि libsndfile ) को सीएल कन्वेंशन का उपयोग करने के लिए डीएलएल में सी कॉलिंग कन्वेंशन का उपयोग करना बंद कर देते हैं, वे बिना किसी दृश्य समस्याओं के।

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

वहाँ बहुत अधिक विरोधी जानकारी है, और मेरे द्वारा चलाए जाने वाले प्रत्येक खोज से मुझे अलग-अलग उत्तर मिलते हैं जो वास्तव में मुझे दोनों के बीच निर्णय लेने में मदद नहीं करता है। मैं एक स्पष्ट, विस्तृत, तर्कपूर्ण स्पष्टीकरण की खोज कर रहा हूं कि मुझे दूसरे पर एक का चयन क्यों करना चाहिए (या दोनों क्यों बराबर हैं)।

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


4
"Cdecl की व्याख्या में कुछ भिन्नताएँ हैं, विशेष रूप से मूल्यों को कैसे लौटाया जाए" - इसका लगभग अर्थ यह है कि x86 पर cdecl का उपयोग करने वाले भिन्न OS cdecl के विभिन्न प्रकारों का उपयोग कर सकते हैं, यह अलग ABI है जो दोनों को "cdecl" कहते हैं। विंडोज विविधताओं को नीचे गिराती है, कोई भी कार्यान्वयन जो विंडोज पर चलता है और विंडोज के विकल्पों का सम्मान नहीं करता है, विंडोज सीडीसीएल फ़ंक्शन को कॉल करने में सक्षम नहीं होगा, इसलिए जैसा कि आप stdcall के साथ कहते हैं, यह विंडोज प्रोग्रामिंग के लिए बेकार है, और कुछ मानक सी हैं कार्यों को लागू करना मुश्किल होगा।
स्टीव जेसप

1
__Stdcall कॉलिंग कन्वेंशन का उपयोग Win32 API फ़ंक्शन को कॉल करने के लिए किया जाता है। docs.microsoft.com/en-us/cpp/cpp/stdcall?view=vs-2017
Zanna

जवाबों:


79

मैंने अभी कुछ वास्तविक दुनिया का परीक्षण (DLVC और अनुप्रयोगों को MSVC ++ और MinGW के साथ संकलित किया, फिर उन्हें मिलाया)। जैसा कि यह प्रतीत होता है, मेरे पास cdeclकॉलिंग सम्मेलन के साथ बेहतर परिणाम थे ।

अधिक विशेष रूप से: के साथ समस्या stdcallयह है कि MSVC ++ DLL निर्यात तालिका में नाम का उपयोग करता है, तब भी extern "C"। उदाहरण के लिए fooबन जाता है _foo@4। यह केवल उपयोग करते समय होता है __declspec(dllexport), डीईएफ फ़ाइल का उपयोग करते समय नहीं; हालाँकि, DEF फाइलें मेरे विचार से रखरखाव में परेशानी हैं, और मैं उनका उपयोग नहीं करना चाहता।

MSVC ++ नाम की मैनबलिंग में दो समस्याएं हैं:

  • GetProcAddressडीएलएल का उपयोग करना थोड़ा अधिक जटिल हो जाता है;
  • डिफ़ॉल्ट रूप से MinGW सजाए गए नामों (जैसे कि foo@4बजाय MinGW का उपयोग करेगा _foo@4) के लिए एक अवांछनीय नहीं करता है , जो लिंकिंग को जटिल करता है। इसके अलावा, यह डीएलएल के "गैर-अंडरस्कोर संस्करण" को देखने के जोखिम का परिचय देता है और अनुप्रयोग जंगली में पॉप अप करते हैं जो "अंडरस्कोर संस्करण" के साथ असंगत हैं।

मैंने cdeclसम्मेलन की कोशिश की है: MSVC ++ और MinGW के बीच अंतर्संचालनीयता पूरी तरह से, आउट-ऑफ-द-बॉक्स काम करती है, और नाम DLL निर्यात तालिका में अनिर्दिष्ट रहते हैं। यह आभासी तरीकों के लिए भी काम करता है।

इन कारणों से, cdeclमेरे लिए एक स्पष्ट विजेता है।


विशेष रूप से, यह 64-बिट DLL पर लागू नहीं होता है, क्योंकि __stdcall और __cdecl दोनों को आमतौर पर अनदेखा किया जाता है- इसलिए निर्यात किए गए C फ़ंक्शन पर कोई नाम नहीं होता है।
jtbr

@jtbr निर्यात सी ++ फ़ंक्शंस (यानी नहीं extern "C") के बारे में कैसे ?
इला .२ El२

@ Ela782 C ++ में फ़ंक्शन ओवरलोडिंग का समर्थन करने के लिए हमेशा कुछ नाम मेनबलिंग होंगे। लेकिन यह मानकीकृत नहीं है और इस प्रकार संकलक के बीच भिन्न हो सकता है।
3

@jtbr क्षमा करें, मेरा मतलब उस नाम से नहीं था जो लड़ रहा था। मेरा मतलब है कि क्या __stdcall और __cdecl दोनों को 64 + बिट DLL में निर्यात की गई C ++ फ़ंक्शन के साथ भी अनदेखा किया गया है।
इला 782

1
@ Ela782 हाँ; मेरा मानना ​​है कि सभी x86-64 संकलन में __stdcall और __cdecl को अनदेखा किया गया है। देखें विकिपीडिया
jtbr

20

दो कॉलिंग सम्मेलनों में सबसे बड़ा अंतर यह है कि " _ _cdecl" कॉलर पर एक फ़ंक्शन कॉल के बाद स्टैक को संतुलित करने का बोझ रखता है, जो चर राशि के तर्कों के साथ फ़ंक्शन की अनुमति देता है। "__Stdcall" सम्मेलन प्रकृति में "सरल" है, हालांकि इस संबंध में कम लचीला है।

इसके अलावा, मेरा मानना ​​है कि प्रबंधित भाषाएँ डिफ़ॉल्ट रूप से stdcall कन्वेंशन का उपयोग करती हैं, इसलिए पी / इनवोक का उपयोग करने वाले किसी व्यक्ति को cdecl के साथ जाने पर कॉलिंग कन्वेंशन को स्पष्ट रूप से बताना होगा।

इसलिए, यदि आपके सभी फ़ंक्शन हस्ताक्षर सांख्यिकीय रूप से परिभाषित होने जा रहे हैं, तो मैं शायद सीडीसीएल की ओर झुकाव करूंगा, यदि सीडीसीएल नहीं।


11

सुरक्षा के संदर्भ में, __cdeclसम्मेलन "सुरक्षित" है क्योंकि यह कॉलर है जिसे स्टैक से निपटने की आवश्यकता है। एक __stdcallपुस्तकालय में क्या हो सकता है कि डेवलपर स्टैक को ठीक से निपटाना भूल गया हो, या कोई हमलावर DLL के स्टैक (जैसे एपीआई हुकिंग) को दूषित करके कुछ कोड को इंजेक्ट कर सकता है जो तब कॉलर द्वारा जांच नहीं की जाती है। मेरे पास कोई सीवीएस सुरक्षा उदाहरण नहीं हैं जो दिखाते हैं कि मेरा अंतर्ज्ञान सही है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.