全國計(jì)算機(jī)等級考試二級Delphi輔導(dǎo)講義 103

字號:

10.2.1.1 編寫一般DLLs的步驟
    編寫一般DLLs的步驟如下:
    1.利用Delphi的應(yīng)用程序模板,建立一個(gè)DLLs程序框架。
    對于Delphi 1.0的用戶,由于沒有DLLs模板,因此:
    (1).建立一個(gè)一般的應(yīng)用程序,并打開工程文件;
    (2).移去窗體和相應(yīng)的代碼單元;
    (3).在工程文件中,把program改成library,移去Uses子句中的Forms,并添加適當(dāng)?shù)膸靻卧ㄒ话鉙ysUtils、Classes是需要的),刪去begin…end之間的所有代碼。
    2.以適當(dāng)?shù)奈募3治募?,此時(shí)library后跟的庫名自動修改;
    3.輸入過程、函數(shù)代碼。如果過程、函數(shù)準(zhǔn)備供其它應(yīng)用程序調(diào)用,則在過程、函數(shù)頭后加上export 編譯指示;
    4.建立exports子句,包含供其它應(yīng)用程序調(diào)用的函數(shù)和過程名。可以利用標(biāo)準(zhǔn)指示 name 、Index、resident以方便和加速過程/函數(shù)的調(diào)用;
    5.輸入庫初始化代碼。這一步是可選的;
    6.編譯程序,生成動態(tài)鏈接庫文件。
    10.2.1.2 動態(tài)鏈接庫中的標(biāo)準(zhǔn)指示
    在動態(tài)鏈接庫的輸出部分,用到了三個(gè)標(biāo)準(zhǔn)指示:name、Index、resident。
    1.name
    name后面接一個(gè)字符串常量,作為該過程或函數(shù)的輸出名。如:
    exports
    InStr name MyInstr;
    其它應(yīng)用程序?qū)⒂眯旅郑∕yInstr)調(diào)用該過程或函數(shù)。如果仍利用原來的名字(InStr),則在程序執(zhí)行到引用點(diǎn)時(shí)會引發(fā)一個(gè)系統(tǒng)錯(cuò)誤。
    2.Index
    Index指示為過程或函數(shù)分配一個(gè)順序號。如果不使用Index指示,則由編譯器按順序進(jìn)行分配。
    Index后所接數(shù)字的范圍為1…32767。使用Index可以加速調(diào)用過程。
    3.resident
    使用resident,則當(dāng)DLLs裝入時(shí)特定的輸出信息始終保持在內(nèi)存中。這樣當(dāng)其它應(yīng)用程序調(diào)用該過程時(shí),可以比利用名字掃描DLL入口降低時(shí)間開銷。
    對于那些其它應(yīng)用程序常常要調(diào)用的過程或函數(shù),使用resident指示是合適的。例如:
    exports
    InStr name MyInStr resident;
    10.2.1.3 DLLs中的變量和段
    一個(gè)DLLs擁有自己的數(shù)據(jù)段(DS),因而它聲明的任何變量都為自己所私有。調(diào)用它的模塊不能直接使用它定義的變量。要使用必須通過過程或函數(shù)界面才能完成。而對DLLs來說,它永遠(yuǎn)都沒有機(jī)會使用調(diào)用它的模塊中聲明的變量。
    一個(gè)DLLs沒有自己的堆棧段(SS),它使用調(diào)用它的應(yīng)用程序的堆棧。因此在DLL中的過程、函數(shù)絕對不要假定DS = SS。一些語言在小模式編譯下有這種假設(shè),但使用Delphi可以避免這種情況。Delphi絕不會產(chǎn)生假定DS = SS的代碼,Delphi的任何運(yùn)行時(shí)間庫過程/函數(shù)也都不作這種假定。需注意的是如果讀者想嵌入?yún)R編語言代碼,絕不要使SS和DS登錄同一個(gè)值。
    10.2.1.4 DLLs中的運(yùn)行時(shí)間錯(cuò)和處理
    由于DLLs無法控制應(yīng)用程序的運(yùn)行,導(dǎo)致很難進(jìn)行異常處理,因此編寫DLLs時(shí)要十分小心,以確保被調(diào)用時(shí)能正常執(zhí)行 。當(dāng)DLLs中發(fā)生一個(gè)運(yùn)行時(shí)間錯(cuò)時(shí),相應(yīng)DLLs并不一定從內(nèi)存中移去(因?yàn)榇藭r(shí)其它應(yīng)用程序可能正在用它),而調(diào)用DLLs的程序異常中止。這樣造成的問題是當(dāng)DLLs已被修改,重新進(jìn)行調(diào)用時(shí),內(nèi)存中保留的仍然可能是以前的版本,修改后的程序并沒有得到驗(yàn)證。對于這個(gè)問題,有以下兩種解決方法:
    1.在程序的異常處理部分顯式將DLL卸出內(nèi)存;
    2.完全退出Windows,而后重新啟動,運(yùn)行相應(yīng)的程序。
    同一般的應(yīng)用程序相比,DLL中運(yùn)行時(shí)間錯(cuò)的處理是很困難的,而造成的后果也更為嚴(yán)重。因此要求程序設(shè)計(jì)者在編寫代碼時(shí)要有充分、周到的考慮。
    10.2.1.5 庫初始化代碼的編寫
    傳統(tǒng)Windows中動態(tài)鏈接庫的編寫,需要兩個(gè)標(biāo)準(zhǔn)函數(shù):LibMain和WEP,用于啟動和關(guān)閉DLL。在LibMain中,可以執(zhí)行開鎖DLL數(shù)據(jù)段、分配內(nèi)存、初始化變量等初始化工作;而WEP在從內(nèi)存中移去DLLs前被調(diào)用,一般用于進(jìn)行必要的清理工作,如釋放內(nèi)存等。Delphi用自己特有的方式實(shí)現(xiàn)了這兩個(gè)標(biāo)準(zhǔn)函數(shù)的功能。這就是在工程文件中的begin…end部分添加初始化代碼。和傳統(tǒng)Windows編程方法相比,它的主要特色是:
    1.初始化代碼是可選的。一些必要的工作(如開鎖數(shù)據(jù)段)可以由系統(tǒng)自動完成。所以大部分情況下用戶不會涉及到;
    2.可以設(shè)置多個(gè)退出過程,退出時(shí)按順序依次被調(diào)用;
    3.LibMain和WEP對用戶透明,由系統(tǒng)自動調(diào)用。
    初始化代碼完成的主要工作是:
    1.初始化變量、分配全局內(nèi)存塊、登錄窗口對象等初始化工作。在(10.3.2)節(jié)“利用DLLs實(shí)現(xiàn)應(yīng)用程序間的數(shù)據(jù)傳輸”中,用于數(shù)據(jù)共享的全局內(nèi)存塊就是在初始化代碼中分配的。
    2.設(shè)置DLLs退出時(shí)的執(zhí)行過程。Delphi有一個(gè)預(yù)定義變量ExitProc用于指向退出過程的地址。用戶可以把自己的過程名賦給ExitProc。系統(tǒng)自動調(diào)用WEP函數(shù),把ExitProc指向的地址依次賦給WEP執(zhí)行,直到ExitProc為nil。
    下邊的一段程序包含一個(gè)退出過程和一段初始化代碼,用來說明如何正確設(shè)置退出過程。
    library Test;
    {$S-}
    uses WinTypes, WinProcs;
    var
    SaveExit: Pointer;
    procedure LibExit; far;
    begin
    if ExitCode = wep_System_Exit then
    begin
    { 系統(tǒng)關(guān)閉時(shí)的相應(yīng)處理 }
    end
    else
    begin
    { DLL卸出時(shí)的相應(yīng)處理 }
    end;
    ExitProc := SaveExit; { 恢復(fù)原來的退出過程指針 }
    end;
    begin
    {DLL的初始化工作 }
    SaveExit := ExitProc; { 保存原來的退出過程指針 }
    ExitProc := @LibExit; { 安裝新的退出過程 }
    end.
    在初始化代碼中,首先把原來的退出過程指針保存到一個(gè)變量中,而后再把新的退出過程地址賦給ExitProc。而在自定義退出過程LibExit結(jié)束時(shí)再把ExitProc的值恢復(fù)。由于ExitProc是一個(gè)系統(tǒng)全局變量,所以在結(jié)束時(shí)恢復(fù)原來的退出過程是必要的。
    退出過程LibExit中使用了一個(gè)系統(tǒng)定義變量ExitCode,用于標(biāo)志退出時(shí)的狀態(tài)。 ExitCode的取值與意義如下:
    表10.1 ExitCode的取值與意義
    ━━━━━━━━━━━━━━━━━━━━━
    取 值 意 義
    —————————————————————
    WEP_System_Exit Windows關(guān)閉
    WEP_Free_DLLx DLLs被卸出
    ━━━━━━━━━━━━━━━━━━━━━
    退出過程編譯時(shí)必須關(guān)閉stack_checking,因而需設(shè)置編譯指示 {$S-} 。
    (責(zé)任編輯:大可)