Delphi採用接口實現DLL調用

Delphi使用模塊化開發,能夠採用DLL或者BPL,二者的區別是BPL只能被同版本的Delphi使用,DLL能夠被不一樣版本和不一樣開發工具的開發的軟件調用。程序員

所以咱們的軟件大多使用Delphi做爲界面以及部分DLL模塊的開發工具。ide

DLL模塊之間經過接口方式調用。模塊化

 

1.對象建立採用工廠模式,每一個DLL負責某個對象或若干個對象的建立及釋放,例如:函數

DLL工程爲http客戶端(prjHttp.DLL)模塊,經過DLL導出的GetHttpClientFactory獲取http客戶端工廠接口,經過接口建立Http客戶端和釋放Http客戶端,工程工具

包括3個文件:工程文件,實現單元,接口單元。開發工具

調用此DLL的程序僅須要包含接口單元。spa

 

DLL工程文件指針

1 library prjHttp;
2 
3 uses   System.SysUtils,   System.Classes,   utHTTPClient in 'utHTTPClient.pas';
4 
5 {$R *.res}
6 
7 exports   
8   GetHttpClientFactory; 
9 end.


utHttpClient示例code

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 := 0
  else
    Result := E_NOINTERFACE;
end;

function TIntObject._AddRef: Integer;
begin
  Result := -1;
end;

function TIntObject._Release: Integer;
begin
  Result := -1
end;


3.自管理接口對象在Delphi調用注意事項

1)接口賦值

  錯誤代碼:(Delphi編譯器產生代碼會先判斷接口指針是否爲nil,若是非nil自動調用接口的_Release方法)

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

  

  建議代碼:

  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, 10000, 10,0, 40000);
  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, 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;

 

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;
相關文章
相關標籤/搜索