後一頁
前一頁
回目錄
回首頁
第十章 動態鍊結庫程式設計(二)

10.3.2.2 伺服器程式的編寫 

  伺服器程式必須包含對DLL的呼叫代碼,如: 

function GetGlobalMem: THandle; far; external 'c:\dlls\glbmem';

  通過呼叫該函數,伺服器可以獲得全局記憶體塊的句柄。

  在寫入數據前,伺服器必須鎖定全局記憶體,以避免在寫入過程中Windows移動該記憶體塊的位置。

  函數GlobalLock鎖定全局記憶體並返回指向該記憶體塊的指標: 

pMem := GlobalLock(hMem);

  對pMem的任何修改都會反映到全局記憶體塊中。

  對記憶體塊進行操作後,呼叫GlobalUnLock進行解鎖。記憶體塊操作之後盡早解鎖,有利於Windows充分利用記憶體資源。

  伺服器寫入數據的實現代碼如下。 

var

hMem: THandle;

pMem: PChar;

begin

hMem := GetGlobalMem; {獲得全局記憶體塊的句柄}

if hMem <> 0 then

begin

pMem := GlobalLock(hMem); {加鎖全局記憶體塊}

if pMem <> nil then

begin

StrPCopy(pMem,Memo1.text); {向全局記憶體塊寫入數據}

GlobalUnlock(hMem); {解鎖全局記憶體塊}

end

else

MessageDlg('Couldnot Lock memory block',mtWarning,[mbOK],0);

end; 

10.3.2.3 用戶程式的編寫 

  用戶程式幾乎是伺服器程式的翻版。唯一的區別在於一個是寫入數據,一個是下載數據。

下面是用戶從全局記憶體塊下載數據的程式清單。 

var

hMem: THandle;

pMem: PChar;

begin

hMem := GetGlobalMem; {獲得全局記憶體塊的句柄}

if hMem <> 0 then

begin

pMem := GlobalLock(hMem); {加鎖全局記憶體塊}

if pMem <> nil then

begin

Memo1.text := StrPas(pMem); {從全局記憶體塊讀取數據}

GlobalUnlock(hMem); {解鎖全局記憶體塊}

end

else

MessageDlg('Couldnot Lock memory block',mtWarning,[mbOK],0);

end;

10.4 利用DLLs實現窗體重用 

  實現窗體重用是Delphi DLLs功能中一個引人注目的特色。當你建立了一個令自己滿意的一般的窗體並希望能在不同應用程式中使用,特別是希望能在非Delphi 應用程式中使用時,把窗體做進一個動態鍊結庫中是最適當的。這樣即使用其它工具開發的應用程式,如C++、Visual Basic等,也都可以去呼叫它。

  包含窗體的DLLs有100K左右的部件庫(Component Library)開銷。可以通過把幾個窗體編譯成一個DLLs來最小化這筆開銷。DLl中的不同窗體可以共享部件庫。 

10.4.1 利用DLLs實現窗體重用的一般步驟 

  利用DLLs實現窗體重用的步驟是:

  1.在整合開發環境(IDE)中,按自己的需要設計一個窗體;

  2.編寫一個用於輸出的函數或過程。在該函數或過程中,設計的窗體被實例化;

  3.重複步驟1、2,直到完成所有重用窗體的設計;

  4.打開工程文件,進行修改,以適應產生 .dll文件的需要:

  (1).把保留字program設為library;

  (2).從uses子句中去掉Forms單元;

  (3).移去begin,end之間的所有代碼;

  (4).在uses子句下,begin…end塊之前,加入保留字exprots。exports 後是輸出函數名或過程名。

  5.編譯產生DLLs文件;

  6.在其它應用程式中呼叫重用窗體。

  重用窗體的呼叫同一般DLLs函數或過程的呼叫完全一致,不再贅述。讀者可參看下面的例子。 

10.4.2 窗體重用實例 

  下面我們通過一個具體的實例來敘述窗體重用的設計過程。我們在一個名為passform.dll 的文件中儲存了一個密碼設定視窗和一個密碼檢查視窗。而後在一個Delphi 編寫的程式和一個VB編寫的程式中進行呼叫。事實證明這種方法是完全可行的。

10.4.2.1 窗體重用DLLs的設計 

  窗體重用DLLs的設計依照(10.4.1)中介紹的步驟進行。DLLs中的兩個窗體 SetPassWordForm和GetPassWordForm分別用於設定和檢查密碼。它們的設計介面如圖所示。

窗體類TSetPassWordForm定義了兩個數據成員Verified和PassWord,用於記錄密碼確認狀態和設定的密碼。TSetPassWordForm的定義如下:

type

TSetPassWordForm = class(TForm)

Label1: TLabel;

Edit1: TEdit;

OKBtn: TBitBtn;

CancelBtn: TBitBtn;

procedure FormCreate(Sender: TObject);

procedure Edit1KeyPress(Sender: TObject; var Key: Char);

private

{ Private declarations }

Verified: Boolean;

public

{ Public declarations }

PassWord: PChar;

end;

  視窗產生時,對數據成員和部件狀態進行初始化: 

procedure TSetPassWordForm.FormCreate(Sender: TObject);

begin

Verified := False;

PassWord := StrAlloc(40);

OKBtn.Enabled := False;

Label1.Caption := 'Please Input PassWord:';

end;

  按鈕OKBtn在程式啟動時Enabled屬性設定為False,直到密碼被正確設定後Enabled屬性才恢復為True。這樣就保證了只有密碼被正確設定後,密碼設定視窗才能正常關閉。否則只能按Cancel按鈕取消。

  在密碼設定代碼單元中定義了一個輸出函數SetPassWord,用於產生密碼設定視窗並返回設定的密碼: 

function SetPassWord(PWord: PChar): Boolean;

var

SetPassWordForm: TSetPassWordForm;

begin

Result := False;

SetPassWordForm := TSetPassWordForm.Create(Application);

try

with SetPasswordForm do

if ShowModal = mrOK then

begin

StrCopy(PWord,StrUpper(Password));

Result := True;

end;

finally

SetPasswordForm.Free;

end;

end;

  密碼成功設定,把PassWord的值拷貝給PWord輸出,並返回True。應該注意的是由於 PWord本身就是指標型式,指向一個字元串的位址,因而雖然PWord用於輸出,但在參數表中仍為傳值參數,而不是傳址參數。另外呼叫函數StrCopy,要求PWord在傳入前已分配記憶體,否則會導致一個一般保護錯。try...finally用於保護視窗所佔用記憶體資源在任何情況下都能正常釋放,讀者可參看第十二章。

  在密碼設定視窗中,為了確保用戶記住了設定的密碼,在用戶輸入並按回車鍵後,要求用戶再次輸入進行確認。只有用戶重新輸入的字元串與原設定密碼相同,密碼設定視窗才能正常關閉 。否則將原設定密碼清空,要求用戶再次輸入。以上功能的實現在編輯框的OnKeyPress事件處理過程中。 

procedure TSetPassWordForm.Edit1KeyPress(Sender: TObject; var Key: Char);

begin

if Edit1.text = '' then Exit;

if Key = #13 then

begin

if Verified then

if StrPas(PassWord) = Edit1.text then

begin

OKBtn.Enabled := True;

Edit1.Enabled := False;

OKBtn.SetFocus;

end

else

begin

Verified := False;

MessageDlg('PassWord is InValid.',mtWarning,[mbOK],0);

Edit1.text := '';

PassWord := '';

Label1.Caption := 'Please Input PassWord:';

end

else

begin

Verified := True;

StrPCopy(PassWord,Edit1.text);

Edit1.text := '';

Label1.caption := 'Please Verify PassWord:';

end;

Key := #0;

end;

end;

  密碼檢查視窗的實現相對簡單,只定義了一個輸出函數GetPassWord,用於產生密碼檢查視窗並返回密碼檢查的結果。 

function GetPassword(Password: PChar): Boolean;

var

GetPasswordForm: TGetPasswordForm;

begin

Result := False;

GetPasswordForm := TGetPasswordForm.Create(Application);

try

with GetPasswordForm do

if ShowModal = mrOK then

if UpperCase(Edit1.Text) <> StrPas(StrUpper(Password)) then

MessageDlg('Invalid Password', mtWarning, [mbOK], 0)

else

Result := True;

finally

PasswordForm.Free;

end;

end;

  PassWord為輸入的參數,不能為空,由呼叫以上函數的程式負責維護。

  視窗中用戶輸入密碼時回顯在螢幕上的字元由編輯框的PassWordChar屬性確定。

  在DLLs的工程文件中,把兩個輸出函數寫到exports子句中。 

library PassForm; 

uses

GetPass in 'GETPASS.PAS' {PasswordForm},

Setpass in 'SETPASS.PAS' {SetPassWordForm}; 

exports

GetPassword,SetPassWord; 

begin

end. 

10.4.2.2 Delphi應用程式呼叫重用窗體 

  在Delphi應用程式中呼叫重用窗體,首先必須包含passform.dll的兩個輸出函數: 

function GetPassword(Password: PChar): Boolean;

far; external 'c:\dlls\PassForm';

function SetPassword(PassWord: PChar): Boolean;

far; external 'c:\dlls\PassForm';

  這位於程式單元的implementation部分。

密碼設定部分的實現代碼為: 

procedure TForm1.SetButtonClick(Sender: TObject);

begin

PassWord := StrAlloc(40);

if SetPassWord(PassWord) = False then

MessageDlg('PassWord is not set',mtInformation,[mbOK],0);

end;

  首先為密碼字元串分配記憶體。當密碼設定窗體按Cancel按鈕取消時,顯示相應的資訊。

  密碼檢查部分的實現代碼為: 

procedure TForm1.TestButtonClick(Sender: TObject);

begin

if PassWord = nil then

begin

MessageDlg('Set password first', mtInformation, [mbOK], 0);

SetButton.SetFocus;

Exit;

end;

if GetPassword(PassWord) then

Label1.Caption := 'You are Wellcome !'

else

Label1.Caption := 'Sorry,You are InValid User.';

end;

  根據密碼檢查的結果,在標籤框中顯示相應的資訊。 

10.4.2.3 VB應用程式呼叫重用窗體 

  VB是微軟公司極力推薦的一個可視化開發工具。它雖然並不支援動態鍊結庫的建立,但可以呼叫標準的Windows API動態鍊結庫和用其它語言編寫的動態鍊結庫。為了驗證所產生DLLs的普適性,我們用VB開發了一個簡單的程式來呼叫passform.dll中儲存的窗體。

下面是VB程式的完整代碼,和Delphi程式的對應部分基本一致。 

Option Explicit

Declare Function GetPassWord Lib "c:\dlls\passform.dll" (ByVal PassWord As String) As Integer

Declare Function SetPassWord Lib "c:\dlls\passform.dll" (ByVal PassWord As String) As Integer 

Dim PassWord As String * 40

Sub Check_Click ()

If PassWord = "" Then

MsgBox ("Enter sample password first")

SetPass.SetFocus

Else

If GetPassWord(PassWord) Then

StatusLbl.Caption = "You are Welcome!"

Else

StatusLbl.Caption = "Sorry,You are Invalid User."

End If

End If

End Sub

Sub SetPass_Click ()

If SetPassWord(PassWord) = 0 Then

MsgBox ("PassWord is not Set.")

End If

End Sub

  有關VB程式設計的一些具體問題,讀者可參看有關的VB參考書。 

10.4.3 小結 

  本章我們討論的是動態鍊結庫程式設計。許多可視化開發工具(如Visual Basic)不支援 DLLs的建立,而Delphi在這�堣S有上乘的表現。特別是窗體重用機制是Delphi對Windows下DLLs程式設計的一個重大改進。在一般的DLLs程式設計中也體現了Delphi快捷、方便的特點。動態鍊結庫是 Windows下程式群群組織的一種重要方式,使用動態鍊結庫可以極大地保護用戶在不同開發工具、不同時期所做的工作。利用動態鍊結庫,用戶可以逐步去構築自己的程式模群組庫,為今後的工作積累素材。

 



後一頁
前一頁
回目錄
回首頁