تصدير وظائف من DLL مع dllexport
-
22-08-2019 - |
سؤال
أود مثال بسيط على تصدير وظيفة من 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.
حلولين
استخدم ملف تعريف. ولكن هذا يجبرك للحفاظ على حالة ملف DEF.
أبسط طريقة: تحديد الماكرو (انظر 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.
السؤال الأكبر هو السبب في أن تريد أن تفعل ذلك؟ ما هو الخطأ في أسماء مشوشة؟