اتفاقيات الاستدعاء Calling Conventions مع المناهج
#1
السلام عليكم ورحمة الله
Calling Conventions أو "اتفاقيات الاستدعاء" (الترجمة مباشرة وحرفية وقد لا توحي بالمقصود)
يقصد بها طريقة تمرير المعلمات (Parameters) إلى المناهج (Routine)
كود :
http://en.wikipedia.org/wiki/Calling_convention
وهي في إصدارات دلفي 32bit خمسة شهيرة: register, pascal, cdecl, stdcall, safecall
مثال تمرير باستخدام cdecl:
كود :
function MyFunction(X, Y: Real): Real; [color=Blue]cdecl[/color];
من ملفات الـ 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

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

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


الملفات المرفقة
.7z   CallingConventions.7z (الحجم : 531 بايت / التحميلات : 74)
الرد
#2
بارك الله فيك مشرفنا الغالي

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

الاخ كاش واحد , على حسب ما فهمت ان التوجيه 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);

شكرا و بالتوفيق .
الرد
#4
إقتباس :هل تظن ان stdcall هي السبب ؟
لا أظن ذلك...
أعتقد أن الخطأ في اختيار طريقة التمرير الأحسن يسبب نتائج غير متوقعة أو خاطئة ولا يتسبب في الغالب في أخطاء...
أيضا التمرير بـ stdcall هو الأنسب في Win32 لأن الويندوز نفسه يستخدم هذا التمرير.

إقتباس :التى تدفع الى المكدس من اليسار الى اليمين .
بالضبط! كذلك تدفع إلى المكدس، وهذا هو المتفق عليه
غير أن الإتيان بها (أي البحث عن القيم التي ستخزن في السجلات eax, ebx, ecx) ولا أقصد تخزينها، يكون حسب ما لاحظت من اليمين إلى اليسار
أي هنا:
كود :
DefaultCall(1, 2, 3, getOne, getTwo);
سيقوم بتنفيذ getTwo قبل getOne
لاحظ المثال:
PHP كود :
{$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. 
[صورة: attachment.php?attachmentid=3070&stc=1&d=1320701641]
كيف تفسر النتائج؟


الملفات المرفقة
.jpg   RegisterCall.JPG (الحجم : 28.02 ك ب / التحميلات : 182)
الرد
#5
هذه هي الرسالة التي يصدرها بعد حوالي 6 دقائق من العمل بالبرنامج

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


وهذه هي العملية التي يبعثها dll الى البرنامج
procedure sendtex(stext:ShortString) ;stdcall;external '.\GSmob.exe'
لأستهلنّ الصعب أو أدرك المنى    فما انقادت الآمال إلاّ لصابر
الرد
#6
نعم اخ كاش واحد اذا كان الاجراء ذو التوجيه register و يحتوى على ثلاث متغيرات او اقل فلا يهم الاتجاه الذى ياتى بهم (مع انه من اليمين الى ليسار) المهم السجلات التى ستحفض تلك القيم . بعد الثلاث متغيرات الاولى سيدفع ل المكدس من اليسار الى اليمين .
اى الشىء المهم فى الاتجاه هو عند الدفع الى المكدس و الله اعلم .
مثلا كل التوجيهات الاخرى pascal, cdecl ... الخ تعمل على دفع المتغيرات الى المكدس باتجاه معين دون تخزين اى شىء فى المسجل
عكس التوجيه register ( Smile حتى اسمه يدل عليه) هذا التوجيه يخزن 3 متغيرات الاولى فى المسجلات . و بعد الثلاث متغيرات هاته تاتى اهمية الاتجاه لانه سيدفعها الى المكدس .
الرد
#7
و عليكم السلام و رحمة الله وبركاته

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

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

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

PHP كود :
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)
لا اله الا أنت سبحانك اني كنت من الضالمين
الرد
#8
ملاحظة: التوجيه forward يستخدم لأجل تعريف لاحق في قسم implementation (لأن ذلك غير ممكن فرضا)
مثال:
كود :
var b: bool = false; implementation procedure test; [color=Red]forward[/color]; procedure callTest(); begin if b then test; end; procedure test(); begin callTest; b := false; end;

قد تجد في بعض المناهج عبارة nil = وهي فقط من أجل تهيئة متغيرات المناهج بقيمة افتراضية:
PHP كود :
var
  
p: procedure = nil; 
بإضافة توجيه تصبح:
PHP كود :
p: procedure; stdcall = nil; 
بالتوفيق.
الرد
#9
التوجيهان assembler وinline ما تركا إلا لأغراض التوافق مع الإصدارات القديمة
ويتم إلغاؤها عند التجميع (ِCompile)، هكذا جاء في Help دلفي 7...

غير أن كتابة التوجيه inline يعد خطء نحوي، رغم وجوده حتى قبل إصدارات دلفي 2005!
والدليل أن الكلمة محجوزة ولونها مغاير:
كود :
[color=blue]inline[/color][color=Black]: char;[/color] //< -- [color=Red]ERROR[/color]
يؤكد Rudy Velthuis أنها محجوزة من أيضا Turbo Pascal حيث كان لأغراض أخرى آنذاك، ولا معنى لها في دلفي 3 أيضا
ويظن أنها بقيت inline لاستعمال لاحق (دلفي 2005 فما فوق)

ترتيب الارسال عند assembler من اليمين إلى اليسار
PHP كود :
procedure AssemblerCall(i, j: Integer); assembler// Right to Left <--- 
اللهم احقن دماء المسلمين، لا تنسوهم بالدعاء...
الرد


التنقل السريع :


يقوم بقرائة الموضوع: بالاضافة الى ( 1 ) ضيف كريم