Delphi使用模块化开发,可以采用DLL或者BPL,两者的区别是BPL只能被同版本的Delphi使用,DLL可以被不同版本和不同开发工具的开发的软件调用。

因此我们的软件大多使用Delphi作为界面以及部分DLL模块的开发工具。

DLL模块之间通过接口方式调用。

1.对象创建采用工厂模式,每个DLL负责某个对象或若干个对象的创建及释放,例如:

DLL工程为http客户端(prjHttp.DLL)模块,通过DLL导出的GetHttpClientFactory获取http客户端工厂接口,通过接口创建Http客户端和释放Http客户端,工程

包括3个文件:工程文件,实现单元,接口单元。

调用此DLL的程序仅需要包含接口单元。

DLL工程文件

 library prjHttp;

 uses   System.SysUtils,   System.Classes,   utHTTPClient in 'utHTTPClient.pas';

 {$R *.res}

 exports
GetHttpClientFactory;
end.

utHttpClient示例

unit utHttpClient;

interface

uses utBaseObject, utHttpInterface, Classes, SysUtils;

type
......... THTTPClientConnection = class(TIntObject, IHTTPClientConnection)
public
function Connect: Boolean;
function Info: IHTTPClientConnectionInfo;
function TcpConnection: ITcpConnection;
function DataConnection: IConnection;
function Param:IHttpClientConnectionParam;
public
constructor Create;
destructor Destroy; override;
end; THttpClientConnectionFactory = class(TIntObject, IHttpClientConnectionFactory)
protected
FObjectPool: THTTPClientConnectionPool;
public
constructor Create;
destructor Destroy; override;
procedure CreateHttpClient(out Conn: IHTTPClientConnection);
procedure DestroyHttpClient(var aClient);
end; function GetHttpClientFactory: IHttpClientConnectionFactory; implementation ............ var
HttpClients: THttpClientConnectionFactory; function GetHttpClientFactory: IHttpClientConnectionFactory;
begin
if not Assigned(HttpClients) then
HttpClients := THttpClientConnectionFactory.Create;
Result := HttpClients;
end; initialization
finalization
if Assigned(HttpClients) then FreeAndNil(HttpClients);
end.

utHttpInterface接口文件示例

unit utHttpInterface;

interface

uses utBaseInterface;

const
IID_IHTTPClientConnectionInfo = '{24C3D6BF-EC3D-4783-AD98-A5C6E4F24F19}';
IID_IHTTPClientConnectionParam = '{0FA49A71-48BF-40CD-9D77-63B233C4F717}';
IID_IHTTPClientConnection = '{78C39E26-A690-4022-9E97-6035768CE75C}';
IID_IHTTPClientConnectionEvent = '{2FB0AC19-9994-4E77-B105-121192943EBC}';
IID_IHttpClientConnectionFactory = '{429C5C2B-C1E3-4871-9631-E3B943619EFD}'; GUID_IHTTPClientConnectionInfo: TGUID = IID_IHTTPClientConnectionInfo;
GUID_IHTTPClientConnectionParam: TGUID = IID_IHTTPClientConnectionParam;
GUID_IHTTPClientConnection: TGUID = IID_IHTTPClientConnection;
GUID_IHTTPClientConnectionEvent: TGUID = IID_IHTTPClientConnectionEvent;
GUID_IHttpClientConnectionFactory = IID_IHttpClientConnectionFactory;
type
IHttpClientConnectionParam = interface
['{0FA49A71-48BF-40CD-9D77-63B233C4F717}']
function TcpParam: ITcpConnectionParam;
function GetMethod: PAnsiChar;
function GetPathAndParams: PAnsiChar;
function GetAgent: PAnsiChar;
function GetHeader: PAnsiChar;
function GetData: PAnsiChar;
function GetUserName: PAnsiChar;
function GetPassword: PAnsiChar;
procedure SetValue(const ServerAddr: PAnsiChar; const ServerPort: Integer; const UserName, Password, Method, PathAndParams, Agent, Header, Data: PAnsiChar);
end; IHTTPClientConnectionInfo = interface(ITcpConnectionInfo)
['{24C3D6BF-EC3D-4783-AD98-A5C6E4F24F19}']
function Auth: PAnsiChar;
end; IHTTPClientConnection = interface;
IHTTPClientConnectionEvent=interface
['{2FB0AC19-9994-4E77-B105-121192943EBC}']
procedure OnHeader(const Http:IHTTPClientConnection; const Header:Pointer; const HeaderLenght:NativeInt);
procedure OnStartReceiveContent(const Http:IHTTPClientConnection; const ContentLength:NativeInt);
procedure OnReceiveProgress(const Http:IHTTPClientConnection; const ContentLenght, ContentReceived:NativeInt);
procedure OnError(const Http:IHTTPClientConnection; const ErrStr:PAnsiChar);
end; THttpClientConnectionEvent = (heHeader, heStartReceiveContent, heReceiveProgress, heError); IHTTPClientConnection = interface
[IID_IHTTPClientConnection]
function Connect: Boolean;
function Info: IHTTPClientConnectionInfo;
function TcpConnection: ITcpConnection;
function DataConnection: IConnection;
function Param:IHttpClientConnectionParam;
end; IHttpClientConnectionFactory = interface
[IID_IHttpClientConnectionFactory]
procedure CreateHttpClient(out Conn: IHTTPClientConnection);
procedure DestroyHttpClient(var aClient);
end; implementation end.

调用prjHttp.DLL的Delphi工程可以包含下面的单元以及上面的接口单元utHttpInterface.pas即可

将utHttpDLL.pas中的

//{$define utHttpDLL)

去掉注释,即可以将http客户端这些代码包含到Delphi工程中。

unit utHttpDLL;

//{$define utHttpDLL}
interface uses utHttpInterface, utBaseInterface; var
HttpClientFactory: IHttpClientConnectionFactory; implementation {$ifdef utHttpDLL} uses Windows, SysUtils; const
DLLName = 'prjHttp.DLL'; type
Proc = function: IInterface; var
LibHandle: THandle; function GetHttpClientFactory: IHttpClientConnectionFactory;
begin
Result := HttpClientFactory;
end; procedure Init;
var
P: Proc;
begin
LibHandle := SafeLoadLibrary(DLLName);
if LibHandle <> INVALID_HANDLE_VALUE then
begin
P := GetProcAddress(LibHandle, 'GetHttpClientFactory');
if Assigned(P) then
HttpClientFactory := IHttpClientConnectionFactory(P);
end
else
raise Exception.Create('无法打开库文件' + DLLName);
if not Assigned(HttpClientFactory) then
raise Exception.Create(DLLName + '找不到指定函数');
end; procedure Done;
begin
if LibHandle <> INVALID_HANDLE_VALUE then
FreeLibrary(LibHandle);
Pointer(HttpClientFactory) := nil;
end; {$else}
uses utHttpClient; procedure Init;
begin
HttpClientFactory:= GetHttpClientFactory;
end; procedure Done;
begin
Pointer(HttpClientFactory):=nil;
end; {$endif} initialization
Init;
finalization
Done;
end.

2.DLL中输出接口对象的生命周期管理

Delphi对接口采用引用计数的方法管理对象生命周期,但是DLL中输出的对象可能不是被Delphi调用,其引用计数不一定正确,因此DLL中接口对象的生命周期不由Delphi编译器自动生成的代码管理,而是程序员自己控制,所以上面

的工厂包括构造和解析两个接口对象的生命周期管理方法。

所有接口对象应该集成自下面的接口,而不应该继承自Delphi自带的TInterfacedObject:

  TIntObject = class(TObject, IInterface)
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end; function TIntObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result :=
else
Result := E_NOINTERFACE;
end; function TIntObject._AddRef: Integer;
begin
Result := -;
end; function TIntObject._Release: Integer;
begin
Result := -
end;

3.自管理接口对象在Delphi调用注意事项

1)接口赋值

  错误代码:(Delphi编译器产生代码会先判断接口指针是否为nil,如果非nil自动调用接口的_Release方法)

  var P1:IHttpServer
。。。。。。。。。。。。
   P1:=FServer.Param;
   P1.SetValue(PWideChar(aName), PAnsiChar(AnsiString(ServerAddr)), ServerPort, , ,, );   

  建议代码:

  var P1:IHttpServer
................
Pointer(P1):=nil;  
P1:=FServer.Param; //如果赋值前P1不是nil,程序会线调用P1._Release后再赋值   

2)局部接口变量
  错误代码:

constructor TTcpServerSplitter.Create(aName:String; ServerAddr: String; ServerPort: Integer;
RemoteAddr: String; RemotePort: Integer);
var
Service:IInterfaceObservable;
P1:ITcpConnectionServerParam;
P2:ITcpConnectionParam;
begin
inherited Create;
FServerEvent:=TTcpConnectionServerEventAdapter.Create(Self as ITcpConnectionServerEvent);
FTcpConnectionEvent:=TTcpConnectionEventAdapter.Create(Self as ITcpConnectionEvent);
FConnectionEvent:=TConnectionEventAdapter.Create(Self as IConnectionEvent); TcpServerFactory.CreateTcpConnectionServer(FServer);
P1:=FServer.Param;
P1.SetValue(PWideChar(aName), PAnsiChar(AnsiString(ServerAddr)), ServerPort, , ,, );
RegistObserver(FServer, FServerEvent);
TcpClientFactory.CreateTcpConnection(FRemote);
P2:=FRemote.Param;
P2.SetValue(PAnsiChar(AnsiString(RemoteAddr)), RemotePort, Self);
RegistObserver(FRemote,FTcpConnectionEvent);
end;

上面代码中运行退出后,Delphi编译器会在此代码后面自动调用P1._Release; P2._Release,
  建议代码:

constructor TTcpServerSplitter.Create(aName:String; ServerAddr: String; ServerPort: Integer;
RemoteAddr: String; RemotePort: Integer);
var
Service:IInterfaceObservable;
P1:ITcpConnectionServerParam;
P2:ITcpConnectionParam;
begin
inherited Create;
FServerEvent:=TTcpConnectionServerEventAdapter.Create(Self as ITcpConnectionServerEvent);
FTcpConnectionEvent:=TTcpConnectionEventAdapter.Create(Self as ITcpConnectionEvent);
FConnectionEvent:=TConnectionEventAdapter.Create(Self as IConnectionEvent); TcpServerFactory.CreateTcpConnectionServer(FServer);
P1:=FServer.Param;
P1.SetValue(PWideChar(aName), PAnsiChar(AnsiString(ServerAddr)), ServerPort, , ,, );
RegistObserver(FServer, FServerEvent);
TcpClientFactory.CreateTcpConnection(FRemote);
P2:=FRemote.Param;
P2.SetValue(PAnsiChar(AnsiString(RemoteAddr)), RemotePort, Self);
RegistObserver(FRemote,FTcpConnectionEvent);
Pointer(P1):=nil;
Pointer(P2):=nil;
end;

3)函数返回值为接口指针

如下面的示例中FServer.Param定义为function THttpServer.Param:IHttpServerParam,返回的是接口类型,下面的代码直接调用Param.SetValue方法:

constructor TTcpServerSplitter.Create(aName:String; ServerAddr: String; ServerPort: Integer;
RemoteAddr: String; RemotePort: Integer);
var
Service:IInterfaceObservable;
P1:ITcpConnectionServerParam;
P2:ITcpConnectionParam;
begin
inherited Create;
FServerEvent:=TTcpConnectionServerEventAdapter.Create(Self as ITcpConnectionServerEvent);
FTcpConnectionEvent:=TTcpConnectionEventAdapter.Create(Self as ITcpConnectionEvent);
FConnectionEvent:=TConnectionEventAdapter.Create(Self as IConnectionEvent); TcpServerFactory.CreateTcpConnectionServer(FServer);
FServer.Param.SetValue(PWideChar(aName), PAnsiChar(AnsiString(ServerAddr)), ServerPort, 10000, 10,0, 40000);
RegistObserver(FServer, FServerEvent);
TcpClientFactory.CreateTcpConnection(FRemote);
FRemote.Param.SetValue(PAnsiChar(AnsiString(RemoteAddr)), RemotePort, Self);
RegistObserver(FRemote,FTcpConnectionEvent);
end;

上面的代码,Delphi编译器会自动生成两个接口变量,保存FServer.Param和FRemote.Param,由于FServer和FRemote为TTcpServerSplitter对象的全局变量,所以接口在TTcpServerSplitter对象释放时,被调用_Release

将导致内存访问异常。

constructor TTcpServerSplitter.Create(aName:String; ServerAddr: String; ServerPort: Integer;
RemoteAddr: String; RemotePort: Integer);
var
Service:IInterfaceObservable;
P1:ITcpConnectionServerParam;
P2:ITcpConnectionParam;
begin
inherited Create;
FServerEvent:=TTcpConnectionServerEventAdapter.Create(Self as ITcpConnectionServerEvent);
FTcpConnectionEvent:=TTcpConnectionEventAdapter.Create(Self as ITcpConnectionEvent);
FConnectionEvent:=TConnectionEventAdapter.Create(Self as IConnectionEvent); TcpServerFactory.CreateTcpConnectionServer(FServer);
P1:=FServer.Param;
P1.SetValue(PWideChar(aName), PAnsiChar(AnsiString(ServerAddr)), ServerPort, 10000, 10,0, 40000);
RegistObserver(FServer, FServerEvent);
TcpClientFactory.CreateTcpConnection(FRemote);
P2:=FRemote.Param;
P2.SetValue(PAnsiChar(AnsiString(RemoteAddr)), RemotePort, Self);
RegistObserver(FRemote,FTcpConnectionEvent);
Pointer(P1):=nil;
Pointer(P2):=nil;
end;

4)对象中的接口变量,在对象释放时,需要将接口变量清空。

destructor TTcpServerSplitter.Destroy;
begin
Stop;
Pointer(FServer):=nil;
Pointer(FRemote):=nil;
inherited;
end;

Delphi采用接口实现DLL调用的更多相关文章

  1. delphi dll调用问题

    dll传递string实现方法 delphi中dll传递string的实现方法: dll项目uses第一个引用sharemem单元; 调用的项目uses第一个引用sharemem单元; 调用的单元us ...

  2. delphi 跨版本DLL调用嵌入窗体实现

    delphi 能实现把别的DLL的窗体句柄查到后,贴到PANL之中,此类文章网上不少,而如果是delphi不同版本开发的DLL互调时,一些控件内部的定义有所区别,因为无法(至少目前我觉得理论上不可行) ...

  3. csharp通过dll调用opencv函数,图片作为参数

    [blog 项目实战派]csharp通过dll调用opencv函数,图片作为参数          ​一直想做着方面的研究,但是因为这个方面的知识过于小众,也是由于自己找资料的能力比较弱,知道今天才找 ...

  4. 用delphi的THTTPRIO控件调用了c#写的webservice。

    用delphi的THTTPRIO控件调用了c#写的webservice. 下面是我调试时遇到的一些问题: 1,导入wsdl文件:file--new----other----wenservice---W ...

  5. [转]Delphi 中动态链接库(dll)的建立和使用

    动态链接库是一个能够被应用程序和其它的DLL调用的过程和函数的集合体,它里面包含的是公共代码或资源.由于DLL代码使用了内存共享技术,在某些地方windows也给了DLL一些更高的权限,因而DLL中可 ...

  6. Delphi 的接口机制——接口操作的编译器实现过程(2)

    接口对象的内存空间 假设我们定义了如下两个接口 IIntfA 和 IIntfB,其中 ProcA 和 ProcB 将实现为静态方法,而 VirtA 和 VirtB 将以虚方法实现: IIntfA =  ...

  7. Delphi 封装Frame到Dll文件

    做项目的时候,发现这个Frame很好用,为了省空间.调用和修改方便,就将Frame封装到dll(动态链接库)里面,确实很好使. 效果图如下: 上图是临时测试用的,忘了将Frame的align设置成al ...

  8. 使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。

    分布式微服务现在成为了很多公司架构首先项,据我了解,很多java公司架构都是 Maven+Dubbo+Zookeeper基础上扩展的. Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按 ...

  9. Delphi 的接口机制——接口操作的编译器实现过程(1)

    学习COM编程技术也快有半个月了,这期间看了很多资料和别人的程序源码,也尝试了用delphi.C++.C#编写COM程序,个人感觉Delphi是最好上手的.C++的模版生成的代码太过复杂繁琐,大量使用 ...

随机推荐

  1. 什么是shell? bash和shell有什么关系?

    什么是Shell?      shell是你(用户)和Linux(或者更准确的说,是你和Linux内核)之间的接口程序.你在提示符下输入的每个命令都由shell先解释然后传给Linux内核.      ...

  2. java通过jxls框架实现导入导出excel

    //使用jxls报表生成工具,把java实体类导出生成 Excel文件或导入 Excel 插入数据库 02 03//读取04 05public class ReadExcel {06 private ...

  3. JBOSS参数调优

        阅读目录 JBOSS参数调优 jvm调优讲解1 JVM调优讲解2 JVM常见配置汇总 JBOSS生产环境下JVM调优 JBOSS瘦身 JBoss性能优化:内存紧张的问题终于解决了(转载)--- ...

  4. Linux->ZooKeeper集群搭建

    人,总免不了有心结,限制着自己,难以前行.对于ZooKeeper的理解,以及实践也拖了很久,今天用零散时间学习一下,补点干货. 一.简述 Zookeeper是Google的Chubby一个开源的实现, ...

  5. 总结:从Node爬取数据到前端图表展示

    最近寒假在家学习Node.js开发,光看书或者跟着敲代码还不够,得找一点有趣的事情来玩一玩,于是我决定写一个Node爬虫,爬取一些有意思或者说是有用的数据.这个决定只与我的兴趣有关,与Python或者 ...

  6. 阅读SessionFactory源码

    一.阅读类注释 ①.SessionFactory的主要任务是创建Session的实例.通常一个应用程序只有一个单一的SessionFactory对象,而且线程从这个SessionFactory中获取S ...

  7. Cocos2d-x 3.1.1 学习日志3--C++ 初始化类的常量数据成员、静态数据成员、常量静态数据成员

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/u011292087/article/details/37598919 有关const成员.stati ...

  8. Redis 有序聚合实现排行榜功能

    排行榜功能是一个很普遍的需求.使用 Redis 中有序集合的特性来实现排行榜是又好又快的选择.Redis有序集合非常适用于有序不重复数据的存储 一般排行榜都是有实效性的,比如“用户积分榜”.如果没有实 ...

  9. 【转】Android开发:shape和selector和layer-list的(详细说明)

    <shape>和<selector>在Android UI设计中经常用到.比如我们要自定义一个圆角Button,点击Button有些效果的变化,就要用到<shape> ...

  10. PHP中__get()和__set()的用法实例详

    刚刚看到一个对我有用的文章,我就把它摘抄下来了.                                                                        php面 ...