سؤال

تم إنشاء C ++ DLL الأساسي والأسماء المصدرة باستخدام ملف تعريف الوحدة النمطية (mydll.def). بعد التجميع ، أتحقق من أسماء الوظائف المصدرة باستخدام dumpbin.exeأتوقع أن أرى:

SomeFunction

لكني أرى هذا بدلاً من ذلك:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

لماذا ا؟

تبدو الوظيفة المصدرة غير مُشكِّل (خاصة مقارنة بعدم استخدام ملف DEF للوحدة) ، ولكن ما الأمر مع الأشياء الأخرى؟

إذا استخدمت dumpbin.exe مقابل DLL من أي تطبيق تجاري ، تحصل على نظافة:

SomeFunction

ولا شيء آخر ...

حاولت أيضًا إزالة تعريف الوحدة النمطية وتصدير الأسماء باستخدام نمط التصدير "C" ، أي:

extern "C" void __declspec(dllexport) SomeFunction();

(ببساطة استخدام "Extern" C "لم ينشئ وظيفة مصدرة)

ومع ذلك ، لا يزال هذا يخلق نفس الإخراج ، وهو:

SomeFunction = SomeFunction@@@23mangledstuff#@@@@

لقد جربت أيضا #define dllexport __declspec(dllexport) الخيار وإنشاء lib مع عدم وجود مشكلة. ومع ذلك ، لا أريد تقديم ملف lib للأشخاص الذين يستخدمون DLL في تطبيق C#.

إنه Vanilla C ++ DLL (رمز غير مُدار) ، تم تجميعه باستخدام C ++ لا شيء سوى رأس ورمز بسيط. بدون الوحدة النمطية ، أحصل على وظائف مصدرة (يمكنني إنشاء مكتبة ثابتة واستخدام Lib لا مشكلة. أحاول تجنب ذلك). إذا استخدمت extern "C" __declspec(dllexport) أو تعريف الوحدة النمطية أحصل على ما يبدو أنه اسم وظيفة غير مملوءة ... المشكلة الوحيدة هي أنه يتبعه "=" وما يبدو وكأنه نسخة مزينة من الوظيفة. أريد التخلص من الأشياء بعد "=" - أو على الأقل أفهم سبب وجودها.

كما هو الحال ، أنا متأكد تمامًا من أنه يمكنني استدعاء الوظيفة من C# باستخدام P/Invoke ... أريد فقط تجنب هذا الخردة في نهاية "=".

أنا منفتح على اقتراحات حول كيفية تغيير إعدادات المشروع/التحويل البرمجي ، لكنني استخدمت للتو قالب Visual Studio DLL القياسي - لا شيء خاص.

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

المحلول

يمكنك الحصول على ما تريده عن طريق إيقاف توليد معلومات التصحيح. مشروع + خصائص ، رابط ، تصحيح ، توليد معلومات التصحيح = رقم

بطبيعة الحال ، أنت تريد فقط القيام بذلك لبناء الإصدار. حيث يتم تعيين الخيار بالفعل بهذه الطريقة.

نصائح أخرى

بدلا من استخدام ملف .DEF فقط أدخل pragma comment مثله

#pragma comment(linker, "/EXPORT:SomeFunction=_SomeFunction@@@23mangledstuff#@@@@")

تحرير: أو حتى أسهل: داخل جسم استخدام الوظيفة

#pragma comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)

. . . إذا كان لديك مشاكل في العثور على اسم الوظيفة المزينة. يمكن تخفيض هذا البراغما الأخير مع تعريف ماكرو بسيط.

عليك أن تعلن الوظائف extern "C" إذا كنت لا تريد أن تكون أسماءهم مشوهة.

من التجربة ، كن حذرًا إذا كنت تستخدم __stdcall في توقيع وظيفتك. مع __stdcall, ، سيبقى الاسم مشوهًا إلى حد ما (ستكتشف بسرعة كافية). على ما يبدو ، هناك مستويان من التشويش ، واحد extern "C" يتعامل مع مستوى C ++ ، لكنه لا يتعامل مع مستوى آخر من الاسم الذي يسببه __stdcall. يبدو أن التشويش الإضافي وثيق الصلة بالحمل الزائد - لكنني لست متأكدًا من ذلك.

آسف للرد على موضوع قديم ، ولكن ما تم تمييزه كما الجواب لم ينجح بالنسبة لي.

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

الإعداد: VS2005 تجميع مشروع مكتبة فئة C ++ المرئي. كنت أتحقق من إخراج .DLL مع أداة Walker التبعية من Microsoft.

فيما يلي وصفة مثال عملت بالنسبة لي ...

في Project.H:

#define DllExport extern "C" __declspec( dllexport )

DllExport bool API_Init();
DllExport bool API_Shutdown();

في project.cpp:

#include "project.h"

bool API_Init()
{
  return true;
}

bool API_Shutdown()
{
  return true;
}

ثم يتم استدعاؤه من الرمز المدير C# ، class.cs:

using System.Runtime.Interopservices;
namespace Foo
{
    public class Project
    {
        [DllImport("project.dll")]
        public static extern bool API_Init();

        [DllImport("project.dll")]
        public static extern bool API_Shutdown();
    }
}

منع ما سبق منع الأسماء المشوهة في كل من تصحيح الأخطاء ووضع الإصدار ، بغض النظر عن إعداد معلومات التصحيح. حظا طيبا وفقك الله.

حتى بدون الانقلاب ، يقوم الأسماء 32 بت و 64 بت ببناء تصدير الأسماء بشكل مختلف ، حتى مع "C" الخارجي. تحقق من ذلك مع الاعتمادات.

قد يعني هذا مشكلة كبيرة لأي عميل يقوم بمكتبة التحميل+GetProcadress للوصول إلى وظيفتك.

لذا ، علاوة على ذلك ، استخدم الآخرون ملف تعريف الوحدة النمطية على النحو التالي:

LIBRARY MYDLL
EXPORTS
myFunction=myFunction

نعم ، من الألم بعض الشيء ، ولكن بعد ذلك كم عدد الوظائف التي تم تصديرها تكتبها في اليوم؟

علاوة على ذلك ، عادةً ما أقوم بتغيير وحدات الماكرو كما هو موضح أدناه ، نظرًا لأن وظائف تصدير DLLS الخاصة بي ليست فئات C ++ وأريد أن تكون قابلة للاستدعاء من خلال معظم بيئات البرمجة:

#ifdef WTS_EXPORTS
#define WTS_API(ReturnType) extern "C" __declspec(dllexport) ReturnType WINAPI
#else
#define WTS_API(ReturnType) extern "C" __declspec(dllimport) ReturnType WINAPI
#endif

WTS_API(int) fnWTS(void);

السطر الأخير المستخدم لارتباك VisualAssistx قبل عامين ، لا أعرف ما إذا كان هذا هو الهضم بشكل صحيح الآن :-)

أعرف عدد المرات التي حاولت فيها فرض أسماء الوظائف باستخدام الكود و #pragma's. وأنا دائمًا ما ينتهي بنفس الشيء تمامًا ، باستخدام ملف تعريف الوحدة النمطية (*.def) في النهاية. وهنا السبب:

//---------------------------------------------------------------------------------------------------
// Test cases built using VC2010 - Win32 - Debug / Release << doesn't matter
//---------------------------------------------------------------------------------------------------
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = Yes (/DEBUG)
//  || (or, also doesn't matter)
// SET: Project > Properties > Linker > Debugging > Generate Debug Info = No + delete PDB file!

extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> _SetCallback@4

__declspec(dllexport) void SetCallback(LPCALLBACK function);
> ?SetCallback@@YAXP6AXHPADPAX@Z@Z

__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> ?SetCallback@@YGXP6GXHPADPAX@Z@Z    

//---------------------------------------------------------------------------------------------------
// this also big is nonsense cause as soon you change your calling convention or add / remove
// extern "C" code won't link anymore.

// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases
#pragma comment(linker, "/EXPORT:SetCallback=SetCallback")
extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=_SetCallback@4")
extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YAXP6AXHPADPAX@Z@Z")
__declspec(dllexport) void SetCallback(LPCALLBACK function);

// doesn't work on other cases / creates alias
#pragma comment(linker, "/EXPORT:SetCallback=?SetCallback@@YGXP6GXHPADPAX@Z@Z")
__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);

//---------------------------------------------------------------------------------------------------
// So far only repetable case is using Module-Definition File (*.def) in all possible cases:
EXPORTS
  SetCallback

extern "C" __declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

extern "C" __declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback

__declspec(dllexport) void SetCallback(LPCALLBACK function);
> SetCallback

__declspec(dllexport) void __stdcall SetCallback(LPCALLBACK function);
> SetCallback

// And by far this is most acceptable as it will reproduce exactly same exported function name 
// using most common compilers. Header is dictating calling convention so not much trouble for
// other sw/ppl trying to build Interop or similar.

أتساءل لماذا لم يفعل أحد هذا ، استغرق الأمر مني 10 دقائق فقط لاختبار جميع الحالات.

eishfunction@23mangledstuff #@@@@noved هو معطى الأنواع وفئة وظيفة C ++. الصادرات البسيطة هي وظائف قابلة للاتصال من C IE مكتوبة في C أو يتم إعلانها EXTERN وظائف غير الأعضاء في مساحة الاسم العالمية.

في الأساس ، عندما تستخدم وظائف في C ++ ، تتضمن أجزاء من أسمائها الآن توقيعها ومثل هذا ، من أجل تسهيل ميزات اللغة مثل التحميل الزائد.

إذا قمت بكتابة DLL باستخدام __declspec (dllexport) ، فيجب أن تنتج أيضًا lib. ارتباط بهذا lib ، وسيتم ربطك تلقائيًا وتسجيل الوظائف التي تم تسجيلها بواسطة CRT في وقت بدء التشغيل (إذا تذكرت تغيير جميع وارداتك إلى الصادرات). لا تحتاج إلى معرفة اسم mangling إذا كنت تستخدم هذا النظام.

في حال لم يكن الأمر واضحًا من مئات أسطر الوفل حول موضوع الصادرات المشوهة. ها هي قيمتها 2C :)

بعد إنشاء مشروع يسمى Win32Project2 باستخدام VS 2012 واختيار تصدير جميع الرموز في المعالج. يجب أن يكون لديك ملفان يسمى Win32Project2.cpp و Win32Project2.h

سيشير كلاهما إلى متغير قابل للتصدير ومثال تم تصديره.

في Win32Project2.h سيكون لديك ما يلي:

#ifdef WIN32PROJECT2_EXPORTS
#define WIN32PROJECT2_API __declspec(dllexport)
#else
#define WIN32PROJECT2_API __declspec(dllimport)
#endif

extern WIN32PROJECT2_API int nWin32Project2;
WIN32PROJECT2_API int fnWin32Project2(void);

لإلغاء الزنوج تغيير الخطين الأخيرتين إلى إعلانات "C" الخارجية إلى:

extern "C" WIN32PROJECT2_API int nWin32Project2;
extern "C" WIN32PROJECT2_API int fnWin32Project2(void);

في Win32Project2.cpp ، سيكون لديك أيضًا التعاريف الافتراضية التالية:

// This is an example of an exported variable
WIN32PROJECT2_API int nWin32Project2=0;

// This is an example of an exported function.
WIN32PROJECT2_API int fnWin32Project2(void)
{
    return 42;
}

لتغيير هذه إلى:

// This is an example of an exported variable
extern "C" WIN32PROJECT2_API int nWin32Project2=0;

// This is an example of an exported function.
extern "C" WIN32PROJECT2_API int fnWin32Project2(void)
{
    return 42;
}

في الأساس ، يجب عليك استخدام البادئة الخارجية "C" أمام التصريحات من أجل إجبار الرابط على إنتاج أسماء مثل C.

إذا كنت تفضل استخدام أسماء مشوهة لهذا الشيء من التشويش الإضافي (في حالة أن معلومات mangling مفيدة لشخص ما بطريقة أو بأخرى) ، استخدم "Dumpbin /Exports Win32Project2.dll" من سطر أوامر VC إلى البحث عن أسماء المرجع الفعلية. سيكون له النموذج "؟ fnwind32project2@[param bytes]@[معلومات أخرى]. هناك أيضًا أدوات عرض DLL أخرى حولها إذا لم يتم تشغيل قشرة أمر VC.

بالضبط سبب عدم قيام مرض التصلب العصبي المتعدد بالتخلف عن هذه الاتفاقية هو لغز. تعني معلومات التشويش الفعلية شيئًا ما (مثل حجم المعلمة بالبايت والمزيد) مما قد يكون مفيدًا للتحقق من الصحة وتصحيح الأخطاء ولكنه هو GUFF.

لاستيراد وظيفة DLL أعلاه إلى مشروع C# (في هذه الحالة ، يكون تطبيق Windows أساسي C# مع نموذج يحتوي على الزر "Button1") إليك بعض رمز النماذج:

using System.Runtime.InteropServices;



    namespace AudioRecApp
    {

      public partial class Form1 : Form
      {
        [ DllImport("c:\\Projects\test\Debug\Win32Projects2.dll")] 
        public static extern int fnWin32Project2();

        public Form1()
        {
          InitializeComponent();
        }


        private void button1_Click(object sender, EventArgs e)
        {
          int value;

          value = fnWin32Project2();
        }
      }
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top