Ø TREADER
First come to see Delphi's engineering document, you will find a few lines of code like this:
Begin
Application.INITIALIZE;
Application.createform (TFORM1, FORM1);
Application.run;
End.
This is the entrance to the Delphi program. Simply talk about the meaning of these lines: Application.Initialize makes some necessary initialization works, Application.createform (TFORM1, FORM1) creates the necessary form, Application.run program starts running, enter the message cycle.
Now we are most concerned about creating a form. How is the component on the form and the components on the form created? It has been mentioned earlier: All components in the form include the properties of the form itself, in the DFM file, while Delphi is compiled when compiling the program, using the compilation instruction {$ r * .dfm} has compiled DFM file information To the executable. Therefore, it is necessary to read the DFM information when it is concluded that the form is created, what to read, of course it is Treader!
By tracking the procedure, you can find that the program calls the Treader's READComponent method during the creation of the form. The method of this method is to read the root assembly and all of the components thereof. Let's take a look at the implementation of this method:
Function Treader.Readrootcomponent (root: tcomponent): tComponent
......
Begin
Readsignature;
Result: = NIL;
Globalnamespace.beginwrite; // loading from stream address to name space
Try
Try
Readprefix (Flags, i);
if root = nil dam
Begin
Result: = TcomponentClass (FindClass (ReadStr)). Create (nil);
Result.name: = readstr;
END ELSE
Begin
RESULT: = root;
Readstr; {Ignore Class Name}
IF csdesigning in result.componentstate Then
ReadStr else
Begin
Include (Result.fcomponentState, Csloading);
Include (Result.FComponentState, CSReading);
Result.name: = Finduniquename (ReadStr);
END;
END;
FROOT: = Result;
Ffinder: = TclassFinder.create (TPERSISTENTCLASS (RESUE), TRUE
Try
FLOOKUPROOT: = Result;
G: = GlobalLoaded;
IF g <> nil dam
Floaded: = g else
Floaded: = tlist.create;
Try
IF floaded.indexof (stroot) <0 THEN
Floaded.Add (Froot);
FOWNER: = FROOT;
Include (Froot.fcomponentState, CSLoading);
Include (Froot.fcomponentState, CSReading);
Froot.readState (Self);
Exclude (Froot.fcomponentState, CSReading); if g = nil then
For i: = 0 to floaded.count - 1 do tcomponent (floaded [i]). loading;
Finally
IF g = nil the floaded.free;
Floaded: = NIL;
END;
Finally
Ffinder.free;
END;
......
Finally
GlobalNamespace.Endwrite;
END;
END;
ReadrootComponent first calls ReadSignature to read the FILER object tag ('TPF0'). Detect the label before loading the object, prevent negligence, resulting in the reading invalid or outdated data.
Look at Readprefix (Flags, i), the READPREFIX method is very similar to ReadSignature, but it is the flag (prefix) in front of the component in the flow of the stream. When a WRITE object writes the component into the stream, it has written two values in front of the component, and the first value indicates whether the component is inherited from the ancestral form and its location in the form. Important logo; the second value refers to it creates the order in the ancestral form.
Then, if the root parameter is NIL, create a new component with the class name read by ReadStr, and read the Name property of the component in the stream; otherwise, ignore the class name, and determine the uniqueness of the Name property.
Froot.readState (Self);
This is a critical, ReadState method reads the properties of the root assembly and the components they own. Although this ReadState method is a Tcomponent method, further tracking can find that it is actually finally positioned to Treader's READDATAINNER method, the implementation is as follows:
Procedure Treader.ReadDataInner (Instance: Tcomponent);
VAR
Oldparent, Oldowner: Tcomponent;
Begin
While NOTOFLIST Do Readproperty (Instance);
Readlisten;
Oldparent: = Parent;
Oldowner: = Owner;
Parent: = instance.getChildParent;
Try
Owner: = Instance.getChildowner;
IF not assigned (owner) Then Owner: = root;
While NOTOFLIST Do Readcomponent (NIL);
Readlisten;
Finally
Parent: = OldParent;
Owner: = Oldowner;
END;
END;
There is such a line of code:
While NOTOFLIST Do Readproperty (Instance);
This is used to read the properties of the root assembly, for attributes, mentioned above, existing componLished properties of the component itself, and non-public properties, such as TTIMER LEFT and TOP. For these two different properties, there should be two different reading methods. In order to verify this idea, let's take a look at the implementation of the ReadProperty method.
Procedure Treader.Readpropertons (Ainstance: TPERSIStent);
......
Begin
......
Propinfo: = getPropInfo (Instance.classInfo, fpropName); if propinfo <> nil the readpropvalue (Instance, PropInfo) ELSE
Begin
{Cannot Reliably Recover from an error in a defined property}
FcanhandleExcepts: = false;
Instance.defineproperties (Self);
FcanhandleExcepts: = true;
IF fpropname <> '' THEN
PropertyError (FPropName);
END;
......
END;
In order to save space, some code is omitted, here illustrates: fPropName is the name of the attribute read from the file.
PROPINFO: = getPropInfo (Instance.classInfo, FPropname);
This sentence is information that gets the publication attribute fpropName. As can be seen from the next code, if the attribute information is not empty, read the attribute value via the ReadPropValue method, and the READPROPVALUE method is to read the attribute value through the RTTI function, which is no longer detailed here. If the attribute information is empty, the property fPropname is non-public, it must be read by another mechanism. This is the defineproperties method mentioned earlier, as follows:
Instance.defineproperties (Self);
This method actually calls the Treader's defineProperty method:
Procedure Treader.defineProperty (Const Name: String)
ReadData: Tween: TWRITERPROC; Hasdata: boolean;
Begin
IF Sametext (Name, fpropname) and assigned (readdata) THEN
Begin
ReadData (Self);
FPropName: = '';
END;
END;
It will first compare whether the read attribute name is the same as the preset attribute name, if the same and read method readdata does not empty, call the REDATA method to read the attribute value.
Ok, the root assembly has been read, and it should be the components you have to read the root component. Let's take another way:
Procedure Treader.ReadDataInner (Instance: Tcomponent);
This method has a sentence behind this:
While NOTOFLIST Do Readcomponent (NIL);
This is exactly used to read the child assembly. The reading mechanism of sub-assemblies is the same as the root component described above, which is a deep traversal of a tree.
So far, the component's reading mechanism has been introduced.
Let's take a look at the write mechanism of the component. When we add a component on the form, its related properties are saved in the DFM file, which is done by TWRITER.
Ø Twriter
The Twriter object is a Filer object that can be written in the current flow of data. The TWRITER object is inherited directly from TFiler. In addition to covering the method of inheriting from the TFiler, a large number of ways to write various data types such as Integer, String and Component, etc.).
The TWRITER object provides many ways to write various types of data in the flow, and the write data in the TWRITE object is in different formats in accordance with different data. Therefore, you must master the implementation and application of the TWRITER object, you must understand the format of the Writer object store data. The first thing to explain is that the Filer object label is included in the stream of each Filer object. This tag accounts for four bytes of its value of "TPF0". The FILER object is to access the label for WRITESIGNATURE and READSIGNATURE methods. This label is mainly used for Reader object read data (components, etc.), and the read operation is directed.
Second, the Writer object has to leave a byte flag bit before storing data to indicate what type of data stored later. This byte is the value of the TVALUETYPE type. TVALUETYPE is an enumeration type, accounting for one byte space, which is defined as follows:
TvalueType = (Vanull, Valist, Vaint8, Vaint16, Vaint32, Vaentended, Vastring, VAIDENT,
Vafalse, Vatrue, Vabinary, Vaset, Valstring, Vanil, Vacollection;
Therefore, for each write data method for the Writer object, in implementation, you must write the flag bit to write the corresponding data; each read data method of the Reader object must be judged first, if you meet the data Otherwise, an exception event is generated. The Valist flag has special purposes, which is used to identify the same string of types of items, and the flag of the end of the continuous project is VANULL. Therefore, when Writer object writes a continuous number of items, write the Valist flag with WriteListBegin. After writing the data item, then write the Vanull flag; when reading this data, starting with ReadlistBegin, readlistend end, intermediate ENDOFLIST functions Determine if there is a Vanull flag.
Look at the TWRITER's very important method WriteData:
Procedure TWRITER.WRITEDATA (Instance: Tcomponent);
......
Begin
......
WritePrefix (Flags, Fchildpos);
IF usequalifiednames
Writestr (ptypeinfo (instance.classtype.classinfo). UnitName '.' Instance.classname)
Else
Writestr (instance.classname);
Writestr (Instance.Name);
PropertiesPosition: = position;
IF (FanceStorList <> nil) and (fancestorpos Begin IF Ancestor <> NIL THEN INC (FANCESTORPOS); Inc (FCHILDPOS); END; WriteProperties (Instance); WriteListend; ...... END; From the WriteData method, we can see the profile of generating DFM file information. First write the flag (prefix) in front of the component, then write the class name, instance name. Tight other words: WriteProperties (Instance); This is the properties used to write components. As mentioned earlier, in the DFM file, there are both publications attributes, and non-publication properties, the writing method of both attributes should be different. To see the implementation of WriteProperties: Procedure TWRITER.WRITEPROPERTIES (Instance: TPERSIStent); ...... Begin COUNT: = Gettyped (Instance.classInfo) ^ .propcount; If count> 0 THEN Begin GetMem (PROPLIST, Count * Sizeof (Pointer); Try GetPropinfos (Instance.classInfo, Proplist); For i: = 0 to count - 1 do Begin PropInfo: = proplist ^ [i]; IF propinfo = nil dam Break; IF IsstRedProp (Instance, Propinfo) THEN WriteProperty (Instance, PropInfo); END; Finally FreeMem (PROPLIST, Count * Sizeof (Pointer); END; END; Instance.defineproperties (Self); END; Please see the following code: IF IsstRedProp (Instance, Propinfo) THEN WriteProperty (Instance, PropInfo); The function isstoredProp determines whether the attribute needs to be saved by storing whether the attribute needs to be saved, if you want to save, call WriteProperty to save the properties, and WriteProperty is implemented through a series of RTTI functions. After the Published property is saved, save the non-Published property, which is done by this code: Instance.defineproperties (Self); DEFINEPROPERTIES The implementation has been said before, TTIMER's Left, TOP attribute is saved by it. Ok, there is still such a question so far: How is the subcomponents owned by the root assembly? Let's see the WriteData method (this method is mentioned earlier): Procedure TWRITER.WRITEDATA (Instance: Tcomponent); ...... Begin ...... IF not ignorechildren kiln Try IF (fancestor <> nil) and (fancestor is tcomponent) THEN Begin IF (fancestor is tcomponent) and (csinline in tcomponent (fancestor) .componentstate) THEN Frootancestor: = Tcomponent (FANCESTOR); FANCESTORLIST: = TLIST.CREATE; Tcomponent (fancestor) .Getchildren (addancestor, strootancestor); END; IF csinline in instance.componentstate Then FROOT: = Instance; Instance.getChildren (WriteComponent, Froot); Finally FANCESTORLIST.FREE; END; END; The IgnoreChildren property allows a Writer object to store components without storing subcomponents owned by the component. If the IgnoreChildren property is TRUE, the Writer object stores the subcomponents thereof when the component is stored. Otherwise, the subcomponent is stored. Instance.getChildren (WriteComponent, Froot); This is the most critical sentence of writing subcomponents. It uses the WriteComponent method as a callback function. According to the principle of the deep priority tree, if the root component Froot exists, use WriteComponent to save its sub-components. This way we see the tree component structure in the DFM file.