سؤال

أود مثال بسيط على تصدير وظيفة من DLL C ++ Windows.

أود أن أرى الرأس، ملف 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 باستخدام نفس الرأس. لا أريد أي معلومات حول تصدير أساليب فئة C ++، فقط الوظائف العالمية على غرار C.

تحديث

لا يشمل اتفاقية الاتصال (واستخدام خارجية "C") يعطيني أسماء الصادرات كما أحب، ولكن ماذا يعني ذلك؟ هو مهما كانت اتفاقية الاتصال الافتراضية التي أحصل عليها ما (أعتقد أن GetProcaddress، سيعتمد على مؤشر الوظيفة التي تم إنشاؤها المتصل).

أريد أن يتم استخدام DLL هذا دون ملف رأس، لذلك لا أحتاج حقا إلى الكثير من Vancy # Defines لجعل الرأس قابل للاستخدام من قبل المتصل.

أنا موافق مع الإجابة التي يجب أن استخدم ملف def.

هل كانت مفيدة؟

المحلول

إذا كنت تريد صادرات C عادي، استخدم مشروع C وليس C ++. C ++ DLLs تعتمد على اسم Mangingling لجميع ISMS C ++ (Namespaces etc ...). يمكنك ترجمة التعليمات البرمجية الخاصة بك ك C عن طريق الدخول إلى إعدادات المشروع الخاصة بك ضمن C / C ++ -> Advanced، يوجد خيار "ترجمة ك" ما الذي يستند إلى مفاتيح التحويل البرمجي / TP و / TC.

تصدير / استيراد DLL Libs في VC ++

ما تريد فعله حقا هو تحديد ماكرو مشروط في رأس سيتم تضمينه في جميع الملفات المصدر في مشروع 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_EXPORTS لن يتم تعريفه في مشروع يستهلك DLL، عندما يتضمن هذا المشروع ملف الرأس الخاص بمكتبتك سيتم استيراد جميع الوظائف بدلا من ذلك.

إذا كانت مكتبتك هي أن تكون عبر منصة، فيمكنك تحديد library_api لأنه لا شيء عند عدم تشغيل Windows:

#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. إن الطريقتين ينجزان نفس المهمة بطرق مختلفة، وأعتقد أن DLEXPORT / DLLIMPORT هي الطريقة الموصى بها من الاثنين.

تصدير وظائف غير مشغولة من DLL C ++ ل LoadLibrary / Pinvoke

إذا كنت بحاجة إلى هذا لاستخدام loadLibrary و getprocaddress، أو ربما تفعل pinvoke من .NET يمكنك استخدامها extern "C" مضمنة مع dllexport الخاص بك. وبما أننا نستخدم GetProcaddress بدلا من dllimport، فلن نحتاج إلى القيام بالرقص IFDEF من الأعلى، فقط DLExPort بسيط:

الرمز:

#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())
  );
}

وهنا ما تبدو عليه الصادرات مع Dumpbin / Exports:

  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")
);

نصائح أخرى

ل 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. استخدم ملف تعريف. ولكن هذا يجبرك للحفاظ على حالة ملف DEF.

  2. أبسط طريقة: تحديد الماكرو (انظر MSDN.) :

# تعديل تعليق التصدير (رابط، "/ تصدير:" __function__ "=" __fungdname__)

ثم قم بتضمين Pragma التالي في هيئة الوظيفة:

#pragma EXPORT

مثال كامل:

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

سيؤدي ذلك إلى تصدير الوظيفة غير المعطاة لأهداف X86 و X64 مع الحفاظ على __stdcall اتفاقية ل X86. ال __declspec(dllexport) ليس مطلوب في هذه الحالة.

كان لدي نفس المشكلة بالضبط، وكان الحل الخاص بي لاستخدام ملف تعريف الوحدة النمطية (.DEF) بدلا من __declspec(dllexport) لتحديد الصادرات (http://msdn.microsoft.com/en-us/library/d91k01sh.aspx.). ليس لدي أي فكرة لماذا هذا يعمل، لكنه يفعل

أعتقد أنني قد تحصل على ما تريد، ولكنه يمنع أيضا المحول البرمجي من إنشاء رمز إدارة المكدس لهذه الوظيفة. خارجية "C" يسبب الديكور اسم نمط C. إزالة ذلك وهذا يجب أن يتخلص من _ الخاص بك. لا يضيف الرابط السفلية السفلية. يتسبب STDCall في إلحاق حجم كومة الوسائط.

لمزيد من، انظر:http://en.wikipedia.org/wiki/x86_calling_conventions. http://www.codeproject.com/kb/cpp/calling_conventions_demystified.aspx.

السؤال الأكبر هو السبب في أن تريد أن تفعل ذلك؟ ما هو الخطأ في أسماء مشوشة؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top