| 後一頁 前一頁 回目錄 回首頁 |
面向物件技術是九十年代的主流技術,各類應用軟體如果以面向物件的方法構造並且滲透面向物件的風格將使軟體具有更高的品質。在面向物件程式設計中,物件式數據管理佔有很重要的地位。在Delphi中,對物件式數據管理的支援方式是其一大特色。 Delphi是一個面向物件的可視化設計與面向物件的語言相結合的整合開發環境。Delphi的核心是部件。部件是物件的一種。Delphi應用程式完全是由部件來構造的,因此開發高性能的Delphi應用程式必然會涉及物件式數據管理技術。 物件式數據管理包括兩方面的內容: ● 用物件來管理數據 ● 對各類數據物件(包括物件和部件)的管理
Delphi在這兩方面都做的相當出色。在Delphi的早期版本Turbo Pascal 中就曾有流(Stream)、群(Collection)和資源(Resource)等專門用於物件式數據管理的類。在Delphi中,這些功能得到了大大的加強。Delphi將物件式數據管理類歸結為Stream物件(Stream)和Filer物件(Filer),並將它們應用於可視部件類庫(VCL)的方方面面。它們不僅提供了在記憶體、外存和Windows資源中管理物件的功能,還提供了在資料庫BLOB欄位中物件的功能。 在本章中將介紹Stream物件和Filer物件的實現原理、應用方法以及在超媒體系統中的應用。這對於運用Delphi 開發進階應用是很重要的。
20.1 流式物件的實現原理和應用
Stream物件,又稱流式物件,是TStream、THandleStream、TFileStream、TMemoryStream、TResourceStream和TBlobStream等的統稱。它們分別代表了在各種媒介上存儲數據的能力,它們將各種數據型式(包括物件和部件) 在記憶體、外存和資料庫欄位中的管理操作抽象為物件方法,並且充分利用了面向物件技術的優點,應用程式可以相當容易地在各種Stream物件中拷貝數據。 下面介紹各種物件的數據和方法及使用方法。
20.1.1 TStream物件
TStream物件是能在各種媒介中存儲二進制數據的物件的抽象物件。從TStream 物件繼承的物件用於在記憶體、Windows資源文件、磁片檔和資料庫欄位等媒介中存儲數據。 TStream中定義了兩個屬性:Size和Position。它們分別以位元群組為單位表示的流的大小和目前指標位置。TStream中定義的方法用於在各種流中讀、寫和相互拷貝二進制數據。因為所有的Stream物件都是從TStream中繼承來的,所以在TStream中定義的欄位和方法都能被Stream物件呼叫和存取。此外,又由於面向物件技術的動態聯編功能,TStream為各種流的應用提供了統一的接口,簡化了流的使用;不同Stream物件是抽象了對不同存儲媒介的數據上的操作,因此,TStream的需方法為在不同媒介間的數據拷貝提供了最簡捷的手段。
20.1.1.1 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的不同取值和它們的含義:
表20.1 函數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寫入流。
20.1.1.2 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; CouNG="ZH-CN">資源文件中的部件時呼叫,通常程式員不需自己呼叫。如果讀取的不是資源文件ReadResHeader,將觸發異常事件。
procedure TStream.ReadResHeader; var ReadCount: Longint; Header: array[0..79] of Char; begin FillChar(Header, SizeOf(Header), 0); ReadCount := Read(Header, SizeOf(Header) - 1); if (Byte((@Header[0])^) = $FF) and (Word((@Header[1])^) = 10) then Seek(StrLen(Header + 3) + 10 - ReadCount, 1) else raise EInvalidImage.CreateRes(SInvalidImage); end;
ReadComponentRes在Windows資源文件中讀取部件,為了判斷是否是資源文件,它首先呼叫ReadResHeader方法,然後呼叫ReadComponent方法讀取Instance指定的部件。下面是它的實現:
function TStream.ReadComponentRes(Instance: TComponent): TComponent; begin ReadResHeader; Result := ReadComponent(Instance); end;
與ReadComponentRes相應的寫方法是WriteComponentRes,Delphi 呼叫這兩個方法讀寫窗體文件(DFM文件),在後面書中會舉用這兩個方法讀取DFM文件的例子。 お WriteComponent和WriteDescendant方法 Stream物件的WriteDescendant方法在實現過程中,建立了TWriter物件,然後利用TWriter的WriteDescendant方法將Instance寫入流。而WriteComponent方法只是簡單地呼叫WriteDescendant方法將Instance寫入流。它們的實現如下:
procedure TStream.WriteComponent(Instance: TComponent); begin WriteDescendent(Instance, nil); end;
procedure TStream.WriteDescendent(Instance, Ancestor: TComponent); var Writer: TWriter; begin Writer := TWriter.Create(Self, 4096); try Writer.WriteDescendent(Instance, Ancestor); finally Writer.Free; end; end;
か WriteDescendantRes和WriteComponentRes方法 WriteDescendantRes方法用於將部件寫入Windows資源文件;而WriteComponentRes 方法只是簡單地呼叫WriteDescendantRes方法,它們的實現如下:
procedure TStream.WriteComponentRes(const ResName: string; Instance: TComponent); begin WriteDescendentRes(ResName, Instance, nil); end;
procedure TStream.WriteDescendentRes(const ResName: string; Instance, Ancestor: TComponent); var HeaderSize: Integer; Origin, ImageSize: Longint; Header: array[0..79] of Char; begin Byte((@Header[0])^) := $FF; Word((@Header[1])^) := 10; HeaderSize := StrLen(StrUpper(StrPLCopy(@Header[3], ResName, 63))) + 10; Word((@Header[HeaderSize - 6])^) := $1030; Longint((@Header[HeaderSize - 4])^) := 0; WriteBuffer(Header, HeaderSize); Origin := Position; WriteDescendent(Instance, Ancestor); ImageSize := Position - Origin; Position := Origin - 4; WriteBuffer(ImageSize, SizeOf(Longint)); Position := Origin + ImageSize; end;
WriteCompnentRes是與ReadComponentRes相應的物件寫方法,這兩個方法相互配合可讀取Delphi的DFM文件,從而利用Delphi系統的功能。
20.1.2 THandleStream物件
THandleStream物件的行為特別象FileStream物件,所不同的是它通過已建立的文件句柄而不是檔案標簽來存儲流中的數據。 THandleStream物件定義了Handle屬性,該屬性提供了對文件句柄的唯讀存取,並且Handle屬性可以作為Delphi的RTL文件管理函數的參數,利用文件類函數來讀寫數據。THandleStream覆蓋了構造函數Create,該函數帶有Handle 參數,該參數指定與THandleStream物件相關的文件句柄。
20.1.2.1 THandleStream的屬性的方法:
1. Handle屬性 聲明:property Handle: Integer; Handle屬性提供了對文件句柄的唯讀存取,該句柄由THandleStream的構造方法Create傳入。因此除了用THandleStream提供的方法外,也可以用文件管理函數對句柄進行操作。實際上,THandleStream的方法在實現上也是運用文件管理函數進行實際的讀寫操作。 2. Create方法 聲明:constructor Create(AHandle: Integer); Create方法使用傳入的Handle參數建立一個與特定文件句柄相聯的THandleStream物件,並且將AHandle賦給流的Handle屬性。
3. Read、Write和Seek方法 這三個方法是TStream的虛方法,只是在THandleStream 中覆蓋了這三個方法,以實現特定媒介——文件的數據存取。後面會詳細介紹這三個方法的實現。
20.1.2.2 THandleStream的實現原理
THandleStream是從TStream繼承來的,因此可以共用TStream中的屬性和大多數方法。THandleStream在實現上主要是增加了一個屬性Handle和覆蓋了Create、Read、Write和Seek四個方法。 1. 屬性的實現 Handle屬性的實現正如Delphi大多數屬性的實現那樣,先在物件定義的private部分聲明一個存放數據的變數FHandle,然後在定義的public部分聲明屬性Handle,其中屬性定義的讀寫控制部分加上唯讀控制,讀控制只是直接讀取FHandle變數的值,其實現如下:
THandleStream = class(TStream) private FHandle: Integer; public … property Handle: Integer read FHandle; end;
2. 方法的實現 THandleStream的Create方法,以AHandle作為參數,在方法�堶悼u是簡單的將AHandle的值賦給FHandle,其實現如下:
constructor THandleStream.Create(AHandle: Integer); begin FHandle := AHandle; end;
為實現針對文件的數據物件存儲,THandleStream的Read、Write和Seek方法覆蓋了TStream中的相應方法。它們的實現都呼叫了Windows的文件管理函數。 Read方法呼叫FileRead函數實現文件讀操作,其實現如下:
function THandleStream.Read(var Buffer; Count: Longint): Longint; begin Result := FileRead(FHandle, Buffer, Count); if Result = -1 then Result := 0; end;
Write方法呼叫FileWrite函數實現文件寫操作,其實現如下:
function THandleStream.Write(const Buffer; Count: Longint): Longint; begin Result := FileWrite(FHandle, Buffer, Count); if Result = -1 then Result := 0; end;
Seek方法呼叫FileSeek函數實現文件指標的移動,其實現如下:
function THandleStream.Seek(Offset: Longint; Origin: Word): Longint; begin Result := FileSeek(FHandle, Offset, Origin); end;
20.1.3 TFileStream物件
TFileStream物件是在磁片檔上存儲數據的Stream物件。TFileStream是從THandleStream繼承下來的,它和THandleStream一樣都是實現文件的存取操作。不同之處在於THandleStream用句柄存取文件,而TFileStream用檔案標簽存取文件。實際上TFileStream是THandleStream上的一層包裝,其內核是THandleStream的屬性和方法。 TFileStream中沒有增加新的屬性和方法。它只是覆蓋了的構造方法Create和析構方法Destory。在Create方法中帶兩個參數FileName和Mode。FileName描述要建立或打開的檔案標簽,而Mode描述文件模式如fmCreate、fmOpenRead和fmOpenWrite等。Create方法首先使用FileCreate或FileOpen函數建立或打開名為FileName的文件,再將得到的文件句柄賦給FHandle。TFileStream的文件讀寫操作都是由從THandleStream繼承的Read var Stream: TStream; begin Stream := TFileStream.Create(FileName, fmCreate); try SaveToStream(Stream); finally Stream.Free; end; end;
在Delphi 的許多物件的SaveToStream 和SaveToFile、LoadFromStream和LoadFromFile方法的實現都有類似的巢狀結構。
20.1.5 TMemoryStream物件
TMemoryStream物件是一個管理動態記憶體中的數據的Stream物件,它是從TCustomMemoryStream中繼承下來的,除了從TCustomMemoryStream中繼承的屬性和方法外,它還增加和覆蓋了一些用於從磁片檔和其它注台讀數據的方法。它還提供了寫入、消除記憶體內容的動態記憶體管理方法。下面介紹它的這些屬性和方法。
20.1.5.1 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將成為文件內容在記憶體中的完整拷貝。
20.1.5.2 TMemoryStream物件的實現原理
TMemoryStream從TCustomMemoryStream物件直接繼承,因此可以享用TCustomMemoryStream的屬性和方法。前面講過,TCustomMemoryStream是用於記憶體中數據操作的抽象物件,它為MemoryStream物件的實現提供了框架,框架中的內容還要由具體MemoryStream物件去填充。TMemoryStream物件就是按動態記憶體管理的需要填充框架中的具體內容。下面介紹TMemoryStream物件的實現。 1. TMemoryStream屬性的實現 TMemoryStream在其protected部分增加了一個Capacity屬性,該屬性決定了MemoryStream所佔動態記憶體的大小。TMemoryStream首先在private部分聲明了FCapacity變數作為存儲Capacity屬性值的數據欄位,然後在protected部分聲明了該屬性。在屬性聲明的讀控制部分簡單讀取FCapacity的值,在寫控制處呼叫了方法SetCapacity。該方法除了給FCapacity賦值外還執行了修改Capacity屬性所必需操作如狀態改變等。 下面是屬性的實現:
TMemoryStream = class(TCustomMemoryStream) private FCapacity: Longint; procedure SetCapacity(NewCapacity: Longint); protected … property Capacity: Longint read FCapacity write SetCapacity; public … end;
寫控制方法SetCapacity的實現是這樣的:
procedure TMemoryStream.SetCapacity(NewCapacity: Longint); begin SetPointer(Realloc(NewCapacity), FSize); FCapacity := NewCapacity; end;
在SetCapacity 方法先是呼叫Realloc重新分配記憶體,然後用NewCapacity的值給FCapacity賦值。Realloc方法進行某些物件狀態的改變。 2. TMemoryStream物件方法的實現 ぇ Realloc方法 Realloc方法是TMemoryStream動態記憶體分配的核心,它的SetSize、SetCapacity等方法最終都是呼叫Realloc進行記憶體的分配和初始化工作的。它的實現如下:
const MemoryDelta = $2000;
function TMemoryStream.Realloc(var NewCapacity: Longint): Pointer; begin if NewCapacity > 0 then NewCapacity := (NewCapacity + (MemoryDelta - 1)) and not (MemoryDelta - 1); Result := Memory; if NewCapacity <> FCapacity then begin if NewCapacity = 0 then begin GlobalFreePtr(Memory); Result := nil; end else begin if Capacity = 0 then Result := GlobalAllocPtr(HeapAllocFlags, NewCapacity) else Result := GlobalReallocPtr(Memory, NewCapacity, HeapAllocFlags); if Result = nil then raise EStreamError.CreateRes(SMemoryStreamError); end; end; end;
Realloc方法是以8K為單位分配動態記憶體的,方法中的第一句if語句就是執行該操作。如果傳入的NewCapacity參數值為0,則釋放流中的記憶體。Realloc方法用GLobal FreePtr函數釋放記憶體,用GlobalAllocPtr分配記憶體,用GlobalReallocPtr進行記憶體的重分配。如果原來的Capacity屬性值為0,則呼叫Globa|AllocPtr否則呼叫GlobalReallocPtr。最後如果Result為nil則觸發記憶體流錯的異常事件,否則返回指向分配的記憶體的指標。 え Write方法 Write方法從記憶體流內部緩衝池的目前位置開始寫入二進制數據。其實現如下:
function TMemoryStream.Write(const Buffer; Count: Longint): Longint; var Pos: Longint; begin if (FPosition >= 0) and (Count >= 0) then begin Pos := FPosition + Count; if Pos > 0 then begin if Pos > FSize then begin if Pos > FCapacity then SetCapacity(Pos); FSize := Pos; end; System.Move(Buffer, Pointer(Longint(FMemory) + FPosition)^, Count); FPosition := Pos; Result := Count; Exit; end; end; Result := 0; end;
Buffer中存儲要寫入流的二進制數據,如果要寫入的數據的位元群組超出了流的記憶體池的大小,則呼叫SetCapacity方法再分配記憶體,然後用記憶體復制函數將Buffer中的數據復制到FMemory中。接著移動位置指標,並返回寫入數據的位元群組數。分析這段程式可以知道,FCapacity的值和FSize的值是不同的。 ぉ Clear方法 Clear方法消除記憶體流中的數據,將Memory屬性置為nil,並將FSize和FPosition 的值設為0。其實現如下:
procedure TMemoryStream.Clear; begin SetCapacity(0); FSize := 0; FPosition := 0; end;
お LoadFromStream和LoadFromFile方法 LoadFromStream方法首先根據傳入的Stream的Size屬性值重新分配動態記憶體,然後呼叫Stream的ReadBuffer方法往FMemory中復制數據,結果Stream的全部內容在記憶體中有了一份完整拷貝。其實現如下:
procedure TMemoryStream.LoadFromStream(Stream: TStream); var Count: Longint; begin Stream.Position := 0; Count := Stream.Size; SetSize(Count); if Count <> 0 then Stream.ReadBuffer(FMemory^, Count); end; LoadFromFile與LoadFromStream是一對方法。LoadFromFile首先建立了一個TFileStream物件,然後呼叫LoadFromStream方法,將FileStream文件流中的數據寫入MemoryStream中。 |
後一頁 前一頁 回目錄 回首頁 |