Interface Programming Delphi under Delphi Delphi
1.1 Why use the interface?
For example: There is such a ticket service, the cinema can sell tickets, the opera can sell tickets, the passenger station can also sell tickets, then we need to put the cinema, song
Both the theater and passenger stations are designed as a class architecture to provide ticket service? You must know that even managers can sell tickets, it is obvious that they are not suitable for the inheritance structure of the manager to the ticket service.
In the middle, we need only a common ticket service. So, the service of the ticket is an interface, the cinema, and the opera house, as long as they follow such a service definition, it can be very good.
Interaction and communication (if necessary).
1.2 How to use the interface in Delphi
1.2.1 Declaration Interface
Imyinterface =
Interface (IINTERFACE) / / Description (1)
['{63E072DF-B81E-4734-B3CB-3C23C7FDA8EA}'] // Description (2)
Function GetName
Const str: string: String;
STDCALL;
Function queryinterface
Const IID: Tguid;
Out obj): hResult;
stdcall; // Description (3)
Function _addref: integer;
STDCALL; // Make the interface reference number plus 1.
Function _Release: Integer;
STDCALL; // minus the number of interfaces to 1, when it is less than or equal to 0, the release action is released.
END;
Description (1): If there is a continuing relationship, fill the parent interface in parentheses, otherwise, such as: ImyInterface = Interface.
Description (2): This GUID is optional. If you want to implement an interface with COM features, you need to add it, Delphi is in the VMT table for the interface with the GUID.
The information of the interface generates an interface, such as the definition of the interface method, and the method parameter definition can be detailed.
Description (3): The interface must implement these three functions.
1.2.2 implementation of the interface
The interface service is implemented by a class.
TintfClass = Class (Tobject, ImyInterface)
Private
FCOUNTER: Integer;
FREFCOUNT: Integer;
public
Function QueryInterface (Const IID: Tguid; Out Obj): hResult; stdcall;
...
END;
1.2.3 Get the interface
a. Use type conversion.
VAR AINTF: IMYINTERFACE;
Begin
AOBJ: = TINTFCLASS.CREATE
Try
AINTF: = (iMyInterface (AOBJ);
...
b. Using the Delphi compiler to build a mechanism. Such as: AINTF: = AOBJ.
c. Use the QueryInterface method for the object. Such as Olecheck (Aobj.Queryinterface (IID, AINTF)); only COM interfaces with GUID can be accessed.
d. Using the AS operator.
The use of the AS operator must meet the following conditions: 1. The interface must be explicitly specified inherited from the IINTERFACE interface. 2. You must have a GUID value
The implementation class of the interface in Delphi7 must also inherit from TinterFaceDObject, such as
Tintfclass = Class (TinterFaceDObject, ImyInterface)
1.2.4 Interface and Object Life
Because Delphi will check the interface if it is not released after use, add the release code in the generated program, but it also brought the problem, as follows: Var
i: integer;
AOBJ: TINTFCLASS;
AINTF: IMYINTERFACE;
Begin
AOBJ: = TINTFCLASS.CREATE
Try
AINTF: = AOBJ;
AINTF.GETNAME ...
Finally
AINTF: = NIL;
Freeandnil (AOBJ);
END;
The above code execution will generate the illegal error, because the interface is released when the NIL is set, and freeandnil (AOBJ) will release the AINTF again, and set it to AINTF
This object has been released when NIL. Solving this problem As long as the lifetime of the interface is unable to interfere with the life of the object, just reduce the reference count in Release, not the release of the release.
Function Tintfclass._release: integer;
Begin
Result: = interlockedDecrement (freecount);
END;
1.2.5 Entrustment of Interface (Interface Delegation)
Divided into two: 1. Object interface commissioned 2. Class object commission.
The object interface is delegated, if there is already the following interface definition:
IIMPLINTERFACE =
Interface (IINTERFACE)
Function ConvertTousd (Const INTD: Integer): Double;
Function ConvertTORMB (Const INTD: Integer): Double;
END;
Then there is a class that implements the interface:
TIMPLCLASS = Class (Tobject, IIMPLINTERFACE)
Private
FREFCOUNT: Integer;
public
Function ConvertTousd (Const INTD: Integer): Double;
...
END;
IMPLEMentation
Function 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;
Begin
Result: = interlockedDecrement (freecount);
if Result = 0 THEN
DESTROY;
END;
...
Now there is another class TINTFSERVICECLASS to implement the IIMPLINTERFACE interface, do not redefine, you only need to use the TIMPLCLASS:
TintfServiceClass = Class (Tobject, IIMPLINTERFACE)
Private
FIMPLSERVICE: IIMPLINTERFACE;
// fsrvobj: TIMPLCLASS; // If it is used by the class object
public
CONSTRUCTOR CRETE; 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 entrusted with an object.
END;
The implementation is as follows:
Constructor TintFserviceClass.create;
Begin
FIMPLSERVICE: = TIMPLCLASS.CREATE
END;
Constructor TintfServiceClass.create (aclass: tclass);
VAR
TIMPLCLASS;
Begin
Instance: = TIMPLCLASS (ACLASS.NEWINSTANCE);
FIMPLSERVICE: = Instance.create;
END;
Destructor TintfserviceClass.DESTROY DESTRUCTOR
Begin
FIMPLSERVICE: = NIL; // Follow TIMPLCLASS to use reference counts to control object life cycles, see TIMPLCLASS DESTROY implementation.
inherited;
END;
1.2.6 interface and RTTI
Delphi defines the interface briefing in VMT-72 displacement: vmtintftable = -72.
related functions:
GetInterfaceCount; // Get the number of interfaces.
GetInterFaceTable; // Get the interface form.
Related structure:
TinterfaceEntry = Packed Record
IID: Tguid;
VTable: Pointer;
IOFFSET: Integer;
Implgetter: integer;
END;
PinterfaceTable = ^ TINTERFACETABLE;
TinterFaceTable = Packed Record
Entrycount: Integer;
Entries: array [0..9999] of tinterfaceentry;
END;
Self is a pointer to the VMT pointer, so: Self.GetInterFaceTable.EnTrycount is equivalent to:
Aptr: = ppointer (Integeer) ^) VMTINTFTABLE) ^;
Just add RTTI information to programs compiled in Delphi in the declaration, such as:
{$ M }
Iinvokable = Interface (Iinterface)
{$ M-}
The RTTI information of the interface is defined by the TINTFMetAdata record structure:
TintfMetadata = Record
Name: String; // Interface Name
UnitName: String; // Interface declaration program unit name
MDA: TINTFMETHENTRYARRAY; / / Save the dynamic array of method information in the interface
IID: tguid; // interface GUID value
INFO: PTYPEINFO; / / Describe the pointer to interface information
Ancinfo: ptypeinfo; // Describe the pointer to the parent information
Numanc: integer; // This interface inherits the number of methods from the parent interface
END;
The definition of TintfmethentryArray is as follows:
Type
TcallConv = (CCREG, CCCDECL, CCPASCAL, CCSTDCALL, CCSAFECALL);
TINTFMETHENTRY = Record
Name: String; // method name
CC: tcallconv; // Call convention
POS: integer; // Method in the interface paramect: integer; // method number
ResultInfo: ptypeinfo; // Description Method Information Pointer
SelfInfo: ptypeinfo; // Describe the information pointer of the method itself
Params: TINTFPARAMENTRYARRY; / / Description Dynamic array of parameter information
HASRTTI: Boolean; / / Do this method has a Boolean value of RTTI information
END;
TINTFMETHENTRYARRY = array of tintfmethentry;
Parameter information TINTFPARAMENTRY definition:
TINTFPARAMENTRY = Record
Flags: tparamflags;
Name: String;
INFO: PTYPEINFO;
END;
TTYPEINFO = Record
Kind: TTYPEKIND; // Data type
Name: SHORTSTRING; // Type information string format
END;