PDA

View Full Version : Callback function ها چه هستند و چگونه در دلفی فراخوانی می شوند؟



m-khorsandi
یک شنبه 13 اسفند 1385, 07:30 صبح
Callback function ها چه هستند و چگونه در دلفی فراخوانی می شوند؟


عموماً برای فراخوانی توابع API یا DLL ها در دلفی، تابعی می‌نویسید تا آن تابع ، تابع API را فراخوانی کند. اما در بعضی موارد، سیستم در پاسخ به رویدادهای حاصل از فراخوانی یک API ، ناگزیر به فراخوانی یک تابع که در برنامه تعریف شده ست می‌باشد.

ساده تر بگویم که یکCallback function روال یا متدی در برنامه شما ست که ویندوز آن را فراخوانی میکند. بطور کلی یک Callback function به معنی ارسال یک تابع به عنوان یک پارامتر به یک تابع دیگر هست، بعد از اجراء و تکمیل Callback function، کنترل به تابع اصلی (فراخوانی کننده) برمی‌گردد.

هرگز به طور مستقیم یک Callback function را فراخوانی نمی‌کنیم ، بلکه تابع API ، آن را برای ما فراخوانی می‌کند. ویندوز اختیار برقرار‌ی ارتباط مستقیم با هر برنامه‌ای را با پاس دادن پارامترهای مختلف به آن مانند Callback functionها، به ما می‌دهد.

یک مثال خوب از توابع API ویندوز که نیاز به callback function ها دارند، توابع شمارشی هستند که سراسر یک گروه از عناصر ویندوز را یک به یک می‌شمارند، مانند :


EnumWindows, EnumPrinters, EnumFontFamilies

چطور توابع APIیی که Callback function نیاز دارند را بشناسم؟
با مشاهده ی پارامترهای یک تابع API ، می‌توان تشخیص داد که تابع به callback function نیاز دارد. پارامتری که پوینتری به callback function می‌گیرد از نوع Long Pointer هست و معمولاً با پیشوند lp آغاز می‌شود. همچنین نام پارامتر معمولاً با کلمه "Func" خاتمه پیدا می‌کند، که نشان‌ می‌دهد پوینتری به یک تابع گرفته ست.

برای مثال، نگاهی بیاندازید به تعریف عنوان تابع EnumWindows که در یونیت Windows.pas قرار دارد. پارامتر lpEnumFunc نشان می‌دهد که تابع به callback function نیاز دارد.


function EnumWindows(lpEnumFunc: TFNWndEnumProc; LParam: LPARAM): BOOL; stdcall;


callback function های شخصی ، پارامترهای مشخصی دارند که می‌بایست توسط برنامه تعریف شوند. حتماً ترتیب در تعریف پارامترها می‌بایست رعایت شود به این دلیل که ویندوز اطلاعات صحیح را با ترتیب صحیح به برنامه پاس می‌دهد.

تابع EnumWindows تمام پنجره های روی صفحه را با پاس دادن هندل هر پنجره می‌شمارد. برای شمارش هر پنجره، تابع EnumWindows به تابع فراخوانی کننده در برنامه برمی‌گردد و اطلاعاتی که در مورد پنجره ای بدست آورده را برگشت می‌دهد.فراخوانی EnumWindows تا زمانی ادامه پیدا می‌کند که آخرین پنجره نیز شمرده شود یا اینکه callback function مقدار false برگرداند، به این معنی که خواسته‌اید شمارش متوقف شود یا اینکه دیگر پنجره‌ای برای شمارش وجود ندارد.

میخواهیم برای نمایش کاربرد callback functionها از نمونه زیر استفاده کنیم:
بدست آوردن عنوان (Caption) پنجره های روی صفحه Desktop با فراخوانی تابع API یی به نام GetWindowText و تغییر عنوان هر پنجره با تابع SetWindowText و حتی می‌خواهیم عنوان پنجره‌ها را در کامپوننت Memo قرار دهیم.

نام‌گذاری callback functionها
پیشنهاد می‌کنم شما در انتهای نام تابع APIیی که می‌نویسید از کلمه "Func" استفاده کنید. برای مثال callback functionیی که میبایست در تابع EnumWindows استفاده کنیم ، می‌تواند نام EnumWindowFunc داشته باشد. البته ما callback functionهای زیادی می‌توانیم برای یک تابع API داشته باشیم.

Callback function مان را به صورت زیر تعریف می‌کنیم :


function EnumWindowsFunc (Handle: THandle; List: TStringList): boolean; stdcall;

پس این تابعی ست که هم عنوان و هم بدنه آن را خود تعریف می‌کنیم و آن را از جایی (مثلاً یک DLL) وارد برنامه نکرده‌ایم.

نکته : بدون رهنمود stdcall ، ویندوز توانایی دسترسی به callback function را ندارد. کلمه کلیدی stdcall به دلفی می گوید تا این دستور را به صورتی ترجمه کن که ویندوز قابلیت شناسایی آن را داشته باشد و زمانی که ویندوز قابلیت شناسایی این دستور را داشته باشد ، برنامه های دیگر هم این قابلیت را پیدا می‌کنند.

بیشتر توابع API ویندوز که callback function ها را به عنوان پارامتر قبول می‌کنند، پارامترهای معمولی را نیز که اصطلاحاً به آنها "داده‌های تعریف شده ی برنامه" یا Application defined data گفته می‌شود را هم قبول میکنند.
نگاهی به پارامتر LParam در تابع EnumWindows بیاندازید. پارامتر Lparam یی که به تابع API ویندوز پاس می‌دهیم ، به callback function مان برگردانده می‌شود.

یک کامپوننت Button و Memo روی فرم قرار دهید و متد زیر را برای رویداد Button.OnClick بنویسید.

procedure TForm1.Button1Click(Sender: TObject) ;
begin
Memo1.Clear;
EnumWindows(@EnumWindowsFunc, LParam(Memo1.Lines)) ;
end;


نکته:
چون EnumWindows به یک پوینتر برای callback function نیاز دارد، از عملگر @ برای ارسال آدرس callback function مان با نام EnumWindowsFunc در حافظه استفاده می‌کنیم.

خوب، حالا نیاز به پیاده سازی EnumWindowsFunc هست :

function EnumWindowsFunc(Handle: THandle; List: TStringList) : boolean ; stdcall;
var
Caption: array[0..256] of Char;
begin
if GetWindowText (Handle, Caption, SizeOf(Caption)-1) <> 0 then
begin
List.Add(Caption) ;
SetWindowText(Handle, PChar('CF - ' + Caption)) ;
end;

result :=True;
end;

تابع EnumWindowsFunc ، عنوان هر پنجره اصلی را به یک لیست اضافه می‌کند که این لیست از نوع TStringList هست. برای اضافه کردن عناوین پنجره ها به لیست، شیئی از نوع TStringList ، در پارامتر LParam ، به callback function پاس داده شده ست. همچنین callback function دو تابع API دیگر را هم فراخوانی می‌کند : تابع GetWindowText، که عنوان پنجره ی مشخصی را در بافر کپی می‌کند و تابع SetWindowText ، که عنوان پنجره‌ای را که می‌یابد تغییر می‌دهد.

به این دلیل از کلمه "مشخص" استفاده کردم که ممکن ست یک یا چند پنجره در صفحه Desktop وجود داشته باشد، پس callback function مان نیز ممکن ست یک یا چند بار اجرا شود، اما در هر صورت با هر بار اجرا، تابع فقط بر روی یک پنجره کار می‌کند.

Callback function، تابعی‌ست در برنامه شما که توسط DLLهای Win32 یا سایر DLLها فراخوانی می‌شود. اساساً، ویندوز، توابع API متعددی دارد که نیاز به callback function دارند. وقتی یکی از این توابع API را فراخوانی می‌کنیم ، آدرس یک تابع که در برنامه ما تعریف شده (و ویندوز می‌تواند آن را فراخوانی کند) را به آن پاس می‌دهیم.