副标题#e#
3 操作DLLs实现数据传输
3.1 DLLs中的全局内存
Windows划定:DLLs并不拥有它打开的任何文件或它分派的任何全局内存块。这些工具由直接或间接挪用DLLs的应用措施拥有。这样,当应用措施中止时,它拥有的打开的文件自动封锁,它拥有的全局内存块自动释放。这就意味着生存在DLLs全局变量中的文件和全局内存块变量在DLLs
没有被通知的环境下就变为犯科。这将给其它利用该DLLs的应用措施造成坚苦。
为了制止呈现这种环境,文件和全局内存块句柄不该作为DLLs的全局变量,而是作为DLLs中进程或函数的参数通报给DLLs利用。挪用DLLs的应用措施应该认真对它们的维护。
但在特定环境下,DLLs也可以拥有本身的全局内存块。这些内存块必需用gmem_DDEShare属性举办分派。这样的内存块直到被DLLs显示释放或DLLs退出时都保持有效。
由DLLs打点的全局内存块是应用措施间举办数据传输的又一途径,下面我们将专门接头这一问题。
3.2 操作DLLs实现应用措施间的数据传输
操作DLLs实现应用措施间的数据传输的步调为:
1. 编写一个DLLs措施,个中拥有一个用gmem_DDEShare属性分派的全局内存块;
2. 处事器措施挪用DLLs,向全局内存块写入数据;
3. 客户措施挪用DLLs,从全局内存块读取数据。
3.2.1 用于实现数据传输的DLLs的编写
用于实现数据传输的DLLs与一般DLLs的编写基内情同,个中出格的处所是:
1. 界说一个全局变量句柄:
var
hMem: THandle;
2. 界说一个进程,返回该全局变量的句柄。该进程要包括在exports子句中。如:
function GetGlobalMem: THandle; export;
begin
Result := hMem;
end;
#p#副标题#e#
3. 在初始化代码中分派全局内存块:
措施清单如下:
begin
hMem := GlobalAlloc(gmem_MOVEABLE and gmem_DDEShare,num);
if hMem = 0 then
MessageDlg('Could not allocate memory',mtWarning,[mbOK],0);
end.
num是一个预界说的常数。
Windows API函数GlobalAlloc用于从全局内存堆中分派一块内存,并返回该内存块的句柄。该函数包罗两个参数,第一个参数用于配置内存块的分派符号。可以利用的分派符号如下表所示。
表3 全局内存块的分派符号
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
标 志 意 义
———————————
gmem_DDEShare 分派可由应用措施共享的内存
gmem_Discardable 分派可丢弃的内存(只与gmem_Moveable连用)
gmem_Fixed 分派牢靠内存
gmem_Moveable 分派可移动的内存
gmem_Nocompact 该全局堆中的内存不能被压缩或丢弃
gmem_Nodiscard 该全局堆中的内存不能被丢弃
gmem_NOT_Banked 分派不能被分段的内存
gmem_Notify 通知成果。当该内存被丢弃时挪用GlobalNotify函数
gmem_Zeroinit 将所分派内存块的内容初始化为零
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
有两个预界说的常用组合是:
GHND = gmem_Moveable and gmem_Zeroinit
GPTK = gmem_Fixed and gmem_Zeroinit
第二个参数用于配置欲分派的字节数。分派的字节数必需是32的倍数,因而实际分派的字节数大概比所配置的要大。
由于用gmem_DDEShare分派的内存在分派内存的模块终止时自动丢弃,因而不必挪用GlobalFree显式释放内存。
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;
3.2.3 客户措施的编写
客户措施险些是处事器措施的翻版。独一的区别在于一个是写入数据,一个是下载数据。
下面是客户从全局内存块下载数据的措施清单。
#p#分页标题#e#
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;
4 操作DLLs实现窗体重用
实现窗体重用是Delphi DLLs成果中一个引人注目标特色。当你建设了一个令本身满足的通用窗体并但愿能在差异应用措施中利用,出格是但愿能在非Delphi 应用措施中利用时,把窗体做进一个动态链接库中是最适当的。这样纵然用其它东西开拓的应用措施,如C++、Visual Basic等,也都可以去挪用它。
包括窗体的DLLs有100K阁下的部件库(Component Library)开销。可以通过把几个窗体编译成一个DLLs来最小化这笔开销。DLl中的差异窗体可以共享部件库。
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函数或进程的挪用完全一致,不再赘述。读者可参看下面的例子。
4.2 窗体重用实例
下面我们通过一个详细的实例来说明窗体重用的设计进程。我们在一个名为passform.dll 的文件中储存了一个口令配置窗口和一个口令查抄窗口。尔后在一个Delphi 编写的措施和一个VB编写的措施中举办挪用。事实证明这种要领是完全可行的。
4.2.1 窗体重用DLLs的设计
窗体重用DLLs的设计依照(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,用于生成口令配置窗口并返回配置的口令:
#p#分页标题#e#
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.
4.2.2 Delphi应用措施挪用重用窗体
在Delphi应用措施中挪用重用窗体,首先必需包括passform.dll的两个输出函数:
#p#分页标题#e#
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;
按照口令查抄的功效,在标签框中显示相应的信息。
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参考书。
4.3 小结
本章我们接头的是动态链接库编程。很多可视化开拓东西(如Visual Basic)不支持 DLLs的建设,而Delphi在这里又有上乘的表示。出格是窗体重用机制是Delphi对Windows下DLLs编程的一个重大改造。在一般的DLLs编程中也浮现了Delphi快捷、利便的特点。动态链接库是
Windows下措施组织的一种重要方法,利用动态链接库可以极大地掩护用户在差异开拓东西、差异时期所做的事情。操作动态链接库,用户可以慢慢去构筑本身的措施模块库,为此后的事情积聚素材。