آژانس هواپیمایی
pop up

يه سري ريزه کاري.....

شروع موضوع توسط littlerabbit ‏15 مارس 2004 در انجمن برنامه نویسی

  1. littlerabbit

    littlerabbit مدیر بازنشسته کاربر فعال

    تاریخ عضویت:
    ‏13 جولای 2003
    نوشته ها:
    667
    تشکر شده:
    4
    محل سکونت:
    Iran
    سلام
    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;
    
    تا بعد هر کي هر سوالي داره همين جا بپرسه تو عيد ميتونم مدام سر بزنم بعدشم من آسمون جل دوباره آواره هستم. در ضمن عيدتون هم پيشاپيش مبارک.