DELPHI基础教程

第十章 动态链接库编程(二)

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 在这里又有上乘的表现。特别是窗体重用机制是 Delphi 对 Windows 下 DLLs 编程的一个重大改进。在一般的 DLLs 编程中也体现了 Delphi 快捷、方便的特点。动态链接库是 Windows 下程序组织的一种重要方式,使用动态链接库可以极大地保护用户在不同开发工具、不同时期所做的工作。利用动态链接库,用户可以逐步去构筑自己的程序模块库,为今后的工作积累素材。

 [目录] [上一页] [下一页]