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

字號(hào):

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