DLL से dllexport के साथ फ़ंक्शन निर्यात करना


105

मुझे C ++ विंडोज डीएलएल से एक फ़ंक्शन निर्यात करने का एक सरल उदाहरण चाहिए।

मैं हेडर, .cppफ़ाइल और .defफ़ाइल (यदि बिल्कुल आवश्यक हो) देखना चाहूंगा ।

मैं निर्यात किए गए नाम को अनिर्दिष्ट होना चाहूंगा । मैं सबसे मानक कॉलिंग कन्वेंशन ( __stdcall?) का उपयोग करना चाहूंगा । मुझे इसका उपयोग करना चाहिए __declspec(dllexport)और .defफ़ाइल का उपयोग नहीं करना चाहिए ।

उदाहरण के लिए:

  //header
  extern "C"
  {
   __declspec(dllexport) int __stdcall foo(long bar);
  }

  //cpp
  int __stdcall foo(long bar)
  {
    return 0;
  }

मैं लिंक करने वाले से बचने की कोशिश कर रहा हूँ नाम के लिए अंडरस्कोर और / या संख्या (बाइट मायने रखता है?)।

मैं एक ही हेडर का समर्थन dllimportऔर dllexportउपयोग नहीं करने के साथ ठीक हूं । मैं सी + + क्लास के तरीकों के निर्यात के बारे में कोई जानकारी नहीं चाहता, बस सी-स्टाइल वैश्विक फ़ंक्शन।

अपडेट करें

कॉलिंग कन्वेंशन (और उपयोग extern "C") शामिल नहीं है मुझे निर्यात नाम देता है जैसा कि मुझे पसंद है, लेकिन इसका क्या मतलब है? क्या जो भी डिफ़ॉल्ट कॉलिंग कन्वेंशन है मुझे पिनवोक (.NET), डिक्लेयर (vb6), और GetProcAddressउम्मीद है? (मुझे लगता है कि इसके लिए GetProcAddressयह फ़ंक्शन पॉइंटर बनाने वाले पर निर्भर करेगा)।

मैं चाहता हूं कि यह डीएलएल एक हेडर फ़ाइल के बिना उपयोग किया जाए, इसलिए मुझे #definesहेडर को फोन करने वाले द्वारा उपयोग करने योग्य बनाने के लिए बहुत अधिक फैंसी की आवश्यकता नहीं है ।

मैं एक जवाब के साथ ठीक हूं कि मुझे एक *.defफाइल का उपयोग करना है।


मैं गलत याद रख सकता हूं, लेकिन मुझे लगता है कि: ए) extern Cसजावट को हटा देगा जो फ़ंक्शन के पैरामीटर प्रकारों का वर्णन करता है, लेकिन सजावट नहीं जो फ़ंक्शन के कॉलिंग सम्मेलन का वर्णन करता है; बी) डीईएफ फ़ाइल में आपको (अघोषित) नाम निर्दिष्ट करने के लिए आवश्यक सभी सजावट को हटाने के लिए ।
क्रिस 34

यही मैं भी देख रहा था। शायद आपको इसे पूर्ण उत्तर के रूप में जोड़ना चाहिए?
अद्र्वार्क

जवाबों:


134

यदि आप सादे C निर्यात चाहते हैं, तो C ++ नहीं C प्रोजेक्ट का उपयोग करें। सी ++ डीएलएल सभी सी ++ आइएमएस (नाम स्थान आदि ...) के लिए नाम-प्रबंध पर भरोसा करते हैं। आप अपने कोड को C / C ++ -> उन्नत के तहत अपनी परियोजना सेटिंग्स में जाकर C के रूप में संकलित कर सकते हैं, एक विकल्प "Compile As" है जो कंपाइलर स्विच / TP और / TC से मेल खाता है।

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

VC ++ में DLL Libs का निर्यात / आयात करना

क्या आप वास्तव में करना चाहते हैं एक हैडर में एक सशर्त मैक्रो को परिभाषित करना है जो आपके DLL प्रोजेक्ट में सभी स्रोत फ़ाइलों में शामिल होगा:

#ifdef LIBRARY_EXPORTS
#    define LIBRARY_API __declspec(dllexport)
#else
#    define LIBRARY_API __declspec(dllimport)
#endif

फिर एक ऐसे फंक्शन पर जिसे आप एक्सपोर्ट करना चाहते हैं LIBRARY_API:

LIBRARY_API int GetCoolInteger();

आपके लाइब्रेरी बिल्ड प्रोजेक्ट में LIBRARY_EXPORTSयह परिभाषित होता है कि यह आपके कार्यों को आपके DLL बिल्ड के लिए निर्यात किया जाएगा।

चूंकि LIBRARY_EXPORTSDLL का उपभोग करने वाली परियोजना में परिभाषित नहीं किया जाएगा, जब उस परियोजना में आपकी लाइब्रेरी की हेडर फ़ाइल शामिल होती है, जिसके बजाय सभी फ़ंक्शन आयात किए जाएंगे।

यदि आपकी लाइब्रेरी क्रॉस-प्लेटफ़ॉर्म होने वाली है, तो आप LIBRARY_API को विंडोज पर न होने के कारण परिभाषित कर सकते हैं:

#ifdef _WIN32
#    ifdef LIBRARY_EXPORTS
#        define LIBRARY_API __declspec(dllexport)
#    else
#        define LIBRARY_API __declspec(dllimport)
#    endif
#elif
#    define LIBRARY_API
#endif

Dllexport / dllimport का उपयोग करते समय आपको DEF फ़ाइलों का उपयोग करने की आवश्यकता नहीं होती है, यदि आप DEF फ़ाइलों का उपयोग करते हैं तो आपको dllexport / dllimport का उपयोग करने की आवश्यकता नहीं है। दो विधियाँ एक ही कार्य को अलग-अलग तरीके से पूरा करती हैं, मेरा मानना ​​है कि dllexport / dllimport दोनों में से अनुशंसित विधि है।

LoadLibrary / PInvoke के लिए C ++ DLL से असंबद्ध कार्यों का निर्यात करना

यदि आपको LoadLibrary और GetProcAddress का उपयोग करने की आवश्यकता है, या शायद किसी अन्य भाषा (यानी .NET से PInvoke, या Python / R आदि में FFI) का आयात कर रहे हैं, तो आप extern "C"Cl कंपाइलर को नाम न बताने के लिए बताने के लिए अपने dllexport के साथ इनलाइन का उपयोग कर सकते हैं । और चूंकि हम dllimport के बजाय GetProcAddress का उपयोग कर रहे हैं, हमें ऊपर से ifdef नृत्य करने की आवश्यकता नहीं है, बस एक साधारण dllexport:

कोड:

#define EXTERN_DLL_EXPORT extern "C" __declspec(dllexport)

EXTERN_DLL_EXPORT int getEngineVersion() {
  return 1;
}

EXTERN_DLL_EXPORT void registerPlugin(Kernel &K) {
  K.getGraphicsServer().addGraphicsDriver(
    auto_ptr<GraphicsServer::GraphicsDriver>(new OpenGLGraphicsDriver())
  );
}

और यहाँ निर्यात डम्पबिन / निर्यात के साथ कैसा दिखता है:

  Dump of file opengl_plugin.dll

  File Type: DLL

  Section contains the following exports for opengl_plugin.dll

    00000000 characteristics
    49866068 time date stamp Sun Feb 01 19:54:32 2009
        0.00 version
           1 ordinal base
           2 number of functions
           2 number of names

    ordinal hint RVA      name

          1    0 0001110E getEngineVersion = @ILT+265(_getEngineVersion)
          2    1 00011028 registerPlugin = @ILT+35(_registerPlugin)

तो यह कोड ठीक काम करता है:

m_hDLL = ::LoadLibrary(T"opengl_plugin.dll");

m_pfnGetEngineVersion = reinterpret_cast<fnGetEngineVersion *>(
  ::GetProcAddress(m_hDLL, "getEngineVersion")
);
m_pfnRegisterPlugin = reinterpret_cast<fnRegisterPlugin *>(
  ::GetProcAddress(m_hDLL, "registerPlugin")
);

1
बाहरी "C" सी + + शैली के नाम की छेड़छाड़ को दूर करने के लिए लग रहा था। संपूर्ण आयात बनाम निर्यात चीज़ (जिसे मैंने प्रश्न में शामिल न करने का सुझाव देने की कोशिश की) वास्तव में वह नहीं है जो मैं (लेकिन इसकी अच्छी जानकारी) के बारे में पूछ रहा हूं। मुझे लगा कि यह समस्या को बादल देगा।
अद्र्वार्क

एकमात्र कारण मैं सोच सकता हूं कि आपको इसकी आवश्यकता होगी कि LoadLibrary और GetProcAddress के लिए ... यह पहले से ही ध्यान रखा जाता है, मैं अपने उत्तर निकाय में विस्तार करूँगा ...
joshperry

क्या EXTERN_DLL_EXPORT == बाहरी "C" __declspec (dllexport) है? क्या वह एसडीके में है?
अदरवार्क

3
प्रोजेक्ट की लिंकर सेटिंग में मॉड्यूल डेफिनिशन फाइल को जोड़ना न भूलें - बस "प्रोजेक्ट में एक मौजूदा आइटम जोड़ना" पर्याप्त नहीं है!
जिमी

1
मैंने इसका इस्तेमाल VS के साथ DLL संकलित करने के लिए किया और फिर R .C के उपयोग से इसे कॉल किया। महान!
जुवानेंट्रो

33

C ++ के लिए:

मुझे बस एक ही मुद्दे का सामना करना पड़ा और मुझे लगता है कि यह एक समस्या का उल्लेख करने के लायक है जब कोई एक दोनों __stdcall(या WINAPI) और extern "C" : का उपयोग करता है

जैसा कि आप जानते हैं extern "C"कि सजावट को हटा दिया जाता है ताकि इसके बजाय:

__declspec(dllexport) int Test(void)                        --> dumpbin : ?Test@@YaHXZ

आप अनिर्दिष्ट एक प्रतीक नाम प्राप्त करते हैं:

extern "C" __declspec(dllexport) int Test(void)             --> dumpbin : Test

हालाँकि _stdcall(= मैक्रो WINAPI, जो कॉलिंग कन्वेंशन को बदल देता है) भी नामों को सजाता है ताकि यदि हम दोनों का उपयोग करें तो हम प्राप्त करें:

   extern "C" __declspec(dllexport) int WINAPI Test(void)   --> dumpbin : _Test@0

और लाभ extern "C"खो गया है क्योंकि प्रतीक सजाया गया है (_ @bytes के साथ)

ध्यान दें कि यह केवल x86 आर्किटेक्चर के लिए होता है क्योंकि __stdcallकन्वेंशन x64 ( msdn : x64 आर्किटेक्चर पर ध्यान नहीं दिया जाता है , कन्वेंशन द्वारा, जब संभव हो तो रजिस्टरों में तर्क पारित किए जाते हैं, और बाद के तर्क स्टैक पर पारित किए जाते हैं ।)।

यह विशेष रूप से मुश्किल है अगर आप x86 और x64 दोनों प्लेटफार्मों को लक्षित कर रहे हैं।


दो उपाय

  1. एक परिभाषा फ़ाइल का उपयोग करें। लेकिन यह आपको फ़ाइल की स्थिति बनाए रखने के लिए मजबूर करता है।

  2. सबसे सरल तरीका: मैक्रो को परिभाषित करें (देखें msdn ):

#define निर्यात टिप्पणी (लिंकर, "/ निर्यात:" __FUNCTION__ "=" __FUNCDNAME__)

और फिर फंक्शन बॉडी में निम्नलिखित प्रोगमा शामिल करें:

#pragma EXPORT

पूर्ण उदाहरण:

 int WINAPI Test(void)
{
    #pragma EXPORT
    return 1;
}

यह x86 के लिए __stdcallकन्वेंशन को संरक्षित करते हुए x86 और x64 दोनों लक्ष्यों के लिए अघोषित रूप से फ़ंक्शन का निर्यात करेगा । __declspec(dllexport) नहीं है इस मामले में की आवश्यकता है।


5
इस महत्वपूर्ण संकेत के लिए धन्यवाद। मैं पहले से ही हैरान था कि मेरे 64Bit DLL 32 बिट से अलग क्यों है। मैं आपके उत्तर को उत्तर के रूप में स्वीकार किए गए की तुलना में अधिक उपयोगी मानता हूं।
एल्म्यू

1
मुझे वास्तव में यह दृष्टिकोण पसंद है। मेरी एकमात्र सिफारिश मैक्रो का नाम बदलकर EXPORT_FUNCTION करना होगा क्योंकि __FUNCTION__मैक्रो केवल कार्यों में काम करता है।
लुइस

3

मेरे पास बिल्कुल यही समस्या थी, मेरा समाधान __declspec(dllexport)निर्यात को परिभाषित करने के बजाय मॉड्यूल परिभाषा फ़ाइल (.def) का उपयोग करना था ( http://msdn.microsoft.com/en-us/library/d91k01sh.aspx )। मुझे नहीं पता कि यह क्यों काम करता है, लेकिन यह करता है


इस पर चलने वाले किसी अन्य व्यक्ति पर ध्यान दें: .defमॉड्यूल निर्यात फ़ाइल का उपयोग करना काम करता है , लेकिन externहेडर फ़ाइल में परिभाषाओं की आपूर्ति करने में सक्षम होने के लिए उदाहरण के लिए वैश्विक डेटा - जिस स्थिति में, आपको आंतरिक उपयोगों externमें मैन्युअल रूप से परिभाषा की आपूर्ति करनी होगी वह डेटा। (हाँ, ऐसे समय होते हैं जब आपको इसकी आवश्यकता होती है।) यह आम तौर पर और विशेष रूप से क्रॉस-प्लेटफ़ॉर्म कोड दोनों के लिए बेहतर है कि बस मैक्रो के साथ उपयोग करें ताकि आप डेटा को सामान्य रूप से सौंप सकें। __declspec()
क्रिस क्रिचो

2
कारण शायद यह है कि यदि आप उपयोग कर रहे हैं __stdcall, तो सजावट __declspec(dllexport)को नहीं हटाएगा। .defहालाँकि, वसीयत में फ़ंक्शन जोड़ना ।
ब्योर्न लिंडक्विस्ट

1
@ BjörnLindqvist +1, ध्यान दें कि यह केवल x86 के मामले में है। मेरा जवाब देखिए।
मल्लिक

-1

मुझे लगता है कि _naked को आप जो चाहते हैं वह मिल सकता है, लेकिन यह कंपाइलर को फ़ंक्शन के लिए स्टैक प्रबंधन कोड उत्पन्न करने से भी रोकता है। बाहरी "सी" सी शैली नाम सजावट का कारण बनता है। उसे हटा दें और जिसे आपके _ से छुटकारा मिल जाए। लिंकर अंडरस्कोर नहीं जोड़ता है, कंपाइलर करता है। stdcall तर्क स्टैक आकार को जोड़ देता है।

अधिक के लिए, देखें: http://en.wikipedia.org/wiki/X86_calling_conventions http://www.codeproject.com/KB/cpp/calling_conventions_demystified.aspx

बड़ा सवाल यह है कि आप ऐसा क्यों करना चाहते हैं? आम के नाम में क्या खराबी है?


लोडलेबिल्ड्स / GetProcAddress या अन्य विधियों का उपयोग करते समय मांगे गए नाम बदसूरत होते हैं जो एसी / सी ++ हेडर होने पर भरोसा नहीं करते हैं।
अद्रवार्क

4
यह अनहेल्दी होगा - आप केवल बहुत विशिष्ट परिस्थितियों में संकलित स्टैक प्रबंधन कोड को निकालना चाहते हैं। (बस __cdecl का उपयोग करना सजावट को खोने का एक कम हानिकारक तरीका होगा - डिफ़ॉल्ट रूप से __declspec (dllexport) __cdecl विधियों के साथ सामान्य _ उपसर्ग को शामिल नहीं करता है।)
Griffiths

मैं वास्तव में यह नहीं कह रहा था कि यह मददगार होगा, इसलिए मेरे प्रभाव अन्य प्रभावों के बारे में मेरे प्रश्न हैं और यह भी पूछते हैं कि वह ऐसा क्यों करना चाहता था।
Rob K
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.