المساعد الشخصي الرقمي

مشاهدة النسخة كاملة : اتفاقيات الاتصال Calling Conventions مع المناهج


kachwahed
07-11-2011, 02:40 PM
السلام عليكم ورحمة الله
Calling Conventions أو "اتفاقيات الاتصال" (الترجمة مباشرة وحرفية وقد لا توحي بالمقصود)
يقصد بها طريقة تمرير المعلمات (Parameters) إلى المناهج (Routine)
http://en.wikipedia.org/wiki/Calling_convention
وهي في إصدارات دلفي 32bit خمسة شهيرة: register, pascal, cdecl, stdcall, safecall
مثال تمرير باستخدام cdecl:
function MyFunction(X, Y: Real): Real; cdecl;
من ملفات الـ Help في دلفي نستخلص ما يلي:
تحدد طرق تمرير الملعمات (الباراميترات):
1- ترتيب إرسال القيم (من اليمين إلى اليسار أو العكس)
2- إزالة (أو عدم إزالة) المغيرات من المكدس (Stack) بعد الخروج من المنهج
3- طريقة التعامل مع الاستثناءات وإصدار رسائل الأخطاء Exception
4- موضع تخزين المعلمات (في المكدس أو في سجلات الـ CPU)

التوجيه الافتراضي في دلفي هو register، إذا لم تكتب أي شيء فكأنك كتبت register بعد اسم المنهج.


التوجيه، ترتيب الإرسال، المسؤول عن حذف المتغيرات، موضع التخزين
register ، من اليسار إلى اليمين، المنهج نفسه، السجلات
pascal، من اليسار إلى اليمين، المنهج نفسه، المكدس
cdecl، من اليمين إلى اليسار، مستدعي المنهج، المكدس
stdcall، من اليمين إلى اليسار، المنهج نفسه، المكدس
safecall، من اليمين إلى اليسار، المنهج نفسه، المكدس

ملاحظات:
- يستخدم التوجيه register حتى ثلاث سجلات لتخزين المعلمات، وهو أسرع عند التنفيذ طبعا
- التوجيه safecall يستخدم لتوجيه رسائل الاستثناءات، في بيئة Windows يتم إشعار واجهات COM (التي تورث من IDispatch) لإثارة الاستثناءات
- عند التعامل مع API يستخدم عادة stdcall أو safecall
- توجيه pascal لا زال فعال لأغراض التوافقية (Compatibility)، في حين التوجيهات near و far و export لم تعد تعمل وتركت لنفس السبب (مخصصة لبيئة 16 bit).

انتهى.

Peter Below يشرح بشكل مماثل (بمثال) نفس الفكرة:
http://www.swissdelphicenter.ch/torry/showcode.php?id=1233

الآن شيء غريب!
أعتقد أن ترتيب تمرير المعلمات ينبغي أن يتم التفصيل فيه
صحيح إذا كتبنا:
function MyFunction(X, Y: Real): Real; register;
فإن تمرير المعلمات سيكون من اليسار إلى اليمين
أي: بدء بـ x ثم y وليس العكس
لكن هذا من حيث وصول القيم إلى MyFunction (أي دفعها إلى المكدس) ولكن ليس من حيث جلب قيم X و Y
يتضح ذلك إذا حاولنا مع دالتين تأتيان بقيمين X و Y
function MyFunction(getX(), getY(): Real): Real; register;
تخيل أن هناك أوامر تنفذ في getX() وgetY() أيهما سينفذ أولا!
الذي لاحظت أن جميع التوجهات يُؤتى بها من اليمين إلى اليسار (ما عدا pascal)
ثم توجه إلى المنهج كما هو مألوف
المثال في المرفق يوضح الفكرة.

إذا أخطأ في شيء فلا تترددوا بالتعقيب :)

mobile
07-11-2011, 05:31 PM
بارك الله فيك مشرفنا الغالي

كنت بصدد كتابة طلب شرح لمثل هذا الموضوع اي انواع الاتصلات واستحييت لكثرة طلباتي وعدم تقديم اي شيئ للاخوة في هذا المنتدى الغالي
هذا كان بسبب مشكلة حصلت لي في برنامجي الذي يتصل بمكتبة ديناميكية فيها عنصر edit ياتي هذا الفورم فوق الساعة لاكتب فيه ما **** ان ابحث عنه فيرسل للبرنامج كلمة البحث عن طريق stdcall لكن بعد عدة مرات او عندما اترك البرنامج لمدة لا ابحث فيه . ثم اعيد كتابة كلمة البحث تواجهني رسالة خطا فشككت في طريقة الربط اي بين المكتبة الديناميكية والبرنامج
علما اني اعيد تشغيل البرنامج فيشتغل عادي وبعد مدة زمنية لا يعمل
هل تظن ان stdcall هي السبب ؟

hanipino
07-11-2011, 07:58 PM
و عليكم السلام و رحمة الله .

الاخ كاش واحد , على حسب ما فهمت ان التوجيه register يعمل على تخزين 3 (الثلاث) متغيرات الاولى فى المسجلات بالترتيب التالى :

المتغير الاول فى EAX
المتغير الثانى فى EDX
المتغير الثالث فى ECX

اما باقى المتغيرات هى التى تدفع الى المكدس من اليسار الى اليمين .

كمثال .. الاجراء DefaultCall يصبح بهذا الشكل

procedure DefaultCall(EAX, EDX, ECX, O, T: Integer);register; // = register; Right to Left <---
begin
end;

الاستدعاء

DefaultCall(1, 2, 3, getOne, getTwo);


شكرا و بالتوفيق .

kachwahed
07-11-2011, 09:41 PM
هل تظن ان stdcall هي السبب ؟
لا أظن ذلك...
أعتقد أن الخطأ في اختيار طريقة التمرير الأحسن يسبب نتائج غير متوقعة أو خاطئة ولا يتسبب في الغالب في أخطاء...
أيضا التمرير بـ stdcall هو الأنسب في Win32 لأن الويندوز نفسه يستخدم هذا التمرير.

التى تدفع الى المكدس من اليسار الى اليمين .
بالضبط! كذلك تدفع إلى المكدس، وهذا هو المتفق عليه
غير أن الإتيان بها (أي البحث عن القيم التي ستخزن في السجلات eax, ebx, ecx) ولا أقصد تخزينها، يكون حسب ما لاحظت من اليمين إلى اليسار
أي هنا:
DefaultCall(1, 2, 3, getOne, getTwo);
سيقوم بتنفيذ getTwo قبل getOne
لاحظ المثال:
{$APPTYPE CONSOLE}
function getTwo: Integer;
begin Write('Two '); Result := 2; end;
function getOne: Integer;
begin Write('One '); Result := 1; end;
function getThree: Integer;
begin Write('Three '); Result := 3; end;
procedure RegisterCall(eax, ebc, ecx: Integer); register;
begin end;
begin
RegisterCall(getOne, getTwo, getThree);
Readln;
end.
http://www.delphi4arab.com/forum/attachment.php?attachmentid=3070&stc=1&d=1320701641
كيف تفسر النتائج؟

mobile
08-11-2011, 12:11 AM
هذه هي الرسالة التي يصدرها بعد حوالي 6 دقائق من العمل بالبرنامج

Access violation at address 02DB66CC. Read of address 02DB66CC.


وهذه هي العملية التي يبعثها dll الى البرنامج
procedure sendtex(stext:ShortString) ;stdcall;external '.\GSmob.exe'

hanipino
08-11-2011, 10:51 AM
نعم اخ كاش واحد اذا كان الاجراء ذو التوجيه register و يحتوى على ثلاث متغيرات او اقل فلا يهم الاتجاه الذى ياتى بهم (مع انه من اليمين الى ليسار) المهم السجلات التى ستحفض تلك القيم . بعد الثلاث متغيرات الاولى سيدفع ل المكدس من اليسار الى اليمين .
اى الشىء المهم فى الاتجاه هو عند الدفع الى المكدس و الله اعلم .
مثلا كل التوجيهات الاخرى pascal, cdecl ... الخ تعمل على دفع المتغيرات الى المكدس باتجاه معين دون تخزين اى شىء فى المسجل
عكس التوجيه register ( :) حتى اسمه يدل عليه) هذا التوجيه يخزن 3 متغيرات الاولى فى المسجلات . و بعد الثلاث متغيرات هاته تاتى اهمية الاتجاه لانه سيدفعها الى المكدس .

me&delphi
08-11-2011, 11:09 AM
و عليكم السلام و رحمة الله وبركاته

stdcall، من اليمين إلى اليسار، المنهج نفسه، المكدس

اغلب دوال API مكتوبة بلغة C وتعتمد على هدا النوع من Calling ولهدا يتم دفع البارمترات في المكدس بشكل معكوس

و لنزيد الأمر أكثر وضوحا هدا مثال لدالة يعرفها الجميع MessageBoxA
Delphi+ASM

procedure TForm1.FormCreate(Sender: TObject);
var Cap, Txt: string;
begin
Txt := 'Calling Conventions';
Cap := 'SdtCall';
asm
push 64 //uType ->Style
push Txt //Title
push cap //Text
push 0 //handle
call MessageBoxA
end;
end;

أنظر كيف تم دفع البارمترات البارمتر الأول مقبض النافدة هو آخر ماتم دفعه للمكدس (push 0 //handle)

kachwahed
14-11-2011, 07:26 PM
ملاحظة: التوجيه forward يستخدم لأجل تعريف لاحق في قسم implementation (لأن ذلك غير ممكن فرضا)
مثال:
var
b: bool = false;

implementation

procedure test; forward;

procedure callTest();
begin
if b then
test;
end;

procedure test();
begin
callTest;
b := false;
end;

قد تجد في بعض المناهج عبارة nil = وهي فقط من أجل تهيئة متغيرات المناهج بقيمة افتراضية:
var
p: procedure = nil;

بإضافة توجيه تصبح:
p: procedure; stdcall = nil;
بالتوفيق.

kachwahed
09-03-2012, 10:46 AM
التوجيهان assembler وinline ما تركا إلا لأغراض التوافق مع الإصدارات القديمة
ويتم إلغاؤها عند التجميع (ِCompile)، هكذا جاء في Help دلفي 7...

غير أن كتابة التوجيه inline يعد خطء نحوي، رغم وجوده حتى قبل إصدارات دلفي 2005!
والدليل أن الكلمة محجوزة ولونها مغاير:
inline: char; //< -- ERROR
يؤكد Rudy Velthuis أنها محجوزة من أيضا Turbo Pascal حيث كان لأغراض أخرى آنذاك، ولا معنى لها في دلفي 3 أيضا
ويظن أنها بقيت inline لاستعمال لاحق (دلفي 2005 فما فوق (http://www.delphi4arab.com/forum/showthread.php?t=4202))

ترتيب الارسال عند assembler من اليمين إلى اليسار
procedure AssemblerCall(i, j: Integer); assembler// Right to Left <---