DELPHI Stream对象

  Stream 对象,又称流式对象,是 TStream 、 THandleStream 、 TFileStream 、 TMemoryStream 、 TResourceStream 和 TBlobStream 等的统称。它们分别代表了在各种媒介上存储数据的能力,它们将各种数据类型 ( 包括对象和部件 )

在内存、外存和数据库字段中的管理操作抽象为对象方法,并且充分利用了面向对象技术的优点,应用程序可以相当容易地在各种 Stream 对象中拷贝数据。

  下面介绍各种对象的数据和方法及使用方法。

TStream 对象

   TStream 对象是能在各种媒介中存储二进制数据的对象的抽象对象。从 TStream 对象继承的对象用于在内存、 Windows 资源文件、磁盘文件和数据库字段等媒介中存储数据。

  Stream 中定义了两个属性: Size 和 Position 。它们分别以字节为单位表示的流的大小和当前指针位置。 TStream 中定义的方法用于在各种流中读、写和相互拷贝二进制数据。因为所有的 Stream 对象都是从 TStream 中继承来的,所以在 TStream 中定义的域和方法都能被 Stream 对象调用和访

问。此外,又由于面向对象技术的动态联编功能, TStream 为各种流的应用提供了统一的接口,简化了流的使用;不同 Stream 对象是抽象了对不同存储媒介的数据上的操作,因此, TStream 的需方法为在不同媒介间的数据拷贝提供了最简捷的手段。

TStream 的属性和方法

   1. Position 属性 

声明: property Position: Longint;

   Position 属性指明流中读写的当前偏移量。

   2. Size 属性

  声明: property Size: Longint;

Size 属性指明了以字节为单位的流的的大小,它是只读的。

   3. CopyFrom 方法

  声明: function CopyFrom(Source: TStream; Count: Longint): Longint;

CopyFrom 从 Source 所指定的流中拷贝 Count 个字节到当前流中, 并将指针从当前位置移动 Count 个字节数,函数返回值是实际拷贝的字节数。

   4. Read 方法

  声明: function Read(var Buffer; Count: Longint): Longint; virtual; abstract;

Read 方法从当前流中的当前位置起将 Count 个字节的内容复制到 Buffer 中,并把当前指针向后移动 Count 个字节数,函数返回值是实际读的字节数。如果返回值小于 Count ,这意味着读操作在读满所需字节数前指针已经到达了流的尾部。

   Read 方法是抽象方法。每个后继 Stream 对象都要根据自己特有的有关特定存储媒介的读操作覆盖该方法。而且流的所有其它的读数据的方法(如: ReadBuffer , ReadComponent 等)在完成实际的读操作时都调用了 Read 方法。面向对象的动态联编的优点就体现在这儿。因为后继 Stream 对

象只需覆盖 Read 方法,而其它读操作 ( 如 ReadBuffer 、 ReadComponent 等 ) 都不需要重新定义,而且 TStream 还提供了统一的接口。

   5. ReadBuffer 方法

  声明: procedure ReadBuffer(var Buffer; Count: Longint);

   ReadBuffer 方法从流中将 Count 个字节复制到 Buffer 中, 并将流的当前指针向后移动 Count 个字节。如读操作超过流的尾部, ReadBuffer 方法引起 EReadError 异常事件。

   6. ReadComponent 方法

  声明: function ReadComponent(Instance: TComponent): TComponent;

ReadComponent 方法从当前流中读取由 Instance 所指定的部件,函数返回所读的部件。 ReadComponent 在读 Instance 及其拥有的所有对象时创建了一个 Reader 对象并调用它的 ReadRootComponent 方法。

  如果 Instance 为 nil , ReadComponent 的方法基于流中描述的部件类型信息创建部件,并返回新创建的部件。

   7. ReadComponentRes 方法

  声明: function ReadComponentRes(Instance: TComponent): TComponent;

ReadComponentRes 方法从流中读取 Instance 指定的部件,但是流的当前位置必须是由 WriteComponentRes 方法所写入的部件的位置。

   ReadComponentRes

首先调用 ReadResHeader 方法从流中读取资源头,然后调用 ReadComponent 方法读取 Instance 。如果流的当前位置不包含一个资源头。 ReadResHeader 将引发一个 EInvalidImage 异常事件。在 Classes 库单元中也包含一个名为 ReadComponentRes 的函数,该函数执行相同的操作,只不过它基于应

用程序包含的资源建立自己的流。

   8. ReadResHeader 方法

  声明: procedure ReadResHeader;

ReadResHeader 方法从流的当前位置读取 Windows 资源文件头,并将流的当前位置指针移到该文件头的尾部。如果流不包含一个有效的资源文件头, ReadResHeader 将引发一个 EInvalidImage 异常事件。

  流的 ReadComponentRes 方法在从资源文件中读取部件之前,会自动调用 ReadResHeader 方法,因此,通常程序员通常不需要自己调用它。

   9. Seek 方法

  声明: function Seek(Offset: Longint; Origin: Word): Longint; virtual; abstract;

Seek 方法将流的当前指针移动 Offset 个字节,字节移动的起点由 Origin 指定。如果 Offset 是负数, Seek 方法将从所描述的起点往流的头部移动。下表中列出了 Origin 的不同取值和它们的含义:

函数 Seek 的参数的取值

 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  常量       值       Seek 的起点 Offset 的取值

─────────────────────────────────

  SoFromBeginning 0  流的开头 正 数

  SoFromCurrent 1 流的当前位置 正数或负数

  SoFromEnd 2 流的结尾 负 数

 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  10. Write 方法

  在 Delphi 对象式管理的对象中有两类对象的方法都有称为 Write 的: Stream 对象和 Filer 对象。 Stream 对象的 Write 方法将数据写进流中。 Filer 对象通过相关的流传递数据,在后文中会介绍这类方法。

   Stream 对象的 Write 方法声明如下:

function Write(const Buffer; Count: Longint): Longint; virtual; abstract;

Write 方法将 Buffer 中的 Count 个字节写入流中,并将当前位置指针向流的尾部移动 Count 个字节,函数返回写入的字节数。

  TStream 的 Write 方法是抽象的,每个继承的 Stream 对象都要通过覆盖该方法来提供向特定存储媒介 ( 内存、磁盘文件等 ) 写数据的特定方法。流的其它所有写数据的方法 ( 如 WriteBuffer 、 WriteComponent) 都调用 Write 担当实际的写操作。

   11. WriteBuffer 方法

  声明: procedure WriteBuffer(const Buffer; Count: Longint);

   WriteBuffer 的功能与 Write 相似。 WriteBuffer 方法调用 Write 来执行实际的写操作,如果流没能写所有字节, WriteBuffer 会触发一个 EWriteError 异常事件。

   12. WriteComponent 方法

  在 Stream 对象和 Filer 对象都有被称为 WriteComponent 的方法。 Stream 对象的 WriteComponent 方法将 Instance 所指定的部件和它所包含的所有部件都写入流中; Writer 对象的 WriteComponent 将指定部件的属性值写入 Writer 对象的流中。

   Stream 对象的 WriteComponent 方法声明是这样的:

procedure WriteComponent(Instance: Tcomponent);

   WriteComponent 创建一个 Writer 对象,并调用 Writer 的 WriteRootComponent 方法将 Instance 及其拥有的对象写入流。

   13. WriteComponentRes 方法

  声明: WriteComponentRes(const ResName: String; Instance: TComponent);

   WriteComponentRes 方法首先往流中写入标准 Windows 资源文件头,然后将 Instance 指定的部件写入流中。要读由 WriteComponentRes 写入的部件,必须调用 ReadComponentRes 方法。

   WriteComponentRes 使用 ResName 传入的字符串作为资源文件头的资源名,然后调用 WriteComponent 方法将 Instance 和它拥有的部件写入流。

   14. WriteDescendant 方法

  声明: procedure WriteDescendant(Instance Ancestor: TComponent);

   Stream 对象的 WriteDescendant 方法创建一个 Writer 对象,然后调入该对象的 WriteDescendant 方法将 Instance 部件写入流中。 Instance 可以是从 Ancestor 部件继承的窗体,也可以是在从祖先窗体中继承的窗体中相应于祖先窗体中 Ancestor 部件的部件。

   15. WriteDescendantRes 方法

  声明: procedure WriteDescendantRes(const ResName: String;

Instance, Ancestor: TComponent);

   WriteDescendantRes 方法将 Windows 资源文件头写入流,并使用 ResName 作用资源名,然后调用 WriteDescendant 方法,将 Instance 写入流。

TStream 的实现原理

   TStream 对象是 Stream 对象的基础类,这是 Stream 对象的基础。为了能在不同媒介上的存储数据对象,后继的 Stream 对象主要是在 Read 和 Write 方法上做了改进,。因此,了解 TStream 是掌握 Stream 对象管理的核心。 Borland 公司虽然提供了 Stream 对象的接口说明文档,但对于其实现和应

用方法却没有提及,笔者是从 Borland Delphi 2.0 Client/Server Suite 提供的源代码和部分例子程序中掌握了流式对象技术。

  下面就从 TStream 的属性和方法的实现开始。

   1. TStream 属性的实现

  前面介绍过, TStream 具有 Position 和 Size 两个属性,作为抽象数据类型,它抽象了在各种存储媒介中读写数据所需要经常访问的域。那么它们是怎样实现的呢?

  在自定义部件编写这一章中介绍过部件属性定义中的读写控制。 Position 和 Size 也作了读写控制。定义如下:

property Position: Longint read GetPosition write SetPosition;

property Size: Longint read GetSize;

  由上可知, Position 是可读写属性,而 Size 是只读的。

   Position 属性的实现就体现在 GetPosition 和 SetPosition 。当在程序运行过程中,任何读取 Position 的值和给 Position 赋值的操作都会自动触发私有方法 GetPosition 和 SetPosition 。两个方法的声明如下:

function TStream.GetPosition: Longint;

begin

Result := Seek(0, 1);

end;

procedure TStream.SetPosition(Pos: Longint);

begin

Seek(Pos, 0);

end;

在设置位置时, Delphi 编译机制会自动将 Position 传为 Pos 。

  前面介绍过 Seek 的使用方法,第一参数是移动偏移量,第二个参数是移动的起点,返回值是移动后的指针位置。

   Size 属性的实现只有读控制,完全屏蔽了写操作。读控制方法 GetSize 实现如下:

function TStream.GetSize: Longint;

var

Pos: Longint;

begin

Pos := Seek(0, 1);

Result := Seek(0, 2);

Seek(Pos, 0);

end;

2. TStream 方法的实现

  ⑴ CopyFrom 方法

   CopyFrom 是 Stream 对象中很有用的方法,它用于在不同存储媒介中拷贝数据。例如,内存与外部文件之间、内存与数据库字段之间等。它简化了许多内存分配、文件打开和读写等的细节,将所有拷贝操作都统一到 Stream 对象上。

  前面曾介绍: CopyFrom 方法带 Source 和 Count 两个参数并返回长整型。该方法将 Count 个字节的内容从 Source 拷贝到当前流中,如果 Count 值为 0 则拷贝所有数据。

function TStream.CopyFrom(Source: TStream; Count: Longint): Longint;

const

MaxBufSize = $F000;

var

BufSize, N: Integer;

Buffer: PChar;

begin

if Count = 0 then

begin

Source.Position := 0;

Count := Source.Size;

end;

Result := Count;

if Count > MaxBufSize then BufSize := MaxBufSize else BufSize := Count;

GetMem(Buffer, BufSize);

try

while Count <> 0 do

begin

if Count > BufSize then

N := BufSize

else

N := Count;

Source.ReadBuffer(Buffer^, N);

WriteBuffer(Buffer^, N);

Dec(Count, N);

end;

finally

FreeMem(Buffer, BufSize);

end;

end;

  ⑵ ReadBuffer 方法和 WriteBuffer 方法

   ReadBuffer 方法和 WriteBuffer 方法简单地调用虚拟函数 Read 、 Write 来读写流中数据,它比 Read 和 Write 增加了读写数据出错时的异常处理。

procedure TStream.ReadBuffer(var Buffer; Count: Longint);

begin

if (Count <> 0) and (Read(Buffer, Count) <> Count) then

raise EReadError.CreateRes(SReadError);

end;

procedure TStream.WriteBuffer(const Buffer; Count: Longint);

begin

if (Count <> 0) and (Write(Buffer, Count) <> Count) then

raise EWriteError.CreateRes(SWriteError);

end;

  ⑶ ReadComponent 、 ReadResHeader 和 ReadComponentRes 方法

   ReadComponent 方法从当前流中读取部件。在实现上 ReadComponent 方法创建了一个 TStream 对象,并用 TReader 的 ReadRootComponent 方法读部件。在 Delphi 对象式管理中, Stream 对象和 Filer 对象结合很紧密。 Stream 对象的许多方法的实现需要 Filer 对象的支持,而 Filer 对象的构造函数

直接就以 Stream 对象为参数。在 ReadComponent 方法的实现中就可清楚地看到这一点:

function TStream.ReadComponent(Instance: TComponent): TComponent;

var

Reader: TReader;

begin

Reader := TReader.Create(Self, 4096);

try

Result := Reader.ReadRootComponent(Instance);

finally

Reader.Free;

end;

end;

ReadResHeader 方法用于读取 Windows 资源文件的文件头,由 ReadComponentRes 方法在读取 Windows 资源文件中的部件时调用,通常程序员不需自己调用。如果读取的不是资源文件 ReadResH := FSize + Offset;

end;

Result := FPosition;

end;

   Offse 代表移动的偏移量。 Origin 代表移动的起点,值为 0 表示从文件头开始,值为 1 表示从当前位置开始,值为 2 表示从文件尾往前,这时 OffSet 一般为负数。 Seek 的实现没有越界的判断。

   3. SaveToStream 和 SaveToFile 方法

   SaveToStream 方法是将 MemoryStream 对象中的内容写入 Stream 所指定的流。其实现如下:

procedure TCustomMemoryStream.SaveToStream(Stream: TStream);

begin

if FSize <> 0 then Stream.WriteBuffer(FMemory^, FSize);

end;

   SaveToStream 方法调用了 Stream 的 WriteBuffer 方法,直接将 FMemory 中的内容按 FSize 字节长度写入流中。

   SaveToFile 方法是与 SaveToStream 方法相关的。 SaveToFile 方法首先创建了一个 FileStream 对象,然后把该文件 Stream 对象作为 SaveToStream 的参数,由 SaveToStream 方法执行写操作,其实现如下:

procedure TCustomMemoryStream.SaveToFile(const FileName: string);

var

Stream: TStream;

begin

Stream := TFileStream.Create(FileName, fmCreate);

try

SaveToStream(Stream);

finally

Stream.Free;

end;

end;

  在 Delphi 的许多对象的 SaveToStream 和 SaveToFile 、 LoadFromStream 和 LoadFromFile 方法的实现都有类似的嵌套结构。

TMemoryStream对象

 

TMemoryStream 对象是一个管理动态内存中的数据的 Stream 对象,它是从 TCustomMemoryStream 中继承下来的,除了从 TCustomMemoryStream 中继承的属性和方法外,它还增加和覆盖了一些用于从磁盘文件和其它注台读数据的方法。它还提供了写入、消除内存内容的动态内存管理方法。下面

介绍它的这些属性和方法。

TMemoryStream 的属性和方法

   1. Capacity 属性

  声明: property Copacity: Longint;

Capacity 属性决定了分配给内存流的内存池的大小。这与 Size 属性有些不同。 Size 属性是描述流中数据的大小。在程序中可以将 Capacity 的值设置的比数据所需最大内存大一些,这样可以避免频繁地重新分配。

   2. Realloc 方法

  声明: function Realloc(var NewCapacity: Longint): Pointer; virtual;

Realloc 方法,以 8K 为单位分配动态内存,内存的大小由 NewCapacity 指定,函数返回指向所分配内存的指针。

   3. SetSize 方法

   SetSize 方法消除内存流中包含的数据,并将内存流中内存池的大小设为 Size 字节。如果 Size 为零,是 SetSize 方法将释放已有的内存池,并将 Memory 属性置为 nil ;否则, SetSize 方法将内存池大小调整为 Size 。

4. Clear 方法

  声明: procedure Clear;

Clear 方法释放内存中的内存池,并将 Memory 属性置为 nil 。在调用 Clear 方法后, Size 和 Position 属性都为 0 。

   5. LoadFromStream 方法

  声明: procedure LoadFromStream(Stream: TStream);

LoadFromStream 方法将 Stream 指定的流中的全部内容复制到 MemoryStream 中,复制过程将取代已有内容,使 MemoryStream 成为 Stream 的一份拷贝。

   6. LoadFromFile 方法

  声明: procedure LoadFromFile(count FileName: String);

LoadFromFile 方法将 FileName 指定文件的所有内容复制到 MemoryStream 中,并取代已有内容。调用 LoadFromFile 方法后, MemoryStream 将成为文件内容在内存中的完整拷贝。

TMemoryStream 对象的实现原理

   TMemoryStream 从 TCustomMemoryStream 对象直接继承,因此可以享用 TCustomMemoryStream 的属性和方法。前面讲过, TCustomMemoryStream 是用于内存中数据操作的抽象对象,它为 MemoryStream 对象的实现提供了框架,框架中的内容还要由具体 MemoryStream 对象去填充。 TMemoryStrea

m 对象就是按动态内存管理的需要填充框架中的具体内容。下面介绍 TMemoryStream 对象的实 ? FBuffer := AllocMem(FDataSet.RecordSize);

FRecord := FBuffer;

if not FDataSet.GetCurrentRecord(FBuffer) then Exit;

OpenMode := dbiReadOnly;

end else

begin

if not (FDataSet.State in [dsEdit, dsInsert]) then DBError(SNotEditing);

OpenMode := dbiReadWrite;

end;

Check(DbiOpenBlob(FDataSet.Handle, FRecord, FFieldNo, OpenMode));

end;

FOpened := True;

if Mode = bmWrite then Truncate;

end;

 该方法首先是用传入的 Field 参数给 FField , FDataSet , FRecord 和 FFieldNo 赋值。方法中用 AllocMem 按当前记录大小分配内存,并将指针赋给 FBuffer ,用 DataSet 部件的 GetCurrentRecord 方法,将记录的值赋给 FBuffer ,但不包括 BLOB 数据。

  方法中用到的 DbiOpenBlob 函数是 BDE 的 API 函数,该函数用于打开数据库中的 BLOB 字段。

  最后如果方法传入的 Mode 参数值为 bmWrite ,就调用 Truncate 将当前位置指针以后的

数据删除。

  分析这段源程序不难知道:

  ● 读写 BLOB 字段,不允许 BLOB 字段所在 DataSet 部件有 Filter ,否则产生异常事件

  ● 要读写 BLOB 字段,必须将 DataSet 设为编辑或插入状态

  ● 如果 BLOB 字段中的数据作了修改,则在创建 BLOB 流时,不再重新调用 DBiOpenBlob 函数,而只是简单地将 FOpened 置为 True ,这样可以用多个 BLOB 流对同一个 BLOB 字段读写

   Destroy 方法释放 BLOB 字段和为 FBuffer 分配的缓冲区,其实现如下:

destructor TBlobStream.Destroy;

begin

if FOpened then

begin

if FModified then FField.FModified := True;

if not FField.FModified then

DbiFreeBlob(FDataSet.Handle, FRecord, FFieldNo);

end;

if FBuffer <> nil then FreeMem(FBuffer, FDataSet.RecordSize);

if FModified then

try

FField.DataChanged;

except

Application.HandleException(Self);

end;

end;

  如果 BLOB 流中的数据作了修改,就将 FField 的 FModified 置为 True ;如果 FField 的 Modified 为 False 就释放 BLOB 字段,如果 FBuffer 不为空,则释放临时内存。最后根据 FModified 的值来决定是否启动 FField 的事件处理过程 DataChanged 。

  不难看出,如果 BLOB 字段作了修改就不释放 BLOB 字段,并且对 BLOB 字段的修改只有到 Destroy 时才提交,这是因为读写 BLOB 字段时都避开了 FField ,而直接调用 BDE API 函数。这一点是在应用 BDE API 编程中很重要,即一定要修改相应数据库部件的状态。

   2. Read 和 Write 方法的实现

   Read 和 Write 方法都调用 BDE API 函数完成数据库 BLOB 字段的读写,其实现如下:

  

function TBlobStream.Read(var Buffer; Count: Longint): Longint;

var

Status: DBIResult;

begin

Result := 0;

if FOpened then

begin

Status := DbiGetBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition,

Count, @Buffer, Result);

case Status of

DBIERR_NONE, DBIERR_ENDOFBLOB:

begin

if FField.FTransliterate then

NativeToAnsiBuf(FDataSet.Locale, @Buffer, @Buffer, Result);

Inc(FPosition, Result);

end;

DBIERR_INVALIDBLOBOFFSET:

{Nothing};

else

DbiError(Status);

end;

end;

end;

   Read 方法使用了 BDE

API 的 DbiGetBlob 函数从 FDataSet 中读取数据,在本函数中,各参数的含义是这样的: FDataSet.Handle 代表 DataSet 的 BDE 句柄, FReacord 表示 BLOB 字段所在记录, FFieldNo 表示 BLOB 字段号, FPosition 表示要读的的数据的起始位置, Count 表示要读的字节数, Buffer 是读出数据所占的内存,

Result 是实际读出的字节数。该 BDE 函数返回函数调用的错误状态信息。

   Read 方法还调用了 NativeToAnsiBuf 进行字符集的转换。

function TBlobStream.Write(const Buffer; Count: Longint): Longint;

var

Temp: Pointer;

begin

Result := 0;

if FOpened then

begin

if FField.FTransliterate then

begin

GetMem(Temp, Count);

try

AnsiToNativeBuf(FDataSet.Locale, @Buffer, Temp, Count);

Check(DbiPutBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition,

Count, Temp));

finally

FreeMem(Temp, Count);

end;

end else

Check(DbiPutBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition,

Count, @Buffer));

Inc(FPosition, Count);

Result := Count;

FModified := True;

end;

end;

Write 方法调用了 BDE API 的 DbiPutBlob 函数实现往数据库 BLOB 字段存储数据。

该函数的各参数含义如下:

调用函数 DbiPutBlob 的各传入参数的含义

 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

   参数名           含义

──────────────────────────────

  FDataSetHandle 写入的数据库的 BDE 句柄

  FRecord 写入数据的 BLOB 字段所在的记录

FFieldNo BLOB 字段号

  FPosition 写入的起始位置

  Count 写入的数据的字节数

  Buffer 所写入的数据占有的内存地址

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

标志,该标志意味着后面存储有一连串的项目。 Reader 对象,在读这一连串项目时先调用 ReadListBegin 方法读取该标志位,然后用 EndOfList 判断是否列表结束,并用循环语句读取项目。在调用 WriteListBegin 方法的后面必须调用 WriteListEnd 方法写列表结束标志,相应的在 Reader 对象中

有 ReadListEnd 方法读取该结束标志。

   5. WriteListEnd 方法

  声明: procedure WriteListEnd;

WriteListEnd 方法在流中,写入项目列表结束标志,它是与 WriteListBegin 相匹配的方法。

   6. WriteBoolean 方法

  声明: procedure WriteBoolean(Value: Boolean);

WriteBoolean 方法将 Value 传入的布尔值写入流中。

   7. WriteChar 方法

  声明: procedure WriteChar(Value: char);

WriteChar 方法将 Value 中的字符写入流中。

   8. WriteFloat 方法

  声明: procedure WriteFloat(Value: Extended);

WriteFloat 方法将 Value 传入的浮点数写入流中。

   9. WriteInteger 方法

  声明: procedure WriteInteger(Value: Longint);

WriteInteger 方法将 Value 中的整数写入流中。

   10. WriteString 方法

  声明: procedure WriteString(const Value: string);

WriteString 方法将 Value 中的字符串写入流中。

   11. WriteIdent 方法

  声明: procedure WriteIdent(const Ident: string);

WriteIdent 方法将 Ident 传入的标识符写入流中。

   12. WriteSignature 方法

  声明: procedure WriteSignature;

WriteSignature 方法将 Delphi Filer 对象标签写入流中。 WriteRootComponent 方法在将部件写入流之前先调用 WriteSignature 方法写入 Filer 标签。 Reader 对象在读部件之前调用 ReadSignature 方法读取该标签以指导读操作。

   13. WritComponent 方法

  声明: procedure WriteComponent(Component: TComponent);

WriteComponent 方法调用参数 Component 的 WriteState 方法将部件写入流中。在调用 WriteState 之前, WriteComponent 还将 Component 的 ComponetnState 属性置为 csWriting 。当 WriteState 返回时再清除 csWriting.

14. WriteRootComponent 方法

  声明: procedure WriteRootComponent(Root: TComponent);

WriteRootComponent 方法将 Writer 对象 Root 属性设为参数 Root 带的值,然后调用 WriteSignature 方法往流中写入 Filer 对象标签,最后调用 WriteComponent 方法在流中存储 Root 部件。