• 0
Install

مشكلة اثناء محاولة انشاء مكتبة dll بسيطة في c++

سؤال

السلام عليكم ورحمة الله وبركاته

عساكم بخير وصحة وسلامة اخواني الأعزاء

لدي مشكلة

هي اني كلما احاول انشئ مكتبة dll واستدعيها في ملف آخر ينهار البرنامج كالتالي

post-275718-0-46429600-1402592761_thumb.

المكتبة فيها ملفين myd.cpp , myd.h

الملفين فيهما الاكواد التالي :

myd.cpp

int multiply(int a,int b){ return a*b;}

myd.h

#ifndef MYD_H_#define MYD_H_#ifdef EXPORTING_DLLextern __declspec(dllexport) int multiply(int,int) ;#elseextern __declspec(dllimport) int multiply(int,int) ;#endifclass Maincs{public:	Maincs();	int  __stdcall multiply(int,int);};#endif

البرنامج الذي ينفذ هذه المكتبة فيه الكود التالي :

/* * m.cpp * *  Created on: Jun 12, 2014 *      Author: ASUS */#include<iostream>#include<windows.h>#include<string>using namespace std;int main(){	HINSTANCE h = LoadLibrary("my.dll");	FARPROC functionP = GetProcAddress(HMODULE(h),"multiply");	typedef int(__stdcall * pIC)(int,int);	pIC multiply;	multiply = pIC(functionP);	int retValue = multiply(6,10);	cout << retValue;}

وكلما احاول تنفيذ البرنامج الذي يستدعي المكتبة وينفذ الدالة multiply ينهار البرنامج ولا يعمل

اين المشكلة ؟؟

ولكم جزيل الشكر

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

6 إجابة على هذا السؤال .

  • 0

أنت لم تصدر الدالة multiply لذا ستفشل GetProcAddress في إيجاد عنوانها (تحقق أن LoadLibrary و GetProcAddress لاتعيد NULL). أيضاً ملف الـheader ليس له دور طالما أنك لم تدرجه.

كي تصدر دالة يلزمك أن تصدر اسمها وتعطل تعديل الإسم باستخدام extern "C" دامك تستخدم c++ (والا ستصدر باسم مثل [email protected]@[email protected] أو_Z8multiplyii)، وتوحد طريقة الإستدعاء، دالتك الأولى تستخدم cdecl وفي العميل تستخدم stdcall وهذا خطأ، ربما تستخدم الأولى cdecl كي لاضطر للتعامل مع ملفات الـdef بسبب تعديل الأسم (multiply ستصدر [email protected]).

اعدادات بسيطة:

 

// myd.h#ifndef MYD_H_#define MYD_H_#ifdef __cplusplusextern "C" {#endif#define DLLCALL __cdecl#define DLLEXPORT __declspec(dllexport)DLLEXPORT int DLLCALL multiply(int a, int b);#ifdef __cplusplus};#endif#endif


#include "myd.h"DLLEXPORT int DLLCALL multiply(int a, int b){ return a*b;}


/* * m.cpp * *  Created on: Jun 12, 2014 *      Author: ASUS */#include<iostream>#include<windows.h>#include<string>using namespace std;int main(){    HINSTANCE h = LoadLibrary("my.dll");  if(h == NULL)  {    cerr << "Error: LoadLibrary" << endl;    return -1;  }    FARPROC functionP = GetProcAddress(HMODULE(h),"multiply");  if(functionP == NULL)  {    cerr << "Error: GetProcAddress" << endl;    return -1;  }    typedef int(__stdcall *pIC)(int,int);    pIC multiply;    multiply = pIC(functionP);    int retValue = multiply(6,10);    cout << retValue;}

يرجى أيضاً الإطلاع على المقال: كيف تصنع مكتبه.

2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

الأخ الرائع والعزيز Mr.B كالعادة

جزاك الله كل خير ياأخي وشكرا لكم على المساعدة والإهتمام

اريد ان اطرح المزيد من الأسئلة وارجوا ان لا اكون ثقيل الظل عليك اخي العزيز

اسئلتي ستكون معلمة باللون الأحمر مع انك ستجد معظم الكلام في اللون الأحمر لان معظم الكلام عبارة عن اسئلة ومعظمها ربما سخيف ويدل على قلة خبرتي فعلا

ولكنني حقاً متشوق لأحترف موضوع التعامل مع مكتبات ال dll والتعامل ايضاً مع الذاكرة بشكل مباشرة التعامل مع العمليات التي تعمل في الذاكرة والتعديل عليها تفريغ مساحتها واحتلالها لزرع اكواد برامج اخرى فيها كل هذه الاشياء حقاً تشدّني وخصوصاً إذا كانت معمولة باستخدام الــ STL C++ العظيمة

 

 

أنت لم تصدر الدالة multiply لذا ستفشل GetProcAddress في إيجاد عنوانها

اخي العزيز فعلاً كلامك صحيح بحثت اولا ووجدت انه يجب عمل Export للدوال المستخدمة في المكتبة وإلا ستعتبر Private ولن ترى إلا من داخل المكتبة نفسها

فبحثت ووجدت انه يجب ان اضيف شيء ما يشبه

extern __declspec(dllexport) int multiply(int,int) ;

وبحثت مرة اخرى فوجدت انه يمكنني ان اعرف جدول الدوال المصدرة , أو قائمة باسماء كل الدوال والأعضاء التي تصدرها المكتبة عن طريق اداة Dumpin التي توفرها مايكروسوفت

 

 

وفعلا استخدمت الأداة ولاحظت كما قلت تماما أخي العزيز

فعلا اسماء الدوال عجيبة كانت

هذه صورة عن ماأقصده

post-275718-0-80035300-1402608014_thumb.

 

فعلاً كما قلت يااخي , ولكن للأسف لم اعرف إلى الآن لماذا وكيف نقوم بعمل Export للدوال

وهل حقاً نستطيع ان نعمل Export لاي members ضمن المكتبة

ولدي سؤال لو استخدمنا اكثر من namespace في المكتبة هل يجب ان يكون هناك اجراءات اخرى

 

وبخصوص الدالة GetProcAddress اخي العزيز ماهي وظيفتها بالضبط وما هي الحالات التي نستخدمها فيها

 

مااعرفه انا انها ترجع مؤشر لموقع دالة ما

هل هناك استخدامات اخرى لها

وما هو المقصود بمؤشر لموقع الدالة ؟ مؤشر لها في الذاكرة ام ماذا ؟ وهل يقتصر استخدامها على مكاتب ال DLL ام يمكن ايضاً استخدامها على الملفات التنفيذية

 

 

أيضاً ملف الـheader ليس له دور طالما أنك لم تدرجه.

ماهو المقصود ب "طالما لم ادرجه " اخي العزيز ؟

 

 

 

 

كي تصدر دالة يلزمك أن تصدر اسمها وتعطل تعديل الإسم باستخدام extern "C" دامك تستخدم c++

 

 

لماذا extern "C" وماالمقصود بذلك

اعرف انا ان الكلمة المفتاحية extern تعني انها هذه الدالة يمكن استخدامها من خارج المكان الذي عرفت به اليس كذلك طيب و C مالمقصود بها هنا ؟

 

اخي العزيز ايضاً:انت قلت

 

وفي العميل تستخدم stdcall وهذا خطأ

 

كيف افعل ذلك ؟؟

فقط اكتب __cdecl

ولماذا علامتي __ قبلها في العميل ؟؟ لماذا يجب ان نكتبهم

 

جزاك الله كل خير على الرابط المفيد جداً

لم اكن اعلم حقاً ان هناك من وضع مثل هكذا مواضيع مفيدة جدا جدا جدا

والله لا اعرف كيف اعبر عن شكري لك .

لان مثل هكذا معلومات نادراً ما تجد مصدر عربي قوي تستند عليه او شخص خبير تسأله

انت يااخي والأخ C++er والله كنز بكل ماتحمل الكلمة من معنى

الله يحفظكم ويديمكم يااخي بما تفيدوننا به فعلا

والسلام عليكم ورحمة الله وبركاته وعذراً على كثرة الأسئلة ارجوا ان تعذر جهلي

فهذه اول مرة لي اتعامل مع مكاتب dll في ال c++

لقد صدمت حقاً بالفرق الشاسع بين انشاء مكتبة dll في .net وبين انشاء مكتبة dll في c++

سأقوم بتجربة التعديلات وارد لك خبر

اخي انا اقوم بعمل compile للمكتبة "انا استخدم eclipse ك IDE"

فينتج ملف .o اقوم بتحويله ل dll يدوياً عن طريق استدعاء ال g++ بالكوماند لاين التالي :

 

g++ -shared mydll.o -o mydll.dll

هذا صحيح اليس كذلك

اتمنى لو لديك مصادر حتى ولو اجنبية تفيدني فيها عن هذه الاشياء المتقدمة

وجزاك الله كل خير مرة اخرى , ومرة اخرى عذراً على الإطالة

والسلام عليكم ورحمة الله

 

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

بخصوص سؤالي حول "ماهو المقصود ب "طالما لم ادرجه " اخي العزيز ؟ "  

:D   يبدوا انني عندما قمت بالتعديل على الملف cpp. نسيت ومسحت التضمين للملف .h معاه

آسف أخي انسى هذا السؤال واعذرني ارجوك

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

الأخ العزيز Mr.B

للاسف نفذت التعديلات التي قلت لي ان انفذها :

اصبح الملف myd.h كالتالي :

#ifndef MYD_H_#define MYD_H_#ifdef __cplusplusextern "C" {#endif#define DLLCALL __cdecl#define DLLEXPORT __declspec(dllexport)class Maincs{public:	Maincs();	DLLEXPORT int  DLLCALL multiply(int a,int b);};#ifdef __cplusplus};#endif#endif

واصبح الملف myd.cpp :

/* * myd.cpp * *  Created on: Jun 12, 2014 *      Author: ASUS */#include "myd.h"DLLEXPORT int DLLCALL multiply(int a,int b){	return a*b;}

عملت BUILD ثم نفذت الكوماند لاين " g++ -shared myd.o myd.dll"

ونتج ملف ال dll نفذت البرنامج انطبعت رسالة الخطأ Error : GetPocAddress  فحصت المكتبة بأداة Dumpbin ونفس إسم الدالة الخاطئ

ولكن الحمد لله اني اخذت احتياطاتي العجيبة

انا مرة كنت ابحث عن كود اخذ قيمة ال md5 لملف

المهم كانت قبل ان اقوم بالتعديلات اخذت md5 ال dll القديم

ومن ثم قمت بتعديلاتي وبعد ال build وانتاج ملف ال dll الجديد اخذت ال md5 الكارثة الغير قابلة للفهم

ان الملفين القديم والجديد لهما نفس ال md5  ؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟؟

 

قلت لنفسي فلأضع التأثيرات السحرية في الحسبان

اخذت اكوادك من ردك و COPY - PASTE لملف المشروع

عملت BUILD و نفذت البرنامج العميل والحمد لله تم تنفيذ الدالة بنجاح وارجع 60

لماذا اخي هذا حصل ؟؟؟ اين الخطأ ؟ ماهو السبب العلمي لما حدث ؟؟

ولكم جزيل الشكر وبارك الله فيكم مرة اخرى

تم تعديل بواسطه Install
1

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

* لما تحمل مكتبة، فأنك لاتعرف أين سيكون موقع المكتبة في الذاكرة فقد تحمل عند أي عنوان، توجد طرق لإقتراح عنوان معين على المحمل، لكن لايوجد ما يضمن أنها ستحمل عند هذا العنوان، مجرد مايجد المحمل أن العنوان مستخدم (عن طريق مكتبة أخرى أو هناك ملف مربوط بهذا العنوان memory mapped file)، فسيحملها لعنوان آخر، أيضاً لو حليت هذه المشكلة بطريقة ما، فبمجرد إجراء تغيير في المكتبة فسيتغير مواقع الدوال فيها، الحل هو استخدام هذا جدول التصدير export table.

 

التصدير يتم عن طريق إنشاء جدول للدوال أو المتغيرات العامة التي تصدرها المكتبة (اي شيء مخزن)، يحتوي هذا الجدول على أسماء الدوال (نصوص)[1] وموقع كل دالة من بداية عنوان المكتبة. لما تستخدم __declspec(dllexport) فأنت تطلب إضافة هذه الدالة في الجدول لتصدرها.

 

LoadLibrary تقوم بتحميل المكتبة للذاكرة وتعيد HMODULE، وهو عنوان بداية المكتبة في الذكرة. GetProcAddress تقوم بالبحث عن اسم الدالة بين الدوال التي تصدرها المكتبة، إذا وجدتها فتجمع عنوان المكتبة مع عنوان الدالة من بداية المكتبة وتعيد لك عنوان الدالة.

 

مثال دالة صغيرة لمحاكاة GetProcAddress:

 

/* * Barakat S. * MiniGetProcAddress, GetProcAddress like */#include <windows.h>#include <stdio.h>#include <assert.h>typedef int (*func_t)(int, int);void *MiniGetProcAddress(HMODULE hModule, LPCSTR lpProcName);intmain(int argc, char **argv){  HMODULE Module;  func_t add;  Module = LoadLibraryW(L"file.dll");  if(Module == NULL)    goto Cleanup;  add = (func_t) MiniGetProcAddress(Module, "add");  if(add == NULL)    goto Cleanup;  printf("%d\n", add(4, 1));  FreeLibrary(Module);  return 0;Cleanup:  printf("Error\n");  return -1;}void *MiniGetProcAddress(HMODULE hModule, LPCSTR lpProcName){  BYTE *Module;  IMAGE_DOS_HEADER *DosHeader;  IMAGE_NT_HEADERS *NtHeader;  IMAGE_EXPORT_DIRECTORY *ExportDirectory;  DWORD *FunctionsOffset;  DWORD *FunctionsName;  WORD *FunctionsOrder;  size_t i;  if(lpProcName == NULL)    return NULL;  Module = (BYTE *) hModule;  DosHeader = (IMAGE_DOS_HEADER *) Module;  if(DosHeader->e_magic != (WORD)0x5a4d) /* 0x5A4D = 'MZ' */    return NULL;  NtHeader = (IMAGE_NT_HEADERS *)(Module + DosHeader->e_lfanew);  if(NtHeader->Signature != (DWORD)0x00004550) /* 0x00004550 = 'PE\0\0' */    return NULL;  ExportDirectory = (IMAGE_EXPORT_DIRECTORY *)(Module +    NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);  FunctionsOffset = (DWORD *)(Module + ExportDirectory->AddressOfFunctions);  FunctionsName   = (DWORD *)(Module + ExportDirectory->AddressOfNames);  FunctionsOrder  = (WORD *)(Module + ExportDirectory->AddressOfNameOrdinals);  for(i = 0 ; i < ExportDirectory->NumberOfFunctions ; i++)  {    char *Name = (char *)(Module + FunctionsName[i]);    WORD Order = FunctionsOrder[i];    void *Addr = (void *)(Module + FunctionsOffset[Order]);    /* printf("%04x: %p : %s\n", Order, Addr, Name); */    if(strcmp(Name, lpProcName) == 0)      return Addr;  }  return NULL;}

 

ممكن أن تقرأ Portable Executable File Format – A Reverse Engineer View الجزء 8 The Export Section للمزيد عن جدول التصدير في ويندوز.

 

* extern "C" كما أشرت أنها تعطل تعديل الأسماء الذي تجريه مصرفات c++ على اسم الدالة وتصدر الدالة كما هي بنفس الإسم، المقصود بالتعديل مثل تحويل multiply إلى [email protected]@[email protected] أو_Z8multiplyii (هذا التعديل لغرض دعم الـoverloading).

 

* استخدام اسلوب إستدعاء مختلف (في المكتبة cdecl وفي العميل stdcall أو العكس) يسبب أخطاء أو انهيار البرنامج، طريقة تنظيف المكدس من المتغيرات الممررة للدوال تختلف بين cdecl و stdcall، الأول المستدعي يزيل هذه الدوال وفي الثانية المستدعى يزيل هذه الدوال، وضع اسلوب استدعاء مختلف يجعل المتغيرات تزال مرتين ويصبح عنوان المكدس يشير للقيمة الخطأ.

 

* أي شيء يبدأ بـ__ يعني إضافة غير قياسية على اللغة، طرق تصدير الدوال وأساليب الإستدعاء وغيرها أشياء ليست جزء من c أو c++.

 

* في برنامجك الأخير، أنت لم تنشيء كائن من الـclass، لذا الـclass كأن ليس له وجود، عموماً طريقة تصدير الفئات تختلف بين مصرف وآخر، ممكن أن تقرأ هذا المقال: تصدير C++ class في مكتبة DLL.

 

[1] توجد طريقة نادرة الإستخدام لتصدير الدوال حسب ترتيبها ordinal بدل استخدام الإسم تستخدم الترتيب مثل، 1، 3، 5، مثل:

 

GetProcAddress(Module, (LPCSTR) 1)
بدل:
GetProcAddress(Module, "add")

 

البحث عن الدالة أسرع، لكنها صعبة الإستخدام وقد لاتعمل إذا كان مطور المكتبة لايعتمد على الترتيب (أو لايعلم بوجوده).

3

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
  • 0

ماشاء الله عليك
اخي والله استفدت جدا من شرحك المفصل ماشاء الله .

جزاك الله عني كل خير ياأخي.

هكذا استفساراتي كلها انتهت وشكرا الك على المساعدة

0

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه
زوار
This topic is now closed to further replies.

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .