Delphi 中的DLL 封装和调用对象技术
Delphi用DLL来封装对象的技术主要有三种:
用接口实现
用纯虚和抽象类方法实现
用类引用实现
前两种,都是在DLL中生成类实例;第3种通过在调用方生成实例。三种方法的共同局限如下:
调用方只能调用封装类中的virtual方法;
调用方和提供方都必须提供类的描述,接口实现中需要提供接口描述(COM方法例外);
不能创建DLL包含对象的派生类(接口派生除外)。
首先,用接口实现当然包括COM实现,不过由于COM是一种实现接口技术的独立门类,所以在下面说的接口实现中不包括这方面的东西。其次用纯虚和抽象类方法实现,在《Delphi 6 Developer‘s Guide》中提及的inc文件作为公用的头文件,和刘艺在《Delphi面向对象编程思想》中提及的方法本质上是一样的。无非都是为了在调用方加入abstract关键字,而无需方法实现,从而使得编译通过。inc文件的方法是利用宏指令来达到统一处理,不用inc方法是在两处都需要进行描述。
下面用一个例子来展示所以上面提到的集中方法。
调用方主要文件列表为:
UnitMain.pas 调用DLL的界面
UnitPublic.pas 第二种方法在调用端的类描述文件
UnitIDest.pas 接口方法和类引用方法的类描述文件
IncDemo.inc 利用inc文件实现的类描述文件
DLL工程主要文件列表为:
ProDLLDemo.dpr DLL工程文件
UnitDLLDemo.pas 所有类实现部分的文件
UnitIDest.pas 接口方法和类引用方法的类描述文件
IncDemo.inc 利用inc文件实现的类描述文件
在方法的归纳上如有遗漏,有请指教。
UnitMain.pas 源代码:
unit UnitMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, UnitPublic, UnitIDest;
{$I IncDemo.inc}
// 用于Inc方法的宏指令,如果不是用该方法可去掉上面那行
type
TfmMain = class(TForm)
Memo1: TMemo;
btnUseAbstract: TButton;
btnUseReference: TButton;
btnUseInterface: TButton;
btnUseInc: TButton;
procedure btnUseAbstractClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure btnUseInterfaceClick(Sender: TObject);
procedure btnUseReferenceClick(Sender: TObject);
procedure btnUseIncClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
fmMain: TfmMain;
function GetNewspaper: TNewspaper; external 'ProDLLDemo.dll';
function GetCircle: ICircle; external 'ProDLLDemo.dll';
function GetBall: TBallClass; external 'ProDLLDemo.dll';
function GetCar: TCar; external 'ProDLLDemo.dll';
implementation
{$R *.dfm}
procedure TfmMain.FormCreate(Sender: TObject);
begin
memo1.Lines.Clear;
end;
// abstract, virtual 方法示例
procedure TfmMain.btnUseAbstractClick(Sender: TObject);
var
NewspaperObj: TNewspaper;
Info: pchar;
i: integer;
begin
NewspaperObj := GetNewspaper;
if NewspaperObj = nil then
memo1.Lines.Add('Create newspaper object failed!')
else
begin
try
GetMem(Info, 255);
i := NewspaperObj.Read(Info);
memo1.Lines.Add('Create newspaper object successed!');
memo1.Lines.Add('read : ' + Info + ' return code : ' + inttostr(i));
finally
FreeMem(Info);
NewspaperObj.Free;
end;
end;
end;
// interface方法示例
procedure TfmMain.btnUseInterfaceClick(Sender: TObject);
var
CircleObj: ICircle;
Info: pchar;
i: integer;
begin
CircleObj := GetCircle;
if CircleObj = nil then
memo1.Lines.Add('Create circle object failed!')
else
begin
try
GetMem(Info, 255);
i := CircleObj.Scroll(Info);
memo1.Lines.Add('Create circle object successed!');
memo1.Lines.Add('scroll : ' + Info + ' return code : ' + inttostr(i));
finally
FreeMem(Info);
CircleObj := nil;
end;
end;
end;
// class reference方法示例
procedure TfmMain.btnUseReferenceClick(Sender: TObject);
var
BallObj : TIBall;
Info: pchar;
i: integer;
begin
BallObj := GetBall.Create;
if BallObj = nil then
memo1.Lines.Add('Create ball object failed!')
else
begin
try
GetMem(Info, 255);
i := BallObj.Play(Info);
memo1.Lines.Add('Create ball object successed!');
memo1.Lines.Add('play : ' + Info + ' return code : ' + inttostr(i));
finally
FreeMem(Info);
BallObj.Free;
end;
end;
end;
// .inc文件方法示例
procedure TfmMain.btnUseIncClick(Sender: TObject);
var
CarObj : TCar;
Info: pchar;
i: integer;
begin
CarObj := GetCar.Create;
if CarObj = nil then
memo1.Lines.Add('Create car object failed!')
else
begin
try
GetMem(Info, 255);
i := CarObj.Run(Info);
memo1.Lines.Add('Create car object successed!');
memo1.Lines.Add('car : ' + Info + ' return code : ' + inttostr(i));
finally
FreeMem(Info);
CarObj.Free;
end;
end;
end;
end.
UnitPublic.pas源代码:
unit UnitPublic;
interface
type
// ----- virtual, abstract的类定义 -----
TPaper = class(TObject);
TNewspaper = class(TPaper)
public
function Read(info : pchar) : integer; virtual; abstract;
end;
implementation
end.
UnitIDest.pas源代码:
unit UnitIDest;
interface
type
ICircle = interface
['{A971701F-96EC-4201-9266-57F982805B6E}']
function Scroll(Info : pchar) : integer;
end;
TIBall = class(TObject)
public
constructor Create; virtual; abstract;
destructor Destroy; virtual; abstract;
function Play(info : pchar) : integer; virtual; abstract;
end;
TBallClass = class of TIBall;
implementation
end.
IncDemo.inc源文件:
type
TCar = class(TObject)
public
function Run(info: pchar): integer; virtual; {$IFNDEF INCTEST} abstract; {$ENDIF}
end;
ProDLLDemo.dpr源文件:
library ProDLLDemo;
uses
SysUtils, Classes,
UnitDLLDemo in 'UnitDLLDemo.pas',
UnitIDest in 'UnitIDest.pas';
{$R *.res}
function GetNewspaper : TNewspaper;
begin
result := TNewspaper.Create;
end;
function GetCircle : ICircle;
begin
result := TCircle.Create;
end;
function GetBall : TBallClass;
begin
result := TBall;
end;
function GetCar : TCar;
begin
result := TCar.Create;
end;
exports
GetNewspaper,
GetCircle,
GetBall,
GetCar;
end.
UnitDLLDemo.pas源代码:
unit UnitDLLDemo;
{$DEFINE INCTEST}
interface
uses Sysutils, UnitIDest;
{$I IncDemo.inc}
{ ----- 利用纯虚和抽象类实现 ----- }
type
TPaper = class(TObject)
end;
TNewspaper = class(TPaper)
public
constructor Create;
destructor Destroy;
function Read(info: pchar): integer; virtual;
end;
{ ----- 利用接口实现 ----- }
type
TShape = class(TInterfacedObject)
end;
TCircle = class(TShape, ICircle)
public
constructor Create;
destructor Destroy;
function Scroll(info: pchar): integer;
end;
{ ----- 利用类引用实现 ----- }
type
TBall = class(TIBall)
public
constructor Create;override;
destructor Destroy;override;
function Play(info : pchar) : integer; override;
end;
implementation
{ TNewspaper }
constructor TNewspaper.Create;
begin
inherited Create;
end;
destructor TNewspaper.Destroy;
begin
inherited;
end;
function TNewspaper.Read(info: pchar): integer;
var
str: string;
begin
if info <> nil then
begin
str := self.ClassName;
strCopy(info, PChar(str));
end;
result := 1;
end;
{ TCircle }
constructor TCircle.Create;
begin
inherited Create;
end;
destructor TCircle.Destroy;
begin
inherited;
end;
function TCircle.Scroll(info: pchar): integer;
var
str: string;
begin
if info <> nil then
begin
str := self.ClassName;
strCopy(info, PChar(str));
end;
result := 2;
end;
{ TBall }
constructor TBall.Create;
begin
// inherited Create;
end;
destructor TBall.Destroy;
begin
// inherited;
end;
function TBall.Play(info: pchar): integer;
var
str: string;
begin
if info <> nil then
begin
str := self.ClassName;
strCopy(info, PChar(str));
end;
result := 3;
end;
{ ----- 利用Inc文件实现 ----- }
function TCar.Run(info: pchar): integer;
var
str: string;
begin
if info <> nil then
begin
str := self.ClassName;
strCopy(info, PChar(str));
end;
result := 4;
end;
end.
[参考文献]:
1. Delphi面向对象编程思想,第8章,刘艺, 2003.9, 机械工业出版社.
2. Delphi 6 Developer‘s Guide, p209, Steve Teixeira, SAMS, 2001.