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;