怎么得到tstream string里面的内容

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: L&&  Position属性指明流中读写的当前偏移量。   2. Size属性   声明:property Size: L&&&&&& Size属性指明了以字节为单位的流的的大小,它是只读的。   3. CopyFrom方法   声明:function CopyFrom(Source: TS Count: Longint): L&&&&&& CopyFrom从Source所指定的流中拷贝Count个字节到当前流中, 并将指针从当前位置移动Count个字节数,函数返回值是实际拷贝的字节数。   4. Read方法   声明:function Read(var B Count: Longint): L&&&&&& Read方法从当前流中的当前位置起将Count个字节的内容复制到Buffer中,并把当前指针向后移动Count个字节数,函数返回值是实际读的字节数。如果返回值小于Count,这意味着读操作在读满所需字节数前指针已经到达了流的尾部。   Read方法是抽象方法。每个后继Stream对象都要根据自己特有的有关特定存储媒介的读操作覆盖该方法。而且流的所有其它的读数据的方法(如:ReadBuffer,ReadComponent等)在完成实际的读操作时都调用了Read方法。面向对象的动态联编的优点就体现在这儿。因为后继Stream对 象只需覆盖Read方法,而其它读操作(如ReadBuffer、ReadComponent等)都不需要重新定义,而且TStream还提供了统一的接口。   5. ReadBuffer方法   声明:procedure ReadBuffer(var B Count: Longint);&&  ReadBuffer方法从流中将Count个字节复制到Buffer 中, 并将流的当前指针向后移动Count个字节。如读操作超过流的尾部,ReadBuffer方法引起EReadError异常事件。   6. ReadComponent方法   声明:function ReadComponent(Instance: TComponent): TC&&&&&& ReadComponent方法从当前流中读取由Instance所指定的部件,函数返回所读的部件。ReadComponent在读Instance及其拥有的所有对象时创建了一个Reader对象并调用它的ReadRootComponent方法。   如果Instance为nil,ReadComponent的方法基于流中描述的部件类型信息创建部件,并返回新创建的部件。   7. ReadComponentRes方法   声明:function ReadComponentRes(Instance: TComponent): TC&&&&&& ReadComponentRes方法从流中读取Instance指定的部件,但是流的当前位置必须是由WriteComponentRes方法所写入的部件的位置。   ReadComponentRes&&首先调用ReadResHeader方法从流中读取资源头,然后调用ReadComponent方法读取Instance。如果流的当前位置不包含一个资源头。ReadResHeader将引发一个EInvalidImage异常事件。在Classes库单元中也包含一个名为ReadComponentRes的函数,该函数执行相同的操作,只不过它基于应 用程序包含的资源建立自己的流。   8. ReadResHeader方法   声明:procedure ReadResH&&&&&& ReadResHeader方法从流的当前位置读取Windows资源文件头,并将流的当前位置指针移到该文件头的尾部。如果流不包含一个有效的资源文件头,ReadResHeader将引发一个EInvalidImage异常事件。   流的ReadComponentRes方法在从资源文件中读取部件之前,会自动调用ReadResHeader方法,因此,通常程序员通常不需要自己调用它。   9. Seek方法   声明:function Seek(Offset: L Origin: Word): L&&&&&& 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 B Count: Longint): L&&
&&&& Write方法将Buffer中的Count个字节写入流中,并将当前位置指针向流的尾部移动Count个字节,函数返回写入的字节数。  && TStream的Write方法是抽象的,每个继承的Stream对象都要通过覆盖该方法来提供向特定存储媒介(内存、磁盘文件等)写数据的特定方法。流的其它所有写数据的方法(如WriteBuffer、WriteComponent)都调用Write担当实际的写操作。   11. WriteBuffer方法   声明:procedure WriteBuffer(const B 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: S 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: S &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 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 SetP &&&& property Size: Longint read GetS
  由上可知,Position是可读写属性,而Size是只读的。   Position属性的实现就体现在GetPosition和SetPosition。当在程序运行过程中,任何读取Position的值和给Position赋值的操作都会自动触发私有方法GetPosition和SetPosition。两个方法的声明如下:
&&&& function TStream.GetPosition: L &&&& begin &&&&&& Result := Seek(0, 1); &&&&
&&&& procedure TStream.SetPosition(Pos: Longint); &&&& begin &&&&&& Seek(Pos, 0); &&&&
&&&& 在设置位置时,Delphi编译机制会自动将Position传为Pos。   前面介绍过Seek的使用方法,第一参数是移动偏移量,第二个参数是移动的起点,返回值是移动后的指针位置。   Size属性的实现只有读控制,完全屏蔽了写操作。读控制方法GetSize实现如下:
&&&& function TStream.GetSize: L &&&& var &&&&&& Pos: L &&&& begin &&&&&& Pos := Seek(0, 1); &&&&&& Result := Seek(0, 2); &&&&&& Seek(Pos, 0); &&&&
&&&& 2. TStream方法的实现   ⑴ CopyFrom方法   CopyFrom是Stream对象中很有用的方法,它用于在不同存储媒介中拷贝数据。例如,内存与外部文件之间、内存与数据库字段之间等。它简化了许多内存分配、文件打开和读写等的细节,将所有拷贝操作都统一到Stream对象上。   前面曾介绍:CopyFrom方法带Source和Count两个参数并返回长整型。该方法将Count个字节的内容从Source拷贝到当前流中,如果Count值为0则拷贝所有数据。
&&&& function TStream.CopyFrom(Source: TS Count: Longint): L &&&& const &&&&&& MaxBufSize = $F000; &&&& var &&&&&& BufSize, N: I &&&&&& Buffer: PC &&&& begin &&&&&& if Count = 0 then &&&&&& begin &&&&&&&& Source.Position := 0; &&&&&&&& Count := Source.S &&&&&& &&&&&& Result := C &&&&&& if Count & MaxBufSize then BufSize := MaxBufSize else BufSize := C &&&&&& GetMem(Buffer, BufSize); &&&&&& try &&&&&&&& while Count && 0 do &&&&&&&& begin &&&&&&&&&& if Count & BufSize then&&&&&&&&&&&&&& N := BufSize&&&&&&&&&&&& else &&&&&&&&&&&& N := C &&&&&&&&&& Source.ReadBuffer(Buffer^, N); &&&&&&&&&& WriteBuffer(Buffer^, N); &&&&&&&&&& Dec(Count, N); &&&&&&&& &&&&&& finally &&&&&&&& FreeMem(Buffer, BufSize); &&&&&& &&&&
  ⑵ ReadBuffer方法和WriteBuffer方法   ReadBuffer方法和WriteBuffer方法简单地调用虚拟函数Read、Write来读写流中数据,它比Read和Write增加了读写数据出错时的异常处理。
&&&& procedure TStream.ReadBuffer(var B Count: Longint); &&&& begin &&&&&& if (Count && 0) and (Read(Buffer, Count) && Count) then &&&&&&&& raise EReadError.CreateRes(SReadError); &&&&
&&&& procedure TStream.WriteBuffer(const B Count: Longint); &&&& begin &&&&&& if (Count && 0) and (Write(Buffer, Count) && Count) then &&&&&&&& raise EWriteError.CreateRes(SWriteError); &&&&
  ⑶ ReadComponent、ReadResHeader和ReadComponentRes方法   ReadComponent方法从当前流中读取部件。在实现上ReadComponent方法创建了一个TStream对象,并用TReader的ReadRootComponent方法读部件。在Delphi对象式管理中,Stream对象和Filer对象结合很紧密。Stream对象的许多方法的实现需要Filer对象的支持,而Filer对象的构造函数 直接就以Stream对象为参数。在ReadComponent方法的实现中就可清楚地看到这一点:
&&&& function TStream.ReadComponent(Instance: TComponent): TC &&&& var &&&&&& Reader: TR &&&& begin &&&&&& Reader := TReader.Create(Self, 4096); &&&&&& try &&&&&&&& Result := Reader.ReadRootComponent(Instance); &&&&&& finally &&&&&&&& Reader.F &&&&&& &&&&
&&&& ReadResHeader方法用于读取Windows资源文件的文件头,由ReadComponentRes方法在读取Windows资源文件中的部件时调用,通常程序员不需自己调用。如果读取的不是资源文件ReadResH := FSize + O &&&&&&&&&& &&&&&&&&&& Result := FP &&&&&&&&
  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); &&&&&&&&
  SaveToStream方法调用了Stream的WriteBuffer方法,直接将FMemory中的内容按FSize字节长度写入流中。   SaveToFile方法是与SaveToStream方法相关的。SaveToFile方法首先创建了一个FileStream对象,然后把该文件Stream对象作为SaveToStream的参数,由SaveToStream 方法执行写操作,其实现如下:
&&&&&&&& procedure TCustomMemoryStream.SaveToFile(const FileName: string); &&&&&&&& var &&&&&&&&&& Stream: TS &&&&&&&& begin &&&&&&&&&& Stream := TFileStream.Create(FileName, fmCreate); &&&&&&&&&& try &&&&&&&&&&&& SaveToStream(Stream); &&&&&&&&&& finally &&&&&&&&&&&& Stream.F &&&&&&&&&& &&&&&&&&
  在Delphi 的许多对象的SaveToStream 和SaveToFile、LoadFromStream和LoadFromFile方法的实现都有类似的嵌套结构。
TMemoryStream对象
 &&&TMemoryStream对象是一个管理动态内存中的数据的Stream对象,它是从TCustomMemoryStream中继承下来的,除了从TCustomMemoryStream中继承的属性和方法外,它还增加和覆盖了一些用于从磁盘文件和其它注台读数据的方法。它还提供了写入、消除内存内容的动态内存管理方法。下面 介绍它的这些属性和方法。
TMemoryStream的属性和方法
  1. Capacity属性   声明:property Copacity: L&&&&&& Capacity属性决定了分配给内存流的内存池的大小。这与Size属性有些不同。Size属性是描述流中数据的大小。在程序中可以将Capacity 的值设置的比数据所需最大内存大一些,这样可以避免频繁地重新分配。   2. Realloc方法   声明:function Realloc(var NewCapacity: Longint): P&&&&&& Realloc方法,以8K为单位分配动态内存,内存的大小由NewCapacity指定,函数返回指向所分配内存的指针。   3. SetSize方法   SetSize方法消除内存流中包含的数据,并将内存流中内存池的大小设为Size字节。如果Size为零,是SetSize方法将释放已有的内存池,并将Memory属性置为nil;否则,SetSize方法将内存池大小调整为Size。 &&&& 4. Clear方法   声明:procedure C&&&&&& 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 := FB &&&&&&&&&&&&&& if not FDataSet.GetCurrentRecord(FBuffer) then E &&&&&&&&&&&&&& OpenMode := dbiReadO &&&&&&&& end else &&&&&&&&&&&& begin &&&&&&&&&&&&&& if not (FDataSet.State in [dsEdit, dsInsert]) then DBError(SNotEditing); &&&&&&&&&&&&&& OpenMode := dbiReadW &&&&&&&&&&&& &&&&&&&&&&&& Check(DbiOpenBlob(FDataSet.Handle, FRecord, FFieldNo, OpenMode)); &&&&&&&&&& &&&&&&&&&& FOpened := T &&&&&&&&&& if Mode = bmWrite then T &&&&&&&&
&&  该方法首先是用传入的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.D &&&&&&&& begin &&&&&&&&&& if FOpened then &&&&&&&&&& begin &&&&&&&&&&&& if FModified then FField.FModified := T &&&&&&&&&&&& if not FField.FModified then &&&&&&&&&&&&&& DbiFreeBlob(FDataSet.Handle, FRecord, FFieldNo); &&&&&&&&&& &&&&&&&&&& if FBuffer && nil then FreeMem(FBuffer, FDataSet.RecordSize); &&&&&&&&&& if FModified then &&&&&&&&&& try &&&&&&&&&&&& FField.DataC &&&&&&&&&& except &&&&&&&&&&&& Application.HandleException(Self); &&&&&&&&&& &&&&&&&&
  如果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 B Count: Longint): L &&&&&&&& var &&&&&&&&&& Status: DBIR &&&&&&&& 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); &&&&&&&&&&&&&&&& &&&&&&&&&&&&&& DBIERR_INVALIDBLOBOFFSET: &&&&&&&&&&&&&&&& {Nothing}; &&&&&&&&&&&& else &&&&&&&&&&&&&& DbiError(Status); &&&&&&&&&&&& &&&&&&&&&& &&&&&&&&
  Read方法使用了BDE&&API的DbiGetBlob函数从FDataSet中读取数据,在本函数中,各参数的含义是这样的:FDataSet.Handle代表DataSet的BDE句柄,FReacord表示BLOB字段所在记录,FFieldNo表示BLOB字段号,FPosition表示要读的的数据的起始位置,Count表示要读的字节数,Buffer是读出数据所占的内存, Result是实际读出的字节数。该BDE函数返回函数调用的错误状态信息。   Read方法还调用了NativeToAnsiBuf进行字符集的转换。
&&&&&&&& function TBlobStream.Write(const B Count: Longint): L &&&&&&&& var &&&&&&&&&& Temp: P &&&&&&&& 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 else &&&&&&&&&&&&&& Check(DbiPutBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition, &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Count, @Buffer)); &&&&&&&&&&&& Inc(FPosition, Count); &&&&&&&&&&&& Result := C &&&&&&&&&&&& FModified := T &&&&&&&&&& &&&&&&&&
&&&& 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 WriteListE&&&&&& 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 WriteS&&&&&& 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部件。
Fillchar是Turbo/Borland Pascal的System单元的一个标准过程,它的使用格式是:FillChar(var X; Count: W value),它的功能是,把指定变量X在内存段中所占的低Count个字节赋为相同的值value, 其中value是填充的值,只能是Byte、Char或Boolean等单字节类型的值。在Free Pascal中稍加扩展为FillChar(var X; Count: L value), 功能没变。 [例1]:Fillchar通常用来给数据赋初值。 var a:array [1..10] 执行fillchar(a,sizeof(a),0); 当arrtype为 1.real(其他实数类型差不多) 使得a中的元素全部成为0.0 2.integer(byte,word,longint,shortint都相同) 全部为0 3.boolean 全部为false 4.char 全部为#0 这里使用了函数sizeof(a),其功能是返回变量a所占的总字节数,如上例返回: 当arrtype为 1.real sizeof(a)的值为60(每个元素占6个字节,10个元素共占60个字节) single sizeof(a)的值为40(每个元素占4个字节,10个元素共占40个字节) double sizeof(a)的值为80(每个元素占8个字节,10个元素共占80个字节) extended sizeof(a)的值为100(每个元素占10个字节,10个元素共占100个字节) comp sizeof(a)的值为80(每个元素占8个字节,10个元素共占80个字节) 2.integer(word) sizeof(a)的值为20 (每个元素占2个字节,10个元素共占20个字节) 3.byte (shortint) sizeof(a)的值为10 (每个元素占1个字节,10个元素共占10个字节) 4.longint sizeof(a)的值为40 (每个元素占4个字节,10个元素共占40个字节) 5.boolean sizeof(a)的值为10(每个元素占1个字节,10个元素共占10个字节) 6.char sizeof(a)的值为10 (每个元素占1个字节,10个元素共占10个字节) 所以例1的结果就是将数组a的所有元素(全部字节)用0来填充,要注意对不同类型的数据而言,对的“解释”是截然不同的!对整型或实型量来 讲,所有字节均为0,则该量也为0;对boolean型量(一个字节)来讲,0表示false(非0数表示true),则该量为false;对char型 量(一个字节)来讲,0表示ASCII码值为0的字符,则该量为#0。 [例2]:将上例中的fillchar(a,sizeof(a),0)改为 fillchar(a,sizeof(a),1),结果如何呢? 执行fillchar(a,size(a),1); 当arrtype为 1.boolean 全部为true(1是非0值,表示true) 2.char 全部为#1 3.byte,shortint 每个元素是1字节量,全部为1 4.integer,word 每个元素是2字节量,全部为(257)10。这是因为 在一个integer或word 型变量中,它的高、低两个字节均用1来填充(将10进制数1转化为二进制数),结果为: 高字节 低字节 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0 0 0 0 0 0 0 0 1 | 0 0 0 0 0 0 0 1 显然,得到的量就是(257)10=(0001)2。 如果,执行的是fillchar(a,size(a),171),结果又是怎样的? 因为(171)10=(,所以,填充后为: 高字节 低字节 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0 1 0 1 0 1 0 1 1 | 1 0 1 0 1 0 1 1 对于integer类型的量,其值为(-21589)10,这是因为integer类型的数据是用补码表示的有符号数,最高位是符号位,0表示 正,1表示负,由于本数是负数,补码为1011,则反码为1010,原码为 0101,其值为-(214+212+210+26+24+22+1)10=-(21589)10;对于word类型的量,其值 为(43947)10,这是因为word类型的数据是用原码表示的无符号数(非负数),原码为1011,其值为(215+213 +211+29+28+27+25+23+21+1)10=(43947)10; 5.longint 每个元素是4字节量,执行fillchar(a,size(a),1)后,全部为(。这是因为,对于每个元素来讲,用1填充后变为: 最高字节 次高字节 31 30 29 28 27 26 25 24 | 23 22 21 20 19 18 17 16 0 0 0 0 0 0 0 1 | 0 0 0 0 0 0 0 1 次低字节 最低字节 15 14 13 12 11 10 9 8 | 7 6 5 4 3 2 1 0 0 0 0 0 0 0 0 1 | 0 0 0 0 0 0 0 1 longint类型的数据是用补码表示的有符号数,最高位是符号位,0表示正,1表示负,由于本数是正数,故补码、反码及原码均为 ,其值为(224+216+28+1)10=( ; 如果,执行的是fillchar(a,size(a),255),结果又是怎样的? 由于(255)10=(,故填充后,补码为,它是负数, 则其反码为,原码为 ,其值为-1 6.single 每个元素是4字节量,全部为2.24E-0038,这是因为,对于每个元素来讲,用1填充后的结果与longint类型的二进制码完全相同,但是, single类型对此数据的“解释”却完全不同: A.最高位(第31位)是整个数的符号位,0为正, 1为负; B.接着的8位(第30位至第23位)是用移码表示的阶码; C.后面的23位(第22至第0位)表示尾数; D.单精度量的值为:±2实际指数*实际尾数 ①、若阶码=,则实际指数=-126,实际尾数=(0.???????????????????????)2,其中的?代表相应位置上的二进制码(0或1);显然,在?全为0时, 这个单精度量的值为0; ②、若阶码大于且小于,则实际指数=阶码-(127)10=阶码-,实际尾数=(1.???????????????????????)2 ③、INF(无穷大)若阶码=,尾数全0,则已达上界,被作为无穷大 ④、浮点运算错误:若阶码=,尾数在(, )之间。 ⑤、NAN(非数:Not A Number)若阶码=,尾数在[, ]之间 下面,我们来分析二进制码为的单精度数(single类型)的值是多少。①最 高位为0,表示正数;②阶码为,换成10进制数为2,则实际指数=2-127=-125,③尾数为 ,实际尾数=1. , 换成10进制数为1+2-7+2-15+2-23=1., ④此单精度数的值是+2-125*1.&#e-38 7.其他实数类型就不一一列举了。 8.对于集合类型 若arrtype=set of '#'..'z'; 执行fillchar(a,sizeof(a),0)后的结果:a全为空集;sizeof(a)返回120。为什么sizeof(a)的值为120?原 来,对集合类型来讲,由于元素范围事先必须给定(如'#'..'z'),每个元素是否存在于某集合中,只需用0或1记下即可,用0表示该元素不属于某集 合,用1表示该元素属于某集合,即只用1个二进制位就可表示1个元素是否属于某集合,那么只要我们按元素的序号顺序记下一串二进制代码,就可以标记所有范 围内的元素是否属于某集合了。但这里有一个问题:数据的存储通常是以字节为单位进行的,不是直接访问每一个二进制位,因此,必须将用户给定的元素的范围进 行调整,调整原则是:两端适当外扩,使第一个元素的序号以及元素的个数正好成为8的倍数,这样就可以字节为单位存储集合了。即:若arrtype=set of char1..char2(事先要定义char1,char2常量),则范围扩大为newchar1..newchar2,其中newchar1=chr (ord(char1)-ord(char1) mod 8), newchar2=chr(ord(char2)+7-ord(char2) mod 8)。对于arrtype=set of '#'..'z',用户给定的范围是:#35..#122,则扩大后的实际范围是#32..#127,元素个数为96,需要用96bit=12byte表 示,故数组a中每个元素(数组中的元素)占12字节,共10个元素要占120字节。 问题:对于arrtype=set of '#'..'z'; 执行fillchar(a,sizeof(a),135)后的结果是什么呢?(135)10= (, 数组a中每个元素如a[1]占12字节,即: , 共96个二进制位,最低位为1,表示扩展后范围内的第1个集合元素(#32即空格)属于集合a[1],第2位为1,表示第2个元素(#33即“!”)属于 集合a[1],第3位为1,表示第3个元素(#34即“"”)属于集合a[1],第4位为0,表示第4个元素(#35即“#”)不属于集合a[1],依此 类推。其他的数组元素a[2],a[3],...,a[10]都与a[1]相同。 [例3]部分字节填充问题。前面讲的都是全部字节被填充(因为用了sizeof()函数) 对例1,若执行fillchar(a,1,55),即将变量a的第一个字节(下标最小的元素的最低字节)填充为(55)10,其原理雷同。 [小结] Fillchar(var X; Count: W value)过程的功能是,把指定变量X在内存段中所占的低Count个字节中的每个字节用一个字节的数据value来填充,由于各种数据类型对相同的二 进制码具有不同的解释,故最后得到的结果也大相径庭。本文探讨了各种类型数据的内部存储机制,有助于加深对数据类型的理解。
为了您的安全,请只打开来源可靠的网址
WinAPI: GetDiskFreeSpaceEx - 获取磁盘容量信息 //声明:GetDiskFreeSpaceEx(& lpDirectoryName: PC&&&&&&&&&&&&&&&&&&&&&&&&& {磁盘根路径}& var lpFreeBytesAvailableToCaller: TLargeI {可用空间}& var lpTotalNumberOfBytes: TLargeI&&&&&&&& {总空间}& TotalFree: PLargeInteger&&&&&&&&&&&&&&&&&&&&&&&& {剩余空间}): BOOL;---------------------------------------------------------------------------
//举例:procedure TForm1.FormCreate(Sender: TObject);var& d1,d2,d3: Int64;begin& GetDiskFreeSpaceEx('C:',d1,d2,@d3);
& Memo1.C& with Memo1.Lines do& begin&&& Add(Format('可用空间: %f GB',[d1/24]));&&& Add(Format('总空间: %f GB',[d2/24]));&&& Add(Format('剩余空间: %f GB',[d3/24]));&
本文转自:
GetVolumeInformation函数来获取硬盘的序列号
函数声明:
& BOOL GetVolumeInformation( & LPCTSTR lpRootPathName,&&&&&&&&&& // 与获取信息卷的根路径 & LPTSTR lpVolumeNameBuffer,&&&&&&& // 用于装在卷名的一个字符串 & DWORD nVolumeNameSize,&&&&&&&&&&& // 字符串的长度 & LPDWORD lpVolumeSerialNumber,&&&& // 用于装载磁盘卷序列号的变量 & LPDWORD lpMaximumComponentLength, // 指定一个变量,用于装载文件名每一部分的长度 & LPDWORD lpFileSystemFlags,&&&&&&& // 用于装载一个或多个二进制位标志的长度 & LPTSTR lpFileSystemNameBuffer,&&& // 指定一个缓冲区 & DWORD nFileSystemNameSize&&&&&&&& // lpFileSystemNameBuffer,的长度 & );
unit Unit1;
uses& Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,& Dialogs, StdC
type& TForm1 = class(TForm)&&& Label1: TL&&& Edit1: TE&&& Button1: TB&&& Button2: TB&&& Button3: TB&&& Edit2: TE&&& Edit3: TE&&& Edit4: TE&&& Button4: TB&&& OpenDialog1: TOpenD&&& ComboBox1: TComboB&&& Edit5: TE&&& Label2: TL&&& Button5: TB&&& procedure Button4Click(Sender: TObject);&&& procedure Button1Click(Sender: TObject);&&& procedure Button2Click(Sender: TObject);&&& procedure Button3Click(Sender: TObject);&&& procedure ComboBox1Change(Sender: TObject);&&& procedure Button5Click(Sender: TObject);& private&&& { Private declarations }& public&&& { Public declarations }&
var& Form1: TForm1;
implementationfunction gefiletime(sFilename:timetype:integer):varffd:twin32dft:lft,time:h:beginh:=windows.FindFirstFile(pchar(sfilename),ffd);case timetype of0:time:=ffd.ftCreationT1:time:=ffd.ftLastWriteT2:time:=ffd.ftLastAccessT&& if(h&&invalid_handle_value) then&& begin&& windows.FindClose(h);&& filetimetolocalfiletime(time,lft);&&&& filetimetodosdatetime(lft,longrec(dft).hi,longrec(dft).lo);&&&& result:=filedatetodatetime(dft);&& end&& else&& result:=0;
function getdiskvoiserialld(drivename:string):vardwtemp1,dwtemp2:nresult:beginnew(nresult);getvolumeinformation(pchar(drivename+'\'),nil,0,nresult,dwtemp1,dwtemp2,nil,0);result:=nresult^;dispose(nresult);{$R *.dfm}
procedure TForm1.Button4Click(Sender: TObject);beginif opendialog1.Execute thenedit1.Text:=opendialog1.FileN
procedure TForm1.Button1Click(Sender: TObject);beginedit2.Text:=datetostr(gefiletime(edit1.text,0));
procedure TForm1.Button2Click(Sender: TObject);beginedit3.Text:=datetostr(gefiletime(edit1.text,1));
procedure TForm1.Button3Click(Sender: TObject);begin&&&&&&&&&&&&&&& edit4.Text:=datetostr(gefiletime(edit1.text,2));
boBox1Change(Sender: TObject);beginedit5.Text:=inttostr(getdiskvoiserialld(combobox1.Items[combobox1.ItemIndex]));
procedure TForm1.Button5Click(Sender: TObject);
var& nu : DWORD; & Vf : DWORD; & Num : DWORD; //用来存储得到的硬盘序列号 & vl : array[0..MAX_PATH] of C& //字符串的长度
begin & GetVolumeInformation(PChar(Trim(Edit1.Text)),nil,SizeOf(vl),@Num,nu,Vf,nil,0);& Edit2.Text := IntToStr(Num);
VC声明HANDLE FindFirstFile(   LPCTSTR lpFileName, // file name   LPWIN32_FIND_DATA lpFindFileData // data buffer );
 lpFileName String,欲搜索的文件名。可包含通配符,并可包含一个路径或相对路
lpFindFileData WIN32_FIND_DATA,这个结构用于装载与找到的文件有关的信息。该结
构可用于后续的搜索
调用失败 返回为INVALID_HANDLE_VALUE(即-1)
typedef&& struct&& _WIN32_FIND_DATA&& {&& //&& wfd&&&& &&&&&&& DWORD&& dwFileA&& &&&&&&& FILETIME&& ftCreationT&& &&&&&&& FILETIME&& ftLastAccessT&& &&&&&&& FILETIME&& ftLastWriteT&& &&&&&&& DWORD&&&&&&&& nFileSizeH&& &&&&&&& DWORD&&&&&&&& nFileSizeL&& &&&&&&& DWORD&&&&&&&& dwReserved0;&& &&&&&&& DWORD&&&&&&&& dwReserved1;&& &&&&&&& TCHAR&&&&&&&& cFileName[&& MAX_PATH&& ];&& &&&&&&& TCHAR&&&&&&&& cAlternateFileName[&& 14&& ];&& }&& WIN32_FIND_DATA;&& WIN32_FIND_DATA是文件查找结构体(我这样给它命名),主要作用是进行文件查找。 你可以用函数FindFirst()和FindNext()来遍历一个目录
unit MainU
uses& Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,& StdC
type& TMainForm = class(TForm)&&& Label1: TL&&& Edit1: TE&&& Button1: TB&&& Label2: TL&&& Edit2: TE&&& Button2: TB&&& Label3: TL&&& Edit3: TE&&& Label4: TL&&& Edit4: TE&&& OpenDialog1: TOpenD&&& procedure Button1Click(Sender: TObject);&&& procedure Button2Click(Sender: TObject);& private&&& { Private declarations }& public&&& { Public declarations }&
var& MainForm: TMainF
implementation
{$R *.DFM}function GetFilesTime(sFilename: S Timetype: Integer): TDateTvar& ffd: TWin32FindD& dft: DW& lft, Time: TFileT& sHandle: THbegin& sHandle:= Windows.FindFirstFile(PChar(sFileName), ffd);& if (sHandle &&INVALID_HANDLE_VALUE) then&&& begin&&&&& case Timetype of&&&&&&& 0: Time:= ffd.ftCreationT&&&&&&& 1: Time:= ffd.ftLastWriteT&&&&&&& 2: Time:= ffd.ftLastAccessT&&&&&&&& Windows.FindClose(sHandle);&&& FileTimeToLocalFileTime(Time, lft);&&& FileTimeToDosDateTime(lft, LongRec(dft).HI, LongRec(dft).Lo);&&& Result:= FileDateToDateTime(dft);& end else Result:= 0;
procedure TMainForm.Button1Click(Sender: TObject);begin& if OpenDialog1.Execute then&&& Edit1.Text:= OpenDialog1.FileN
procedure TMainForm.Button2Click(Sender: TObject);begin& if Edit1.Text&& '' then&&& begin&&&&& Edit2.Text:= DateToStr(GetFilesTime(Edit1.Text, 0));&&&&& Edit3.Text:= DateToStr(GetFilesTime(Edit1.Text, 1));&&&&& Edit4.Text:= DateToStr(GetFilesTime(Edit1.Text, 2));&&& end& else ShowMessage('请指定文件!');
unit Unit1;
uses& Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,& Dialogs, StdC
type& TForm1 = class(TForm)&&& Label1: TL&&& Edit1: TE&&& Button1: TB&&& Button2: TB&&& Edit2: TE&&& Button3: TB&&& Edit3: TE&&& Button4: TB&&& Edit4: TE&&& Button5: TB&&& Edit5: TE&&& Button6: TB&&& Edit6: TE&&& Button7: TB&&& Edit7: TE&&& Button8: TB&&& Button9: TB&&& Button10: TB&&& Edit8: TE&&& Edit9: TE&&& Edit10: TE&&& OpenDialog1: TOpenD&&& procedure Button1Click(Sender: TObject);&&& procedure Button2Click(Sender: TObject);&&& procedure Button3Click(Sender: TObject);&&& procedure Button4Click(Sender: TObject);&&& procedure Button5Click(Sender: TObject);&&& procedure Button6Click(Sender: TObject);&&& procedure Button7Click(Sender: TObject);&&& procedure Button8Click(Sender: TObject);&&& procedure Button9Click(Sender: TObject);&&& procedure Button10Click(Sender: TObject);& private&&& { Private declarations }& public&&& { Public declarations }&
var& Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);beginif opendialog1.Execute thenbeginedit1.Text:=opendialog1.FileN
procedure TForm1.Button2Click(Sender: TObject);begin//将当前路径名与指定文件名合成一个绝对的文件名edit2.Text:=expandfilename(edit1.text)
procedure TForm1.Button3Click(Sender: TObject);begin//获取一个以UNC格式的包括风络驱动器名的绝对文件名edit3.Text:=expandUNCfileName(edit1.text);
procedure TForm1.Button4Click(Sender: TObject);begin//从绝对文件名中获取目录名edit4.Text:=ExtractFileDir(edit1.text);
procedure TForm1.Button5Click(Sender: TObject);begin//从绝对文件名中获取驱动器名edit5.Text:=extractfiledrive(edit1.text);
procedure TForm1.Button6Click(Sender: TObject);begin//从绝对文件名中获取扩展名edit6.Text:=extractfileext(edit1.text);
procedure TForm1.Button7Click(Sender: TObject);begin//从绝对文件名中获取文件名edit7.Text:=extractfilename(edit1.text);
procedure TForm1.Button8Click(Sender: TObject);begin//从绝对文件名中获取路径名edit8.Text:=extractfilepath(edit1.text);
procedure TForm1.Button9Click(Sender: TObject);begin//从绝对文件名中获取相对于某一路径的相对路径名edit9.Text:=extractRelativepath(edit1.text,edit4.text);
procedure TForm1.Button10Click(Sender: TObject);begin// 将长文件名转化为短文件名edit10.Text:=extractshortpathname(edit1.text);
procedure TForm1.N17Click(Sender: TObject);
//读取文件属性 FileGetA
//属性值是一个整数
FileName := 'F:\test\Test.txt';
Attr := FileGetAttr(FileName);
ShowMessage(IntToStr(Attr));
//属性可选值(有些用不着):
//FILE_ATTRIBUTE_READONLY = 1; 只读
//FILE_ATTRIBUTE_HIDDEN = 2; 隐藏
//FILE_ATTRIBUTE_SYSTEM = 4; 系统
//FILE_ATTRIBUTE_DIRECTORY = 16; 文件夹
//FILE_ATTRIBUTE_ARCHIVE = 32; 存档
//FILE_ATTRIBUTE_DEVICE = 64
//FILE_ATTRIBUTE_NORMAL = 128; 一般
//FILE_ATTRIBUTE_TEMPORARY = 256
//FILE_ATTRIBUTE_SPARSE_FILE = 512
//FILE_ATTRIBUTE_REPARSE_POINT = 1204
//FILE_ATTRIBUTE_COMPRESSED = 2048; 压缩
//FILE_ATTRIBUTE_OFFLINE = 4096
//FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192; 不被索引
//FILE_ATTRIBUTE_ENCRYPTED = 16384
下面进行函数分析:
function FileGetAttr(const FileName: string): I
Result := GetFileAttributes(PChar(FileName));
//可以看出此函数是由API函数GetFileAttributes封装而来,参数是要操作文件的文件名
//返回值是整型的属性值,如'32'表示为存档文件
WinAPI: SetCurrentDirectory、GetCurrentDirectory - 设置与获取当前目录
SetCurrentDirectory(
lpPathName: PAnsiChar {路径名}
GetCurrentDirectory(
nBufferLength: DWORD; {缓冲区大小}
lpBuffer: PAnsiChar
{返回目录实际长度}
buf: array[0..MAX_PATH] of C
SetCurrentDirectory('c:\temp');
GetCurrentDirectory(SizeOf(buf), buf);
ShowMessage(buf); {c:\temp}
临界区对象TCriticalSection(Delphi) 与 TRtlCriticalSection 的区别
TRtlCriticalSection 是一个结构体,在windows单元中定义; 是InitializeCriticalSection,EnterCriticalSection,LeaveCriticalSection, DeleteCriticalSection 等这几个kernel32.dll中的临界区操作API的参数;
TCriticalSection是在SyncObjs单元中实现的类,它对上面的那些临界区操作API函数进行了了封装,简化并方便了在Delphi的使用;
如TCriticalSection.E就是调用了EnterCriticalSection这个API函数。
多线程程序中,如果各个线程要访问同一个资源,如同一个变量
这时就要使用线程同步技术,才不会使线程之间产生冲突和干扰
线程同步有多种办法,使用临界区是其中最简单,也是效率最高的办法(CPU占用时间最少)
使用临界区代码如下:
先声明一个TRTLCriticalSection类型的全局变量
varMyCs:TRTLCriticalS
在程序开始或建立线程之前,初始化
InitializeCriticalSection(MyCs);//初始化临界区
在程序结束或所有线程结束后,删除它
DeleteCriticalSection(MyCs);//删除临界区
再在线程中要同步的地方加入
EnterCriticalSection(MyCs); //进入临界区try&&& //程序代码finally&&& LeaveCriticalSection(MyCs); //离开临界区
阅读(...) 评论()}

我要回帖

更多关于 嘴里面溃疡怎么办 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信