TAHA
04-07-2010, 01:50 PM
با سلام
راستش منبع این مقاله رو یادم نیست برای همین اول مقاله عذر خواهی میکنم.
استفاده از کد سی در دلفی
انواع فراخواني توابع
توابع در يک زبان برنامه نويسي بسته به روش فرستادن آرگومانها به تابع، جهت قرار گرفتن آرگومانها در پشته و يا رجيسترها و نوع پاکسازي پشته بعد از پايان تابع، به چند دسته تقسيم ميشوند.:
1. register
در اين نوع براي فرستادن آرگومانها به تابع از ثباتهاي حافظه (رجيسترها) استفاده ميشود. در اين حالت آرگومانها از چپ به راست در رجيسترها قرار گرفته و به تابع پاس داده ميشوند. اين نوع توابع خودشان بعد از پايان کار فضا و پشته را خالي ميکنند. نوع فراخواني پيشفرض براي دلفي حالت register است.
2. pascal (__pascal در C)
در اين نوع، آرگومانها از طريق پشته (Stack ) به تابع فرستاده ميشوند، آرگومانها از چپ به راست در پشته قرار ميگيرند و تابع بعد از پايان کار و پيش از بازگشت پشته را خالي ميکند. اين حالت صرفا براي پشتيباني از کدهاي قديمي هنوز پشتيباني ميشود.
3. cdecl ( __cdecl در C )
آرگومانهاي اين نوع، از طريق پشته به تابع فرستاده ميشوند و از راست به چپ در پشته قرار ميگيرند. تابع بعد از پايان کار پشته را خالي نميکند و صدازننده تابع بايست اين کار را انجام دهد. حالت پيشفرض در C و براي توابع سيستمي براي سيستم عامل هاي غير ويندوز همين نوع است.
4. stdcall (__stdcall در C )
آرگومانها از طريق پشته و از راست به چپ به تابع فرستاده ميشوند. تابع بعد از پايان کار پشته را خالي ميکند. کليه API هاي سيستم عامل ويندوز از اين نوع هستند.
5. safecall (__safecall در C )
اين نوع فراخواني دقيقا شبيه حالت stdcall است با اين تفاوت که در برابر خطاهاي اتفاق افتاده در تابع مثل يک ديوار عمل ميکند و خطا را (در صورتي که در تابع تله گذاري نشده باشد ) به صدا زننده تابع برنميگرداند و معمولا براي پياده سازي تکنولژي COM از آن استفاده ميشود. در این نوع تابع، بازگشتی به صورت آرگومان آخر فرستاده میشود (به عبارتی یک آرگومان به تابع اضافه میشود ) و بازگشتی اصلی به صورت HRESULT میشود که در صورت موفقیت کد S_OK و در غیر اینصورت خطای اتفاق افتاده را باز میگرداند.
فراخواني تابعي از يک نوع به نوع ديگر باعث خطاهايي خواهد شد که تشخيص آن هنگام برنامه نويسي نسبتا مشکل است.
البته منظور از طريقه پاکسازي پشته يا طريقه فرستادن آرگومانها اين نيست که برنامه نويس بايستي خودش اين کار را انجام دهد، کامپايلر هنگام کامپايل عمليات لازم مربوط به اين مراحل را به کد اضافه ميکند.
حالتهاي توصيه شده براي توابع در ويندوز stdcall و safecall هستند. براي مشخص کردن نوع تابع در دلفي کافي است يک از کليدواژه هاي معرفي شده را در پايان معرفي تابع قرار دهيم :
function MyFunc(Arg1 : Integer ; Arg2 : Integer):Integer;stdcall;
در C بايستي نوع تابع را قبل از نام تابع و بعد از نوع بازگشتي مشخص کنيم :
int __stdcall myfunc (int arg1, int arg2 );
همانطور که گفته شد دلفي توابع بدون مشخصه را register و C آنها را cdecl در نظر ميگيرد.
کامپايلر هاي C معمولا هنگام کامپايل يک علامت زيرخط (_) به آغاز نام توابع cdecl اضافه ميکنند و نام توابع نوع pascal تماما به حروف بزرگ تبديل ميشود.
کامپايل فايلهاي C توسط Borland C++ Builder به فايلهاي Obj
طريقه کامپايل فايل هاي c و تبديل آنها به فايلهاي obj بدون ايجاد فايل نهايي بسيار ساده است. دستوري شبيه زير را از طريق خط فرمان يا يک Batch فايل اجرا کنيد
BCC32.EXE [options] file1.c file2.c …
به جاي BCC32.EXE آدرس کامل اين فايل (که در پوشه bin در مکان نصب C++ Builder قرار دارد) را بنويسيد و به جاي options انتخابهايي را که چندتايي از آنها در ادامه معرفي ميشوند و بالاخره در انتها ليست فايلهايي که ميخواهيد کامپايل کنيد را بنويسيد. مثلا :
C:BorlandBCC55BinBCC32.EXE -c -X -v -u- test1.c test2.c
حروف کوچک و بزرگ در مورد انتخابها (options ) مهم هستند اما در مورد نام فايلها نه. هر انتخاب با يک علامت منفي (-) در سمت چپ مشخص ميشود و گذاشتن يک منفي در سمت راست به معني حذف اين انتخاب از ليست انتخابهاست.بعضي option ها که مورد نياز هستند را در اينجا توضيخ ميدهيم. باقي اين موارد را ميتوانيد در راهنمايC++ Builder ببينيد.
-c : اين انتخاب به کامپايلر اعلام ميکند که بعد از پايان کامپايل Linker را فراخواني نکند و به جاي ساختن فايل نهايي (فايل exe يا dll و يا... ) فقط فايلهاي Obj را بسازد.
-X : اين انتخاب باعث ميشود که کامپايلر طلاعات مربوط به فايلهاي سرآمد (Header فايلهاي C ) را به فايل Obj اضافه نکند. در حالت عادي کامپايلر اين کار را ميکند. دلفي نيازي به اين اطلاعات ندارد و ان انتخاب حجم فايلهاي Obj را کم ميکند.
-v :اين انتخاب اطلاعات مربوط به Debug را نيز به Obj فايل اضافه ميکند. اين به شما اجازه ميدهد هنگام تست برنامه در دلفي و Trace آن از طريق کليد F7 کد C را هم در محيط دلفي دنبال کنيد. (البته در صورتي که اصل فايلها به زبان C موجود باشد و شما تنها Obj فايلها را نداشته باشيد.) اين انتخاب اندازه فايلهاي Obj را تا خد زيادي افزايش ميدهد و تنها براي زماني مناسب است که برنامه در مرحله تست و خطايابي است. در هنگام انتشار بهتر است اين انتخاب هنگام کامپايل برداشته شود.
-u- : هنگام کامپايل، کامپايلر C++ Builder يک علامت زيرخط (_) به توابع با نوع فراخواني cdecl اضافه ميکند (حالت پيشفرض -u است) شما با گذاشتن يک منفي در سمت راست ميتوانيد اين گزينه را غير فعال کنيد. در اين صورت کليه توابع cdecl با نام خودشان در فايل Obj قرار ميگيرند.
-pr : نوع فراخواني پيشفرض توابع را از cdecl به register تبديل ميکند. در اين حالت تمام توابعي که نوع فراخواني آنها مشخص نيست به نوع register تبديل ميشوند.
افرودن فايلهاي Obj به کد دلفي
به دليل شباهت لينکرهاي C++ Builder و Delphi براي افزودن يک فايل Obj کامپايل شده توسط C++ Builder - و البته خود دلفي - به دلفي ميتوانيد از دستور پيش پردازنده $L استفاده کنيد. مثلا {$L fileName.obj} و سپس کافي است کليه توابعي که در فايل Obj هستند و نيازمند استفاده از آنها در C هستيد را با يک اعلان پيشرو و به صورت external تعريف کنيد. در اين حالت توابع مورد استفاده که با کامپايلر C++ Builder کامپايل شده اند با لينکر دلفي به برنامه لينک ميشوند.نمونه اين کار ببينيد:
کد سي :
1. /*
2. Link C File in delphi
3. Author : F0ruD
4. */
5. char* __cdecl strncpy(char* dest,char* source,int count);
6.
7. void __stdcall FillBuf ( char *buf , int len)
8. {
9. const char msg[]="this is a message from c code";
10. int i;
11. i=(strlen(msg) > len?len:strlen(msg));
12. strncpy(buf,msg,i);
13. }
کد دلفي :
1. unit uLink;
2.
3. interface
4. uses SysUtils, Dialogs;
5.
6. procedure TestLink;
7. implementation
8. //Link obj file to program
9. {$L test.obj}
10.
11. procedure FillBuf(Buf: PChar; Len: Integer); stdcall; external;
12.
13. //This function called in C program. But there is no
14. //definition of this functionin C file.
15. //So linker search Delphi code for this
16. //function. It meens we need Define this function.
17.
18. function _strncpy(Dest: PChar; Source: PChar; Count: Integer): PChar; cdecl;
19. begin
20. Result := StrLCopy(Dest, Source, Count);
21. end;
22.
23. function _strlen(Str: PChar): Integer; cdecl;
24. begin
25. Result := StrLen(Str);
26. end;
27.
28. procedure TestLink;
29. var
30. Buf : array[0..124] of Char;
31. begin
32. FillChar(Buf, SizeOf(Buf), 0);
33. FillBuf(Buf, 124);
34. ShowMessage(Buf);
35. end;
36.
37.
38. end.
39.
40.
کليه دستورات کليدي C از قبيل for يا while ... بدون هيچ محدوديتي مجاز هستند. تمام قوانين مربوط به آرايه ها، اشاره گرها و... نيز بايستي بدرستي رعايت شوند. اما کامپايلر هنگام کامپايل نيازي به اعلان پيشروي توابع ندارد. در صورتي که در زبان C از هر تابعي که استفاده ميشود يا بايد در يک فايل سرآمد در برنامه include شده باشد يا اين که در همان برنامه تعريف شده باشد. اگر بعد از جايي که از آن تابع استفاده ميشود تعريف شده باشد يک اعلان پيشرو قبل از استفاده از تابع لازم است.
اما هنگام کامپايل وقتي کامپايلر به فراخواني يک تابع برسد کليه هدرفايلها و قسمت بالايي جايي را که به تابع رسيده دنبال خود تابع يا اعلان پيشرو آن ميگردد. در صورتي که اعلان پيشرو يا خود تابع را پيدا کند نوع فراخواني آن را در نظر گرفته و تابع را با نوع فراخواني در آن نقطه صدا ميزند. اما در صورتي که به هيچکدام از اين دو (اعلان پيشرو يا خود تابع) برخورد نکند تابع را با نوع پيشفرض (در حالت عادي cdecl مگر اينکه با انتخابي شبيه -pr حالت پيشفرض عوض شده باشد) صدا ميزند ولي خطايي مبني بر پيدا نکردن تابع اعلام نميکند.در اصل کامپايلر فرض ميکند که تابع هنگام لينک پيدا ميشود و در هنگام لينک، لينکر اين مساله را بررسي خواهد کرد.
در مثال بالا توابع strlen و strncpy در برنامه C صدا زده شده اند. هيچ هدر فايلي در برنامه اضافه نشده (اين باعث ميشود که برنامه در C قابل اجرا نباشد و اين به عمد صورت گرفته است. در صورتي که معمولا برنامه ها طوري نوشته ميشوند که در C قابل اجرا باشند.) وتنها تابع strncpy با اعلان پيشرو در برنامه اعلان شده است. در اين مثال کامپايلر هنگام رسيدن به تابع strlen چون آن را در بالاتر يا در هيچ هدر فايلي پيدا نميکند به صورت cdecl در نظر ميگيرد. تابع strncpy هم با اعلان cdecl به صورت پيشرو تعريف شده است بنابراين کامپايلر آن را هم از اين نوع در نظر ميگيرد.
در صورتي که برنامه سي را با انتخاب -pr کامپايل کنيم تابع strlen به صورت register تعريف ميشود اما تابع strncpy به علت وجود اعلان پيشرو همچنان به صورت cdecl تعريف ميشود. دقت کنيد در صورتي که از هدرهاي استاندارد سي استفاده ميکنيد نوع فراخواني توابع را در نظر داشته باشيد.(معمولا cdecl )
هنگامي که اين کد را با فايل دلفي لينک ميکنيم لينکر دلفي بدنبال توابع _strncpy و _strlen ميگردد. و ما بايستي آنها را در کد دلفي و بعد از جايي که کد به برنامه لينک شده تعريف کنيم. (به تعريف اين دو تابع در کد دلفي توجه کنيد) اين زيرخط تضمين ميکند که توابع همنام در دلفي و سي (مثل strlen و توابع مشابه ديگر ) با هم اشتباه گرفته نميشود. تعريف اين توابع مشابه در محيط سي به فرمي غير از cdecl ميتواند باعث خطاهاي ناخواسته و ناخوشايندي شود. مثلا با تعريف اين تابع به صورت پيشرو و از نوع stdcall باعث ميشود که لينکر دلفي به دنبال تابع strlen بگردد و اين تابع را در کتابخانه هاي use شده در محيط دلفي ميابد. در صورتي که اين توابع کتابخانه اي از نوع register هستند که اين باعث خطاي زمان اجرا (و نه خطاي زمان کامپايل ) ميشود. تنها راه مناسب اين است که برنامه c را با گزينه -pr کامپايل کنيم تا توابع همنام در دلفي و سي به جاي هم استفاده شوند يعني با کامپايل کد سي در مثال شما نيازمند دوباره نويسي تابع strlen نخواهيد بود اما تابع strncpy چون به صورت پيشرو تعريف شده و هم اينکه در کتابخانه دلفي با تابع مشابه خود همنام نيست بايستي دوباره در دلفي نوشته شود مگر اينکه در کد سي از تابع strlcopy استفاده شود که گر چه در کتابخانه سي نيست اما هنگام الحاق به دلفي مشکلي پيش نمي آيد. از گزينه -pr بهتر است در حالت عادي استفاده نشود چون ميتواند مشکلاتي را در مورد توابع همنام يا آرگومانها و يا ترتيب آرگومان غير مشابه بوجود بياورد.
افزودن چند فايل Obj به برنامه
نمونه زير راببينيد :
فايل test1.c
1. /*
2. Link C File in delphi
3. Author : F0ruD
4. */
5. void __stdcall FillBuf(char *buf,char ch,int len)
6. {
7. int i;
8. for (i=0;i<len;i++)
9. *(buf+i)=ch;
10. *(buf+len)=0;
11. }
فايل test1.h
1. #ifndef _TEST1_H_
2. # define _TEST1_H_
3.
4. void __stdcall FillBuf(char *,char,int);
5.
6. #endif
7.
فايل test2.c
1. #include "test1.h"
2.
3. void __stdcall ZeroBuf(char *buf,int len)
4. {
5. FillBuf(buf,0,len);
6. }
کد دلفي :
1. unit uLink;
2.
3. interface
4. uses SysUtils, Dialogs;
5.
6. procedure TestLink;
7. implementation
8.
9. procedure ZeroBuf(Buf: PChar; Len: Integer); stdcall; external;
10. //Link obj files to program
11. {$L test2.obj}
12. {$L test1.obj}
13.
14.
15.
16. procedure TestLink;
17. var
18. Buf : array[0..124] of Char;
19. begin
20. Buf := 'This message must fill with 0';
21. ZeroBuf(Buf, 124);
22. ShowMessage(Buf);
23. end;
24.
25.
26. end.
27.
28.
اين مثال نشان ميدهد که چگونه ميتوان از توابع نوشته شده در يک فايل ديگر c در يک فايل ديگر استفاده کرد. وجود فايل هدر test1.h صرفا براي اعلان پيشروي تابع استفاده شده و به اين صورت کامپايلر هنگام کامپايل نوع تابع FillBuf را به صورت cdecl در نظر گرفته است. البته تابع FillBuf نيز در برنامه دلفي قابل استفاده است.
در صورتي که ترتيب لينک شدن اين دو فايل را عوض کنيم با خطا روبرو خواهيم شد چرا که لينکر دلفي بعد از لينک در قسمت پاييني کد دنبال تابع FillBuf ميگردد.
راستش منبع این مقاله رو یادم نیست برای همین اول مقاله عذر خواهی میکنم.
استفاده از کد سی در دلفی
انواع فراخواني توابع
توابع در يک زبان برنامه نويسي بسته به روش فرستادن آرگومانها به تابع، جهت قرار گرفتن آرگومانها در پشته و يا رجيسترها و نوع پاکسازي پشته بعد از پايان تابع، به چند دسته تقسيم ميشوند.:
1. register
در اين نوع براي فرستادن آرگومانها به تابع از ثباتهاي حافظه (رجيسترها) استفاده ميشود. در اين حالت آرگومانها از چپ به راست در رجيسترها قرار گرفته و به تابع پاس داده ميشوند. اين نوع توابع خودشان بعد از پايان کار فضا و پشته را خالي ميکنند. نوع فراخواني پيشفرض براي دلفي حالت register است.
2. pascal (__pascal در C)
در اين نوع، آرگومانها از طريق پشته (Stack ) به تابع فرستاده ميشوند، آرگومانها از چپ به راست در پشته قرار ميگيرند و تابع بعد از پايان کار و پيش از بازگشت پشته را خالي ميکند. اين حالت صرفا براي پشتيباني از کدهاي قديمي هنوز پشتيباني ميشود.
3. cdecl ( __cdecl در C )
آرگومانهاي اين نوع، از طريق پشته به تابع فرستاده ميشوند و از راست به چپ در پشته قرار ميگيرند. تابع بعد از پايان کار پشته را خالي نميکند و صدازننده تابع بايست اين کار را انجام دهد. حالت پيشفرض در C و براي توابع سيستمي براي سيستم عامل هاي غير ويندوز همين نوع است.
4. stdcall (__stdcall در C )
آرگومانها از طريق پشته و از راست به چپ به تابع فرستاده ميشوند. تابع بعد از پايان کار پشته را خالي ميکند. کليه API هاي سيستم عامل ويندوز از اين نوع هستند.
5. safecall (__safecall در C )
اين نوع فراخواني دقيقا شبيه حالت stdcall است با اين تفاوت که در برابر خطاهاي اتفاق افتاده در تابع مثل يک ديوار عمل ميکند و خطا را (در صورتي که در تابع تله گذاري نشده باشد ) به صدا زننده تابع برنميگرداند و معمولا براي پياده سازي تکنولژي COM از آن استفاده ميشود. در این نوع تابع، بازگشتی به صورت آرگومان آخر فرستاده میشود (به عبارتی یک آرگومان به تابع اضافه میشود ) و بازگشتی اصلی به صورت HRESULT میشود که در صورت موفقیت کد S_OK و در غیر اینصورت خطای اتفاق افتاده را باز میگرداند.
فراخواني تابعي از يک نوع به نوع ديگر باعث خطاهايي خواهد شد که تشخيص آن هنگام برنامه نويسي نسبتا مشکل است.
البته منظور از طريقه پاکسازي پشته يا طريقه فرستادن آرگومانها اين نيست که برنامه نويس بايستي خودش اين کار را انجام دهد، کامپايلر هنگام کامپايل عمليات لازم مربوط به اين مراحل را به کد اضافه ميکند.
حالتهاي توصيه شده براي توابع در ويندوز stdcall و safecall هستند. براي مشخص کردن نوع تابع در دلفي کافي است يک از کليدواژه هاي معرفي شده را در پايان معرفي تابع قرار دهيم :
function MyFunc(Arg1 : Integer ; Arg2 : Integer):Integer;stdcall;
در C بايستي نوع تابع را قبل از نام تابع و بعد از نوع بازگشتي مشخص کنيم :
int __stdcall myfunc (int arg1, int arg2 );
همانطور که گفته شد دلفي توابع بدون مشخصه را register و C آنها را cdecl در نظر ميگيرد.
کامپايلر هاي C معمولا هنگام کامپايل يک علامت زيرخط (_) به آغاز نام توابع cdecl اضافه ميکنند و نام توابع نوع pascal تماما به حروف بزرگ تبديل ميشود.
کامپايل فايلهاي C توسط Borland C++ Builder به فايلهاي Obj
طريقه کامپايل فايل هاي c و تبديل آنها به فايلهاي obj بدون ايجاد فايل نهايي بسيار ساده است. دستوري شبيه زير را از طريق خط فرمان يا يک Batch فايل اجرا کنيد
BCC32.EXE [options] file1.c file2.c …
به جاي BCC32.EXE آدرس کامل اين فايل (که در پوشه bin در مکان نصب C++ Builder قرار دارد) را بنويسيد و به جاي options انتخابهايي را که چندتايي از آنها در ادامه معرفي ميشوند و بالاخره در انتها ليست فايلهايي که ميخواهيد کامپايل کنيد را بنويسيد. مثلا :
C:BorlandBCC55BinBCC32.EXE -c -X -v -u- test1.c test2.c
حروف کوچک و بزرگ در مورد انتخابها (options ) مهم هستند اما در مورد نام فايلها نه. هر انتخاب با يک علامت منفي (-) در سمت چپ مشخص ميشود و گذاشتن يک منفي در سمت راست به معني حذف اين انتخاب از ليست انتخابهاست.بعضي option ها که مورد نياز هستند را در اينجا توضيخ ميدهيم. باقي اين موارد را ميتوانيد در راهنمايC++ Builder ببينيد.
-c : اين انتخاب به کامپايلر اعلام ميکند که بعد از پايان کامپايل Linker را فراخواني نکند و به جاي ساختن فايل نهايي (فايل exe يا dll و يا... ) فقط فايلهاي Obj را بسازد.
-X : اين انتخاب باعث ميشود که کامپايلر طلاعات مربوط به فايلهاي سرآمد (Header فايلهاي C ) را به فايل Obj اضافه نکند. در حالت عادي کامپايلر اين کار را ميکند. دلفي نيازي به اين اطلاعات ندارد و ان انتخاب حجم فايلهاي Obj را کم ميکند.
-v :اين انتخاب اطلاعات مربوط به Debug را نيز به Obj فايل اضافه ميکند. اين به شما اجازه ميدهد هنگام تست برنامه در دلفي و Trace آن از طريق کليد F7 کد C را هم در محيط دلفي دنبال کنيد. (البته در صورتي که اصل فايلها به زبان C موجود باشد و شما تنها Obj فايلها را نداشته باشيد.) اين انتخاب اندازه فايلهاي Obj را تا خد زيادي افزايش ميدهد و تنها براي زماني مناسب است که برنامه در مرحله تست و خطايابي است. در هنگام انتشار بهتر است اين انتخاب هنگام کامپايل برداشته شود.
-u- : هنگام کامپايل، کامپايلر C++ Builder يک علامت زيرخط (_) به توابع با نوع فراخواني cdecl اضافه ميکند (حالت پيشفرض -u است) شما با گذاشتن يک منفي در سمت راست ميتوانيد اين گزينه را غير فعال کنيد. در اين صورت کليه توابع cdecl با نام خودشان در فايل Obj قرار ميگيرند.
-pr : نوع فراخواني پيشفرض توابع را از cdecl به register تبديل ميکند. در اين حالت تمام توابعي که نوع فراخواني آنها مشخص نيست به نوع register تبديل ميشوند.
افرودن فايلهاي Obj به کد دلفي
به دليل شباهت لينکرهاي C++ Builder و Delphi براي افزودن يک فايل Obj کامپايل شده توسط C++ Builder - و البته خود دلفي - به دلفي ميتوانيد از دستور پيش پردازنده $L استفاده کنيد. مثلا {$L fileName.obj} و سپس کافي است کليه توابعي که در فايل Obj هستند و نيازمند استفاده از آنها در C هستيد را با يک اعلان پيشرو و به صورت external تعريف کنيد. در اين حالت توابع مورد استفاده که با کامپايلر C++ Builder کامپايل شده اند با لينکر دلفي به برنامه لينک ميشوند.نمونه اين کار ببينيد:
کد سي :
1. /*
2. Link C File in delphi
3. Author : F0ruD
4. */
5. char* __cdecl strncpy(char* dest,char* source,int count);
6.
7. void __stdcall FillBuf ( char *buf , int len)
8. {
9. const char msg[]="this is a message from c code";
10. int i;
11. i=(strlen(msg) > len?len:strlen(msg));
12. strncpy(buf,msg,i);
13. }
کد دلفي :
1. unit uLink;
2.
3. interface
4. uses SysUtils, Dialogs;
5.
6. procedure TestLink;
7. implementation
8. //Link obj file to program
9. {$L test.obj}
10.
11. procedure FillBuf(Buf: PChar; Len: Integer); stdcall; external;
12.
13. //This function called in C program. But there is no
14. //definition of this functionin C file.
15. //So linker search Delphi code for this
16. //function. It meens we need Define this function.
17.
18. function _strncpy(Dest: PChar; Source: PChar; Count: Integer): PChar; cdecl;
19. begin
20. Result := StrLCopy(Dest, Source, Count);
21. end;
22.
23. function _strlen(Str: PChar): Integer; cdecl;
24. begin
25. Result := StrLen(Str);
26. end;
27.
28. procedure TestLink;
29. var
30. Buf : array[0..124] of Char;
31. begin
32. FillChar(Buf, SizeOf(Buf), 0);
33. FillBuf(Buf, 124);
34. ShowMessage(Buf);
35. end;
36.
37.
38. end.
39.
40.
کليه دستورات کليدي C از قبيل for يا while ... بدون هيچ محدوديتي مجاز هستند. تمام قوانين مربوط به آرايه ها، اشاره گرها و... نيز بايستي بدرستي رعايت شوند. اما کامپايلر هنگام کامپايل نيازي به اعلان پيشروي توابع ندارد. در صورتي که در زبان C از هر تابعي که استفاده ميشود يا بايد در يک فايل سرآمد در برنامه include شده باشد يا اين که در همان برنامه تعريف شده باشد. اگر بعد از جايي که از آن تابع استفاده ميشود تعريف شده باشد يک اعلان پيشرو قبل از استفاده از تابع لازم است.
اما هنگام کامپايل وقتي کامپايلر به فراخواني يک تابع برسد کليه هدرفايلها و قسمت بالايي جايي را که به تابع رسيده دنبال خود تابع يا اعلان پيشرو آن ميگردد. در صورتي که اعلان پيشرو يا خود تابع را پيدا کند نوع فراخواني آن را در نظر گرفته و تابع را با نوع فراخواني در آن نقطه صدا ميزند. اما در صورتي که به هيچکدام از اين دو (اعلان پيشرو يا خود تابع) برخورد نکند تابع را با نوع پيشفرض (در حالت عادي cdecl مگر اينکه با انتخابي شبيه -pr حالت پيشفرض عوض شده باشد) صدا ميزند ولي خطايي مبني بر پيدا نکردن تابع اعلام نميکند.در اصل کامپايلر فرض ميکند که تابع هنگام لينک پيدا ميشود و در هنگام لينک، لينکر اين مساله را بررسي خواهد کرد.
در مثال بالا توابع strlen و strncpy در برنامه C صدا زده شده اند. هيچ هدر فايلي در برنامه اضافه نشده (اين باعث ميشود که برنامه در C قابل اجرا نباشد و اين به عمد صورت گرفته است. در صورتي که معمولا برنامه ها طوري نوشته ميشوند که در C قابل اجرا باشند.) وتنها تابع strncpy با اعلان پيشرو در برنامه اعلان شده است. در اين مثال کامپايلر هنگام رسيدن به تابع strlen چون آن را در بالاتر يا در هيچ هدر فايلي پيدا نميکند به صورت cdecl در نظر ميگيرد. تابع strncpy هم با اعلان cdecl به صورت پيشرو تعريف شده است بنابراين کامپايلر آن را هم از اين نوع در نظر ميگيرد.
در صورتي که برنامه سي را با انتخاب -pr کامپايل کنيم تابع strlen به صورت register تعريف ميشود اما تابع strncpy به علت وجود اعلان پيشرو همچنان به صورت cdecl تعريف ميشود. دقت کنيد در صورتي که از هدرهاي استاندارد سي استفاده ميکنيد نوع فراخواني توابع را در نظر داشته باشيد.(معمولا cdecl )
هنگامي که اين کد را با فايل دلفي لينک ميکنيم لينکر دلفي بدنبال توابع _strncpy و _strlen ميگردد. و ما بايستي آنها را در کد دلفي و بعد از جايي که کد به برنامه لينک شده تعريف کنيم. (به تعريف اين دو تابع در کد دلفي توجه کنيد) اين زيرخط تضمين ميکند که توابع همنام در دلفي و سي (مثل strlen و توابع مشابه ديگر ) با هم اشتباه گرفته نميشود. تعريف اين توابع مشابه در محيط سي به فرمي غير از cdecl ميتواند باعث خطاهاي ناخواسته و ناخوشايندي شود. مثلا با تعريف اين تابع به صورت پيشرو و از نوع stdcall باعث ميشود که لينکر دلفي به دنبال تابع strlen بگردد و اين تابع را در کتابخانه هاي use شده در محيط دلفي ميابد. در صورتي که اين توابع کتابخانه اي از نوع register هستند که اين باعث خطاي زمان اجرا (و نه خطاي زمان کامپايل ) ميشود. تنها راه مناسب اين است که برنامه c را با گزينه -pr کامپايل کنيم تا توابع همنام در دلفي و سي به جاي هم استفاده شوند يعني با کامپايل کد سي در مثال شما نيازمند دوباره نويسي تابع strlen نخواهيد بود اما تابع strncpy چون به صورت پيشرو تعريف شده و هم اينکه در کتابخانه دلفي با تابع مشابه خود همنام نيست بايستي دوباره در دلفي نوشته شود مگر اينکه در کد سي از تابع strlcopy استفاده شود که گر چه در کتابخانه سي نيست اما هنگام الحاق به دلفي مشکلي پيش نمي آيد. از گزينه -pr بهتر است در حالت عادي استفاده نشود چون ميتواند مشکلاتي را در مورد توابع همنام يا آرگومانها و يا ترتيب آرگومان غير مشابه بوجود بياورد.
افزودن چند فايل Obj به برنامه
نمونه زير راببينيد :
فايل test1.c
1. /*
2. Link C File in delphi
3. Author : F0ruD
4. */
5. void __stdcall FillBuf(char *buf,char ch,int len)
6. {
7. int i;
8. for (i=0;i<len;i++)
9. *(buf+i)=ch;
10. *(buf+len)=0;
11. }
فايل test1.h
1. #ifndef _TEST1_H_
2. # define _TEST1_H_
3.
4. void __stdcall FillBuf(char *,char,int);
5.
6. #endif
7.
فايل test2.c
1. #include "test1.h"
2.
3. void __stdcall ZeroBuf(char *buf,int len)
4. {
5. FillBuf(buf,0,len);
6. }
کد دلفي :
1. unit uLink;
2.
3. interface
4. uses SysUtils, Dialogs;
5.
6. procedure TestLink;
7. implementation
8.
9. procedure ZeroBuf(Buf: PChar; Len: Integer); stdcall; external;
10. //Link obj files to program
11. {$L test2.obj}
12. {$L test1.obj}
13.
14.
15.
16. procedure TestLink;
17. var
18. Buf : array[0..124] of Char;
19. begin
20. Buf := 'This message must fill with 0';
21. ZeroBuf(Buf, 124);
22. ShowMessage(Buf);
23. end;
24.
25.
26. end.
27.
28.
اين مثال نشان ميدهد که چگونه ميتوان از توابع نوشته شده در يک فايل ديگر c در يک فايل ديگر استفاده کرد. وجود فايل هدر test1.h صرفا براي اعلان پيشروي تابع استفاده شده و به اين صورت کامپايلر هنگام کامپايل نوع تابع FillBuf را به صورت cdecl در نظر گرفته است. البته تابع FillBuf نيز در برنامه دلفي قابل استفاده است.
در صورتي که ترتيب لينک شدن اين دو فايل را عوض کنيم با خطا روبرو خواهيم شد چرا که لينکر دلفي بعد از لينک در قسمت پاييني کد دنبال تابع FillBuf ميگردد.