[Delphi]DLL封装登录框架实现代码复用

作者在 2008-01-22 10:16:34 发布以下内容
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帮助文件

默认分类 | 阅读 5694 次
文章评论,共0条
游客请输入验证码