| 1.1 Why use interface? For example: there is such a ticket sales service, cinemas can sell tickets, opera houses can sell tickets, and passenger stations can also sell tickets. So do we need to design cinemas, opera houses and passenger stations into a type of architecture To provide ticket sales services? You should know that even managers can sell tickets, which is obviously not suitable to include managers in the inheritance structure of ticket sales services. All we need is a common ticket sales service. Therefore, the ticket selling service is an interface. As long as cinemas, opera houses and other services follow such a service definition, they can interact and communicate well (if necessary). 1.2 How to use interfaces in Delphi | | 1.2.1 Declare interface IMyInterface = interface (IIInterface) //Instructions (1)['{63E072DF-B81E-4734-B3CB-3C23C7FDA8EA}'] //Instructions (2) function GetName( const str: String): String; stdcall ; function QueryInterface( const IID: TGUID; out Obj): HResult; stdcall ; //Description (3) function _AddRef: Integer; stdcall ; //AddRef: Integer; stdcall ; //Add the interface reference number by 1. function _Release: Integer; stdcall; //Decrement the interface reference number by 1, and perform a release action when it is less than or equal to 0. end ;Explanation (1): If there is a continuing relationship, fill in the parent interface in brackets, otherwise, save it, such as: IMyInterface = interface. Note (2): This GUID is optional. If you want to implement an interface with COM characteristics, you need to add it. For interfaces with GUID in Delphi, it is necessary to generate interface information at a predetermined location in the VMT table during runtime, such as the definition of the interface method. , method parameter definition can be detailed. Note (3): The interface must implement these three functions. 1.2.2 Interface Implementation Interface services are implemented by classes. TIntfClass = class(TObject, IMyInterface)PRivateFCounter: Integer;FRefCount: Integer;publicfunction QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;...end;1.2.3 Get interface a. Use type conversion. For example: var aIntf: IMyInterface;beginaObj := TIntfClass.Create;tryaIntf := (IMyInterface(aObj);...b. Use the built-in mechanism of the Delphi compiler. For example: aIntf := aObj.c. Use the QueryInterface method of the object . For example, OleCheck(aObj.QueryInterface(IID, aIntf)); can only access COM interfaces with GUID. d. Use the as operator. Use the as operator must meet the following conditions: 1. The interface must be explicitly specified from IInterface The interface inherits. 2. The implementation class that must have GUID value in Delphi7 must also be inherited from TInterfacedObject, such as TIntfClass = class(TInterfacedObject, IMyInterface)1.2.4 interface and object life period because Delphi will check the interface itself If the release code is not released after use and the generated program is added, but this also causes problems, such as the following code: var i: Integer; aObj: TIntfClass; aIntf: IMyInterface; begin aObj := TIntfclass.Create; try aIntf := aObj; aIntf.GetName... finally aIntf := nil; FreeAndNil(aObj); end ; If the above code is executed, an access violation error will occur because the interface has been released when nil is set to the interface, and FreeAndNil (aObj) will release aIntf again, and the object has been released when aIntf is nil. To solve this problem, just don't let the interface interfere with the life of the object. In Release, you only need to reduce the reference count without releasing. Action. function TIntfClass._Release: Integer;begin Result := InterlockedDecrement(FRefCount);end;1.2.5 Interface Delegation is divided into two types: 1. Object interface delegation 2. Class object delegation. Object interface Delegation, if there is already the following interface definition: IImplInterface = interface (IIinterface)function ConvertToUSD(const iNTD: Integer): Double;function ConvertToRMB(const iNTD: Integer): Double;end; Then a class implements the interface: TImplClass = class(TObject, IImplInterface)private FRefCount: Integer;public function ConvertToUSD(const iNTD: Integer): Double; ...end;implementationfunction TImplClass.QueryInterface(const IID: TGUID; out Obj): HResult;begin if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE;end;function TImplClass._Release: Integer; beginResult := InterlockedDecrement(FRefCount);if Result = 0 thenDestroy;end;... ...Now there is another class TIntfServiceClass To implement the IImplInterface interface, there is no need to redefine it. Just use the above TImplClass: TIntfServiceClass = class(TObject, IImplInterface)privateFImplService: IImplInterface; //FSrvObj: TImplClass; //If you are delegating with a class object, publicConstructor Create; overload; Destructor Destroy; override;Constructor Create(aClass: TClass); overload; property MyService: IImplInterface read FImplService implements IImplInterface; // property MyService: TImplClass read FSrvObj implements IImplInterface; // If it is delegated with an object. end;Implementation is as follows: constructor TIntfServiceClass.Create; beginFImplService := TImplClass.Create; end;constructor TIntfServiceclass.Create(aClass: TClass);var instance: TImplClass; beginininstance := TImplClass(aClass.NewInstance);FImplService := instance.Create ;end;destructor TIntfServiceClass.Destroy; beginFImplService := nil; //Follow TImplClass to use reference count to control the object life cycle, see the Destroy implementation of TImplClass. The interface Gog pointer is defined at the VMT-72 displacement in the interface and RTTIDelphi: vmtIntfTable = -72. Related function: GetInterfaceCount; //Get the number of interfaces. GetInterfaceTable; //Get the interface table. Related structure: TInterfaceEntry = packed recordIID: TGUID;VTable: Pointer;IOffset: Integer;ImplGetter: Integer;end;PInterfaceTable = ^TInterfaceTable;TInterfaceTable = packed recordEntryCount: Integer;Entries: array[0..9999] of TInterfaceEntry;end; Self is a pointer to a VMT pointer, so: Self.GetInterfaceTable.EntryCount is equivalent to: aPtr := PPointer(Integeer((Pointer(Self))^) + vmtIntfTable)^; As long as you use the M+/M- instruction in the declaration, RTTI information can be added to the program compiled in Delphi, such as: {$M+}iInvokable = interface(IIinterface){$M-} The RTTI information of the interface is defined by the TIntfMetaData record structure: TIntfMetaData = recordname: String; //Interface name UnitName: String;//The program unit name declared by the interface MDA: TIntfMethEntryArray;//The dynamic array IID that stores method information in the interface: TGUID;//The GUID value of the interface Info: PTypeInfo;//Pointer describing interface information AncInfo: PTypeInfo ;//Pointer describing parent information NumAnc: Integer;//The number of methods inherited from the parent interface end; TIntfMethEntryArray is defined as follows: typeTCallConv = (ccReg, ccCdecl, ccPascal, ccStdCall, ccSafeCall); TIntfMethEntry = recordName: String;//Method name CC: TCallConv;//Calling convention Pos: Integer;//Position of method in interface ParamCount: Integer;//Number of parameters of method ResultInfo: PTypeInfo;//Describe the information pointer of the method back-pass type SelfInfo: PTypeInfo;//Information pointer describing the method itself Params: TIntfParamEntryArray;//Dynamic array describing parameter information HasRTTI: Boolean;//Does this method have the Boolean value end of the RTTI information; TIntfMethEntryArray = array of TIntfMethEntry; Parameter information TIntfParamEntry Definition: TIntfParamEntry = recordFlags: TParamFlags;Name: String;Info: PTypeInfo;end;TTypeInfo = recordKind: TTypeKind;//Data type Name: ShortString;//Stand format end of type information; |
|