======================================================
注:本文源代码点此下载
======================================================
dll封装登录框架实现代码复用
(说明:发布在电脑编程技巧与维护2007年第4期上)
摘 要 本文介绍用dll封装通用的软件注册,系统登录对话窗体、修改密码窗体和关于窗体。形成登录框架,供不同软件系统调用,实现代码复用。
关键字 dll,delphi,登录对话,注册表,软件保护,代码复用
一、前言
在软件系统的开发过程中,为了维护软件所有者的权益和保证系统的安全性,软件需要注册授权后才能运行,操作员需要登录授权后才能登录使用。对于软件公司,这部分重复工作量很大。能不能把这部分功能封装起来,实现代码复用呢?
本文提出用dll封装登录框架的新方法,新开发一个项目时,只需要几行代码调用就可以分别实现软件注册,系统登录,修改密码和关于对话框等功能。
二、dll简介
dll(动态链接库,dynamic link library)简单来说是一种可通过调用执行的已编译的代码模块。当某个函数或过程需要被使用时,才从硬盘调用它进入内存,一旦没有程序再调用该dll了,将其从内存中清除。多个应用程序调用同一个dll,在内存里只有一个代码副本。而不象静态编译的程序那样每一个都必须全部的被装入。装载dll时,它将被映射到进程的地址空间,同时使用dll的动态链接并非将库代码拷贝,而仅仅记录函数的入口点和接口。
dll还能带来共享的好处。一家公司开发的不同软件可能需要一些公用的函数/过程,这些函数/过程可以被封装为dll,供不同的软件调用。本文就利用dll这一优势,来实现代码复用。
三、设计与实现
1、数据库设计
首先在sql server2000中新建一个数据库,命名为deanmis。然后在deanmis数据库中建立表tsys_user,如图1所示:
图 1 tsys_user表结构
在表tsys_user中添加一条操作员信息,如图2所示:
图 2 tsys_user表中数据
2、建立dll
打开delphi7,新建一个工程,保存,再打开file|new|other|new|dll wizard,修改library名称为deanlogin
加入4个form窗体及相应控件,如图3所示:
图示3 软件界面
我们用setconn方法来设置数据库连接字符串,再用checkpwd函数来调用系统登录对话框。如果登录成功,返回登录信息,通过exports引出例程名称。注意对于传出参数要在参数前加var,主要代码如下:
library deanlogin;
uses
fastmm4,sysutils,classes,controls,forms,windows,
ufrmlogin in 'ufrmlogin.pas' {frmlogin},//登录对话框
ufrmregistry in 'ufrmregistry.pas' {frmregistry},//软件注册
ufrmchangepw in 'ufrmchangepw.pas' {frmchangepw},//修改密码对话框
ufrmabout in 'ufrmabout.pas' {frmabout},//关于对话框
ucommon in 'ucommon.pas',//简单加解密函数
umac in 'umac.pas';//获取计算机网卡mac作为机器识别码
var
myconn:string;//定义公共变量,数据库连接字符串
{$r *.res}
procedure setconn(conn:pchar); stdcall;
begin
myconn := strpas(conn);
end;
//验证系统登录,如果登录成功返回登录id,用户名和姓名
function checkpwd(apphandle:thandle;var opid:pchar;var opname:pchar;var opusername:pchar):boolean; stdcall;
var
frmlogin:tfrmlogin;
resultmodal:integer;//窗体返回值
begin
opid:=pchar('');opname:=pchar('');opusername:=pchar(''); result:=false;
if myconn = '' then exit;
application.handle:= apphandle;//把调用程序的句柄传递给dll,避免任务栏出现dll窗体的实例
frmlogin:=tfrmlogin.create(nil);
try
frmlogin.adoq.connectionstring:=myconn;
resultmodal:=frmlogin.showmodal;//打开登录对话窗体
if resultmodal=mrok then
begin
opid:=pchar(frmlogin.sopid);
opname:=pchar(frmlogin.sopname);
opusername:=pchar(frmlogin.sopusername);
result:=true;
end;
finally
frmlogin.free; //释放资源
application.handle:=0;
end;
end;
exports//导出例程名称
setconn,checkpwd;
begin
end.
3、登录窗体和软件注册
首先登录对话框通过注册表检测软件是否已经注册,如果未注册显示注册按钮并提示用户注册。如果注册成功,把注册码写入注册表,供下次登录校验。
procedure tfrmlogin.btregistryclick(sender: tobject);
var
_registry:tregistry;
begin
//打开注册窗口
hardcode:=trim(getmacaddress);//得到机器识别码,即mac
with tfrmregistry.create(self) do
begin
eid.text:=hardcode;
if showmodal=mrok then //如输入的注册码正确,则把注册码保存到注册表
begin
_registry := tregistry.create;
with _registry do
begin
rootkey:=hkey_local_machine;
if openkey('software\deanzhang',true) then
writestring('mis',ecode.text);
free;
end;
hasreg:=true;
checkreg;//根据注册情况,设置窗体上的控件
end;
free;
end;
end;
如果验证软件已经注册,需向数据库校验用户登录信息,登录成功返回登录用户信息。
procedure tfrmlogin.btloginclick(sender: tobject);
var
username, userpw: string;
begin
modalresult := mrnone;
username := trim(eusername.text);
userpw := euserpw.text;
//………………校验输入数据
if userlogin(username, userpw) then begin
loginok := true;
modalresult := mrok;
end;
end;
function tfrmlogin.userlogin(const username, userpw: string): boolean;
begin //向数据库验证登录信息
screen.cursor:=crhourglass;
try
adoq.close;
adoq.sql.text:='select * from tsys_user where fusername=:username';
adoq.parameters.parambyname('username').value:=trim(username);
adoq.open;
except
messagebox(handle,'网络错误!','错误提示',mb_iconhand+mb_ok+mb_defbutton2);
result := false;
exit;
end;
if adoq.isempty then
begin
screen.cursor := crdefault;
messagebox(handle,'无效的用户名!','错误提示',mb_iconhand+mb_ok+mb_defbutton2);
eusername.setfocus;
result := false;
exit;
end;
if not (adoq.fieldbyname('fpwd').asstring=trim(userpw)) then
begin
screen.cursor:=crdefault;
messagebox(handle,'密码错误!','错误提示',mb_iconhand+mb_ok+mb_defbutton2);
euserpw.setfocus;
result := false;
exit;
end;
if adoq.fieldbyname('fusersign').asstring<>'1' then
begin
screen.cursor:=crdefault;
messagebox(handle,'此用户已禁用!','信息提示',mb_iconwarning+mb_ok+mb_defbutton2);
result := false;
exit;
end;
screen.cursor:=crdefault;
_opid:=adoq.fieldbyname('fid').value; // 操作员编号
_opname:=adoq.fieldbyname('ftruename').value; //操作员姓名
_opusername:=adoq.fieldbyname('fusername').value;//操作员登录用户名
if adoq.active then adoq.close;
result := true;
end;
要返回的登录信息,我们通过定义属性来完成。如下:
property sopid: string read getopid write setopid;
4、其它窗体
其它窗体和登录对话窗体类似,通过changpwd函数来调用修改密码窗体、showabout过程来调用关于窗体。注意要传入调用程序的句柄,不然会在任务栏出现被调用窗体的实例。
四、调用
dll的调用有两种方式,一种是静态(隐式)调用,一种是动态(显式)调用。静态调用方法简单,但dll会在启动调用程序时即被调入。动态调用相对比较复杂,仅在调用外部例程时才将dll装载内存,节约内存空间。
dll动态调用的原理是首先声明一个函数/过程类型并创建一个指针变量。为保证该指针与外部例程指针一致以确保赋值正确,函数/过程的声明必须和外部例程的原始声明一致。接下来通过windows api函数loadlibrary引入指定的库文件,loadlibrary的参数是dll文件名,返回一个thandle。如果该步骤成功,再通过另一个api函数getprocaddress获得例程的入口地址,参数分别为loadlibrary的指针和例程名,最终返回例程的入口指针。将该指针赋值给我们预先定义好的函数/过程指针,然后就可以使用这个函数/过程了。最后使用api函数freelibrary来减少dll引用记数,以保证dll使用结束后可以清除出内存。
本文调用实例设置数据库连接采用静态调用,其它窗体调用采用动态调用,静态调用声明:
procedure setconn(conn:pchar); stdcall; external 'deanlogin.dll';
动态调用声明:
hchangpwd: thandle;
checkpwd:function(apphandle:thandle;var opid:pchar;var opname:pchar;var opusername:pchar):boolean; stdcall;
调用登录窗体:
procedure tfrmmain.formcreate(sender: tobject);
var
popid,popname,popusername:pchar;
bcheckpwd:boolean;
begin
setconn(pchar(cnnstr));//设置数据库连接字符串
hcheckpwd:= loadlibrary('deanlogin.dll');
try
@checkpwd:=getprocaddress(hcheckpwd,'checkpwd');
bcheckpwd:= checkpwd(application.handle,popid,popname,popusername);
finally
freelibrary(hcheckpwd);
end;
if bcheckpwd then
begin//如果登录成功,给状态栏赋值opid:=strpas(popid);opname:=strpas(popname);opusername:=strpas(popusername);
statusbar1.panels[1].text:=opid;statusbar1.panels[3].text:=opusername;
statusbar1.panels[5].text:=opname;
end
else application.terminate;
end;
注意,在dll用到ado来连接数据库,在调用程序uses里面需引入adodb单元。dll的搜索路径的顺序是:当前目录;path路径;windows目录;widows系统目录(system、system32),要确保调用的dll在以上路径能找到。
五、结束语
本文通过对登录框架的dll封装,熟悉dll的开发和调用过程。把复用的代码封装,实现代码复用。以上程序在delphi 7,sql server 2000 sp4, windows 2003 sp1环境下编译通过。在源程序中还引入fastmm(http://fastmm.sourceforge.net)内存管理,替换了borland的内存管理器,提升了内存管理效率,并不再需要任何dll的支持。
参考文献
1、 delphi帮助文件
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/