Delphi component read and write mechanism (3)

zhaozj2021-02-16  57

Ø 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.

转载请注明原文地址:https://www.9cbs.com/read-25065.html

New Post(0)