سلام
API يکي از بحثهايي که خيلي طرفدار داره و همه دوست دارن که از تمام توابع سر دربيارن بدون اينکه از کاربردش هم چندان چيزي بدونن يا اصلا احتياجي به بعضي از اونها داشته باشن.
معمولا هر زبان برنامه نويسي مدرني يه راه براي Import اين توابع در اختيار ميذاره.مثلا وي بي دايرکتيو Declare رو معرفي ميکنه يا دلفي از External استفاده ميکنه. اين راهي که شما ميتونيد خيلي راحت به يک Api دسترسي پيدا کنيد. توضيحات بيشتر رو ميتونيد در Help مربوط به زباني که باهاش کار ميکنيد پيدا کنيد. اما اين روشها نقايصي هم دارد.
1.اسم Dll يا هر ماژولي که شامل Api هست بايد ثابت باشه.يعني مثلا براي تابع DllGetVersion که هم در Shell32 هم در خيلي ماژولهاي ديگه نظير ComCtl32.DLL هست بايد هربار يک تابع جدا تعريف بشه مثلا بايد يکي واسه Shell يکي واسه ComCtl و يکي واسه هر ماژول ديگه اين فقط سردرگمي مياره و بس! علاوه براين در برنامه هايي نظير WinAmp يا Pex يا NSIS يا هر چيز ديگري که از سيتم Plugin به صورت callBack و نه از Interface ها استفاده ميکنن چي؟ يا حتي واسه کنترل پنل که مبتني بر يک سيستم تابعي هست چي؟
مثلا در مورد کنترل پنل يک Applet با پسوند Cpl در حقيقت يه Dll با پسوند تغيير کرده است که بايست دست کم تابع CPlAppletرا Export کند (يعني به قولي يک تابع Api به اسم CplApplet داشته باشه.) يا مثلا تمام ويژوال Plugins هاي WinAmp ميبايست تابع winampVisGetHeader زو در خود داشته باشند و Export کنند. حالا اگر برنامه نويس WinAmp ميخواست از همين توابع Builtin استفاده کنه چي؟ مثلا من يه Plugin واسش مينويسم و اسمش رو ميزارم vis_rabbit و شما يکي ديگه به اسم vis_Alpha نويسنده WinAmp هم اصلا از اينکه من و تو يک Plugin واسه برنامش نوشتيم بيخبره ولي Plugin کار ميکنه. اگه مثلا نويسنده WinAmp ميخواست از روش کلاسيک استفاده کنه بايد يه تابع واسه هر Plugin تعريف ميکرد و اين همون کاري نيست که اون انجام داده.
2.اما مشکل دوم. تابع DllGetVersion که گفتم در Shell وجود داره اگه درست يادم باشه از نسخه 4.71 به بعد هست يعني اگه برنامه اي که من مينويسم و اين تابع رو توش مينويسم اگه تو يه جايي با Shell نسخه پايينتر اجرا بشه اصلا قبل از اجرا با يه خطا پايان داده ميشه و اين خطا اصلا تحت کنترل ما نيست به قولي يه خطاي Fatal محسوب ميشه و سيستم Error Trapping در هيچ زباني قادر به مديريت خطا نيست. چون اصولا اين خطا قبل از بررسي Code Section ايجاد ميشه. اين يعني يه چيز غير قابل کنترل و يعني خيلي بد!!!!!!!!
در کل اين روش فقط واسه توابع مطمئن و دائمي مجازه (مثلا خيلي توابعي که در XP تعريف شده و هيچ نسخه اي از آنها در ويندوزهاي قبلي تعريف نشده نميتونن با اين روش فراخواني بشن مگر اينکه تضمين کني برنامه فقط رو XP اجرا ميشه.) اما يه روش ديگه هم هست يه روش که حتي اين کامپايلر ها که روش اول رو پيشنهاد ميکنن گاهي اونو به اين روش ترجمه ميکنن.
در اين روش (که متاسفانه بايد به بروبچ وي بي کار بگم دورشو خيط قرمز بکشيد و طرفش نيايد چون وي بي نميتونه يه تابع رو از طريق آدرسش فراخواني کنه ) شما اول يه ماژول رو بار ميکنيد بعد آدرس تابع رو پيدا ميکنيد و بعد از طريق آدرس اونو فراخواني ميکنيد. طبيعيه که اگه در هر کدوم از اين مراحل موفق نباشيد ميتونيد راحت بفهميد و جلوي Crash رو بگيريد.
مثل مجري هاي برنامه آشپزي :
توابع لازم :
اين تابع يه رشته ميگيره و در صورتي که فايلو پيدا کنه و فايل با Coff Std مايکروسافت مطابقت داشته باشه اونو تو حافظه بار ميزاره (جدي جدي داره ميشه برنامه آشپري) ساده تر بگم اگه اون فايل يه Exe يا يک Dll سالم (يک PE ) باشه تابع موفق ميشه و گرنه بازگشتي صفر خواهد بود.
اين تابع يه دستگيره ماژول رو ميگيره (که از تابع قبلي ميتونيد اينو ايجاد کنيد) يه رشته که اسم پروسه هست رو هم ميگيره بعد در صورتي که اون تابع در اون ماژول باشه آدرسش رو برميگردونه در غير اين صورت بازگشتي تهي خواهد بود.
حالا تابع شما آمادس ميتونيد اشاره گري رو که بهتون برگردونده ميشه رو به هر نوع تابعي که خودتون ميدونيد درسته TypeCast کنيد و ازش استفاده کنيد. يه نمونه اين زير هست که در صورتي که تابع استاندارد DllGetVersion در يک ماژول باشه اونو فراخواني ميکنه
دقت کنيد که براي استفاده از اين متد شما بايد تابع هدف رو بشناسيد نوع فراخواني (مثلا stdcall يا cdecel يا...) رو بدونيد و آرگومانهاش رو هم دقيقا بدونيد داشتن آدرس يه تابع به معني اين نيست که اونو به هر طريقي ميخوايد فراخواني کنيد
نکته مهم ديگه اينکه حتما بعد از پايان کارتون ماژول را آزاد کنيد :
اينم فقط همون دستگيره که دفعه اول ايجاد کرده از بين ميبره و ماژول رو از رو گاز ورميداره.
اما دستور پخت :
و در آخر طريقه فراخواني :
تا بعد هر کي هر سوالي داره همين جا بپرسه تو عيد ميتونم مدام سر بزنم بعدشم من آسمون جل دوباره آواره هستم. در ضمن عيدتون هم پيشاپيش مبارک.
API يکي از بحثهايي که خيلي طرفدار داره و همه دوست دارن که از تمام توابع سر دربيارن بدون اينکه از کاربردش هم چندان چيزي بدونن يا اصلا احتياجي به بعضي از اونها داشته باشن.
معمولا هر زبان برنامه نويسي مدرني يه راه براي Import اين توابع در اختيار ميذاره.مثلا وي بي دايرکتيو Declare رو معرفي ميکنه يا دلفي از External استفاده ميکنه. اين راهي که شما ميتونيد خيلي راحت به يک Api دسترسي پيدا کنيد. توضيحات بيشتر رو ميتونيد در Help مربوط به زباني که باهاش کار ميکنيد پيدا کنيد. اما اين روشها نقايصي هم دارد.
1.اسم Dll يا هر ماژولي که شامل Api هست بايد ثابت باشه.يعني مثلا براي تابع DllGetVersion که هم در Shell32 هم در خيلي ماژولهاي ديگه نظير ComCtl32.DLL هست بايد هربار يک تابع جدا تعريف بشه مثلا بايد يکي واسه Shell يکي واسه ComCtl و يکي واسه هر ماژول ديگه اين فقط سردرگمي مياره و بس! علاوه براين در برنامه هايي نظير WinAmp يا Pex يا NSIS يا هر چيز ديگري که از سيتم Plugin به صورت callBack و نه از Interface ها استفاده ميکنن چي؟ يا حتي واسه کنترل پنل که مبتني بر يک سيستم تابعي هست چي؟
مثلا در مورد کنترل پنل يک Applet با پسوند Cpl در حقيقت يه Dll با پسوند تغيير کرده است که بايست دست کم تابع CPlAppletرا Export کند (يعني به قولي يک تابع Api به اسم CplApplet داشته باشه.) يا مثلا تمام ويژوال Plugins هاي WinAmp ميبايست تابع winampVisGetHeader زو در خود داشته باشند و Export کنند. حالا اگر برنامه نويس WinAmp ميخواست از همين توابع Builtin استفاده کنه چي؟ مثلا من يه Plugin واسش مينويسم و اسمش رو ميزارم vis_rabbit و شما يکي ديگه به اسم vis_Alpha نويسنده WinAmp هم اصلا از اينکه من و تو يک Plugin واسه برنامش نوشتيم بيخبره ولي Plugin کار ميکنه. اگه مثلا نويسنده WinAmp ميخواست از روش کلاسيک استفاده کنه بايد يه تابع واسه هر Plugin تعريف ميکرد و اين همون کاري نيست که اون انجام داده.
2.اما مشکل دوم. تابع DllGetVersion که گفتم در Shell وجود داره اگه درست يادم باشه از نسخه 4.71 به بعد هست يعني اگه برنامه اي که من مينويسم و اين تابع رو توش مينويسم اگه تو يه جايي با Shell نسخه پايينتر اجرا بشه اصلا قبل از اجرا با يه خطا پايان داده ميشه و اين خطا اصلا تحت کنترل ما نيست به قولي يه خطاي Fatal محسوب ميشه و سيستم Error Trapping در هيچ زباني قادر به مديريت خطا نيست. چون اصولا اين خطا قبل از بررسي Code Section ايجاد ميشه. اين يعني يه چيز غير قابل کنترل و يعني خيلي بد!!!!!!!!
در کل اين روش فقط واسه توابع مطمئن و دائمي مجازه (مثلا خيلي توابعي که در XP تعريف شده و هيچ نسخه اي از آنها در ويندوزهاي قبلي تعريف نشده نميتونن با اين روش فراخواني بشن مگر اينکه تضمين کني برنامه فقط رو XP اجرا ميشه.) اما يه روش ديگه هم هست يه روش که حتي اين کامپايلر ها که روش اول رو پيشنهاد ميکنن گاهي اونو به اين روش ترجمه ميکنن.
در اين روش (که متاسفانه بايد به بروبچ وي بي کار بگم دورشو خيط قرمز بکشيد و طرفش نيايد چون وي بي نميتونه يه تابع رو از طريق آدرسش فراخواني کنه ) شما اول يه ماژول رو بار ميکنيد بعد آدرس تابع رو پيدا ميکنيد و بعد از طريق آدرس اونو فراخواني ميکنيد. طبيعيه که اگه در هر کدوم از اين مراحل موفق نباشيد ميتونيد راحت بفهميد و جلوي Crash رو بگيريد.
مثل مجري هاي برنامه آشپزي :
توابع لازم :
کد:
HMODULE LoadLibrary(
LPCTSTR lpFileName
);
اين تابع يه رشته ميگيره و در صورتي که فايلو پيدا کنه و فايل با Coff Std مايکروسافت مطابقت داشته باشه اونو تو حافظه بار ميزاره (جدي جدي داره ميشه برنامه آشپري) ساده تر بگم اگه اون فايل يه Exe يا يک Dll سالم (يک PE ) باشه تابع موفق ميشه و گرنه بازگشتي صفر خواهد بود.
کد:
FARPROC GetProcAddress(
HMODULE hModule,
LPCSTR lpProcName
);
حالا تابع شما آمادس ميتونيد اشاره گري رو که بهتون برگردونده ميشه رو به هر نوع تابعي که خودتون ميدونيد درسته TypeCast کنيد و ازش استفاده کنيد. يه نمونه اين زير هست که در صورتي که تابع استاندارد DllGetVersion در يک ماژول باشه اونو فراخواني ميکنه
دقت کنيد که براي استفاده از اين متد شما بايد تابع هدف رو بشناسيد نوع فراخواني (مثلا stdcall يا cdecel يا...) رو بدونيد و آرگومانهاش رو هم دقيقا بدونيد داشتن آدرس يه تابع به معني اين نيست که اونو به هر طريقي ميخوايد فراخواني کنيد
نکته مهم ديگه اينکه حتما بعد از پايان کارتون ماژول را آزاد کنيد :
کد:
BOOL FreeLibrary(
HMODULE hModule
);
اينم فقط همون دستگيره که دفعه اول ايجاد کرده از بين ميبره و ماژول رو از رو گاز ورميداره.
اما دستور پخت :
کد:
unit Unit2;
interface
uses
Windows;
type
_DllVersionInfo = record
cbSize : DWORD;
dwMajorVersion :DWORD ;
dwMinorVersion :DWORD;
dwBuildNumber :DWORD;
dwPlatformID:DWORD;
end;
DLLVERSIONINFO = _DllVersionInfo;
TDllVersionInfo = DLLVERSIONINFO;
PDllVersionInfo = ^TDllVersionInfo;
TDllGetVerProc = function (dvi : PDllVersionInfo ): HRESULT ;stdcall;
function RunDllGetVersion(DllName : String):TDllVersionInfo;
implementation
uses SysUtils;
function RunDllGetVersion(DllName : String):TDllVersionInfo;
var
DllHandle : HMODULE;
DllGetVersionProc : TDllGetVerProc;
DllVerInfo : TDllVersionInfo;
begin
DllHandle:=LoadLibrary(PChar(DllName));
if DllHandle=0 then
//Error handler ...
raise Exception.CreateFmt('Can not load Dll : %s',[DllName]);
@DllGetVersionProc:=GetProcAddress(DllHandle,'DllGetVersion');
if @DllGetVersionProc=nil then
raise Exception.Create('Can not find DllGetVersion in dll');
FillChar(DllVerInfo,sizeof(DllVerInfo),0);
DllVerInfo.cbSize:=sizeof(DllVerInfo);
DllGetVersionProc(@DllVerInfo);
Result:=DllVerInfo;
FreeLibrary(DllHandle);
end;
end.
و در آخر طريقه فراخواني :
کد:
procedure TForm1.Button1Click(Sender: TObject);
var
DllInfo : TDllVersionInfo;
const
DLLVER_PLATFORM_WINDOWS = 1;
DLLVER_PLATFORM_NT = 2;
PlatformName : array [DLLVER_PLATFORM_WINDOWS..DLLVER_PLATFORM_NT] of String =('Windows 9X','Windows NT');
begin
DllInfo:=RunDllGetVersion('Shell32.dll') ;
with DllInfo do
ShowMessage(Format('Major : %d - Minor : %d - BuildNumber : %d - Platform: %s',[dwMajorVersion,dwMinorVersion,dwBuildNumber,PlatformName[dwPlatformID]]));
end;
تا بعد هر کي هر سوالي داره همين جا بپرسه تو عيد ميتونم مدام سر بزنم بعدشم من آسمون جل دوباره آواره هستم. در ضمن عيدتون هم پيشاپيش مبارک.