Delphi采用接口实现DLL调用
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调用的更多相关文章
- delphi dll调用问题
dll传递string实现方法 delphi中dll传递string的实现方法: dll项目uses第一个引用sharemem单元; 调用的项目uses第一个引用sharemem单元; 调用的单元us ...
- delphi 跨版本DLL调用嵌入窗体实现
delphi 能实现把别的DLL的窗体句柄查到后,贴到PANL之中,此类文章网上不少,而如果是delphi不同版本开发的DLL互调时,一些控件内部的定义有所区别,因为无法(至少目前我觉得理论上不可行) ...
- csharp通过dll调用opencv函数,图片作为参数
[blog 项目实战派]csharp通过dll调用opencv函数,图片作为参数 一直想做着方面的研究,但是因为这个方面的知识过于小众,也是由于自己找资料的能力比较弱,知道今天才找 ...
- 用delphi的THTTPRIO控件调用了c#写的webservice。
用delphi的THTTPRIO控件调用了c#写的webservice. 下面是我调试时遇到的一些问题: 1,导入wsdl文件:file--new----other----wenservice---W ...
- [转]Delphi 中动态链接库(dll)的建立和使用
动态链接库是一个能够被应用程序和其它的DLL调用的过程和函数的集合体,它里面包含的是公共代码或资源.由于DLL代码使用了内存共享技术,在某些地方windows也给了DLL一些更高的权限,因而DLL中可 ...
- Delphi 的接口机制——接口操作的编译器实现过程(2)
接口对象的内存空间 假设我们定义了如下两个接口 IIntfA 和 IIntfB,其中 ProcA 和 ProcB 将实现为静态方法,而 VirtA 和 VirtB 将以虚方法实现: IIntfA = ...
- Delphi 封装Frame到Dll文件
做项目的时候,发现这个Frame很好用,为了省空间.调用和修改方便,就将Frame封装到dll(动态链接库)里面,确实很好使. 效果图如下: 上图是临时测试用的,忘了将Frame的align设置成al ...
- 使用接口的方式调用远程服务 ------ 利用动态调用服务,实现.net下类似Dubbo的玩法。
分布式微服务现在成为了很多公司架构首先项,据我了解,很多java公司架构都是 Maven+Dubbo+Zookeeper基础上扩展的. Dubbo是Alibaba开源的分布式服务框架,它最大的特点是按 ...
- Delphi 的接口机制——接口操作的编译器实现过程(1)
学习COM编程技术也快有半个月了,这期间看了很多资料和别人的程序源码,也尝试了用delphi.C++.C#编写COM程序,个人感觉Delphi是最好上手的.C++的模版生成的代码太过复杂繁琐,大量使用 ...
随机推荐
- Canvas杂谈
最近在看一些关于Canvas的知识...这篇文章主要是用来记录学习中遇见的一些问题...以及难以理解的一些东西.. 转帖请注明出处:http://www.cnblogs.com/Troy-Lv5/p/ ...
- java Date中方法toLocaleString过时的替代方案
DateFormat ddf = DateFormat.getDateInstance(); DateFormat dtf = DateFormat.getTimeInstance(); DateFo ...
- C#中的多线程 - 高级多线程 z
原文:http://www.albahari.com/threading/part4.aspx 专题:C#中的多线程 1非阻塞同步Permalink 之前,我们描述了即使是很简单的赋值或更新一个字段也 ...
- 图解:TCP协议中的三次握手和四次挥手
建立TCP需要三次握手才能建立,而断开连接则需要四次握手.整个过程如下图所示: 先来看看如何建立连接的. 首先Client端发送连接请求报文,Server段接受连接后回复ACK报文,并为这次连接分配资 ...
- iostat -x命令诊断
-x 参数iostat还有一个比较常用的选项-x,该选项将用于显示和io相关的扩展数据. iostat -d -x -k 1 10Device: rrqm/s wrqm/s r/s w/ ...
- .NET事务
概述 事务ACID特性 事务将一系列的工作视为一个工作单元,它具有 ACID 特性: A:Atomicity 不可分性也就是说事务中有多项工作,如果有一项工作失败了,整个事务就算失败了. C:Cons ...
- python UI自动化实战记录三:pageobject-基类
脚本思路: 使用pageobject模式,写一个basepage基类,所有页面的通用方法封装到基类中.比如打开页面,关闭页面,等待时间,鼠标移到元素上,获取单个元素,获取一组元素,获取元素的子元素,截 ...
- AngularJs学习笔记--Dependency Injection(DI,依赖注入)
原版地址:http://code.angularjs.org/1.0.2/docs/guide/di 一.Dependency Injection(依赖注入) 依赖注入(DI)是一个软件设计模式,处理 ...
- Core Animation Manages Your App’s Content
Core Animation 是对现有图片的操作函数. https://developer.apple.com/library/content/documentation/Cocoa/Conceptu ...
- JsonHelp
using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; using System; u ...