Read方法使用了BDE API的DbiGetBlob函數(shù)從FDataSet中讀取數(shù)據(jù),在本函數(shù)中,各參數(shù)的含義是這樣的:FDataSet.Handle代表DataSet的BDE句柄,F(xiàn)Reacord表示BLOB字段所在記錄,F(xiàn)FieldNo表示BLOB字段號(hào),F(xiàn)Position表示要讀的的數(shù)據(jù)的起始位置,Count表示要讀的字節(jié)數(shù),Buffer是讀出數(shù)據(jù)所占的內(nèi)存,Result是實(shí)際讀出的字節(jié)數(shù)。該BDE函數(shù)返回函數(shù)調(diào)用的錯(cuò)誤狀態(tài)信息。
Read方法還調(diào)用了NativeToAnsiBuf進(jìn)行字符集的轉(zhuǎn)換。
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方法調(diào)用了BDE API的DbiPutBlob函數(shù)實(shí)現(xiàn)往數(shù)據(jù)庫(kù)BLOB字段存儲(chǔ)數(shù)據(jù)。
該函數(shù)的各參數(shù)含義如下:
表20.2 調(diào)用函數(shù)DbiPutBlob的各傳入?yún)?shù)的含義
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
參數(shù)名 含義
──────────────────────────────
FDataSetHandle 寫(xiě)入的數(shù)據(jù)庫(kù)的BDE句柄
FRecord 寫(xiě)入數(shù)據(jù)的BLOB字段所在的記錄
FFieldNo BLOB字段號(hào)
FPosition 寫(xiě)入的起始位置
Count 寫(xiě)入的數(shù)據(jù)的字節(jié)數(shù)
Buffer 所寫(xiě)入的數(shù)據(jù)占有的內(nèi)存地址
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
方法中還根據(jù)FField和FTransliterate的值判斷是否進(jìn)行相應(yīng)的字符集轉(zhuǎn)換,最后移動(dòng)BLOB流的位置指針,并將修改標(biāo)志FModified置為T(mén)rue。
3. Seek和GetBlobSize方法的實(shí)現(xiàn)
Seek方法的功能主要是移動(dòng)BLOB流的位置指針。GetBlobSize方法是私有的,在Seek方法中被調(diào)用,其功能是得到BLOB數(shù)據(jù)的大小。它們的實(shí)現(xiàn)如下:
function TBlobStream.GetBlobSize: Longint;
begin
Result := 0;
if FOpened then
Check(DbiGetBlobSize(FDataSet.Handle, FRecord, FFieldNo, Result));
end;
function TBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
case Origin of
0: FPosition := Offset;
1: Inc(FPosition, Offset);
2: FPosition := GetBlobSize + Offset;
end;
Result := FPosition;
end;
GetBlobSize調(diào)用了BDE API的DbiGetBlobSize函數(shù),該函數(shù)的參數(shù)的含義同前面的API函數(shù)相同。
4. Truncate方法
該方法是通過(guò)調(diào)用BDE API函數(shù)實(shí)現(xiàn)的。其實(shí)現(xiàn)如下:
procedure TBlobStream.Truncate;
begin
if FOpened then
begin
Check(DbiTruncateBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition));
FModified := True;
end;
end;
該方法從BLOB流的當(dāng)前位置起刪除所有數(shù)據(jù),并設(shè)置修改標(biāo)志FModified為T(mén)rue。在Delphi VCL中許多部件特別是數(shù)據(jù)庫(kù)應(yīng)用方面的部件都用BDE API函數(shù)完成對(duì)數(shù)據(jù)庫(kù)的訪問(wèn),如Data Access和Data Control部件。各種數(shù)據(jù)庫(kù)部件都是BDE API函數(shù)外層的包裝簡(jiǎn)化了對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)操作。BDE API中還提供了避開(kāi)BDE配置工具在程序中直接處理Alias(建立、修改、刪除等)的函數(shù)支持,這也是部件所沒(méi)有提供的。在Delphi數(shù)據(jù)庫(kù)應(yīng)用安裝程序中,這些Alias操作函數(shù)無(wú)疑是相當(dāng)重要的。有關(guān)BDE API函數(shù)的詳細(xì)介紹,可閱讀Delphi2.0 Client/Server Suite所帶的BDE API 幫助文件。
20.2 讀寫(xiě)對(duì)象的實(shí)現(xiàn)原理和應(yīng)用
讀寫(xiě)對(duì)象(Filer)包括TFiler對(duì)象、TReader對(duì)象和TWriter對(duì)象。TFiler對(duì)象是文件讀寫(xiě)的基礎(chǔ)對(duì)象,在應(yīng)用程序中使用的主要是TReader和TWriter。TReader和TWriter對(duì)象都直接從TFiler對(duì)象繼承。TFiler對(duì)象定義了Filer對(duì)象的基本屬性和方法。
Filer對(duì)象主要完成兩大功能:
● 存取窗體文件和窗體文件中的部件
● 提供數(shù)據(jù)緩沖,加快數(shù)據(jù)讀寫(xiě)操作
20.2.1 TFiler對(duì)象
TFiler對(duì)象是TReader和TWriter的抽象類(lèi),定義了用于部件存儲(chǔ)的基本屬性和方法。它定義了Root屬性,Root指明了所讀或?qū)懙牟考母鶎?duì)象,它的Create方法將Stream對(duì)象作為傳入?yún)?shù)以建立與Stream對(duì)象的聯(lián)系, Filer對(duì)象的具體讀寫(xiě)操作都是由Stream對(duì)象完成。因此,只要是Stream對(duì)象所能訪問(wèn)的媒介都能由Filer對(duì)象存取部件。TFiler 對(duì)象還提供了兩個(gè)定義屬性的方法:DefineProperty和DefineBinaryProperty,這兩個(gè)方法使對(duì)象能讀寫(xiě)不在部件published部分定義的屬性。
因?yàn)镕iler對(duì)象主要用于存取Delphi的窗體文件和窗體文件中的部件,所以要清楚地理解Filer對(duì)象就要清楚Delphi 窗體文件(DFM文件)的結(jié)構(gòu)。
DFM文件是用于Delphi存儲(chǔ)窗體的。窗體是Delphi可視化程序設(shè)計(jì)的核心。窗體對(duì)應(yīng)Delphi應(yīng)用程序中的窗口,窗體中的可視部件對(duì)應(yīng)窗口中的界面元素,非可視部件如TTable和TOpenDialog,對(duì)應(yīng)Delphi應(yīng)用程序的某項(xiàng)功能。Delphi應(yīng)用程序的設(shè)計(jì)實(shí)際上是以窗體的設(shè)計(jì)為中心。因此,DFM文件在Delphi應(yīng)用設(shè)計(jì)中也占很重要的位置。窗體中的所有元素包括窗體自身的屬性都包含在DFM文件中。
在Delphi應(yīng)用程序窗口,界面元素是按擁有關(guān)系相互聯(lián)系的,因此樹(shù)狀結(jié)構(gòu)是最自然的表達(dá)形式;相應(yīng)地,窗體中的部件也是按樹(shù)狀結(jié)構(gòu)組織;對(duì)應(yīng)在DFM文件中,也要表達(dá)這種關(guān)系。DFM文件在物理上,是以二進(jìn)制方式存儲(chǔ)的,在邏輯上則是以樹(shù)狀結(jié)構(gòu)安排各部件的關(guān)系。Delphi編輯窗口支持以文本方式顯示DFM文件。從該文本中可以看清窗體的樹(shù)狀結(jié)構(gòu)。下面是DFM文件的文本顯示:
Object Form1: TForm1
Left = 72
Top = 77
ActiveControl = DBIMage1
…
Object Panell: TPanel
Left = 6
…
Object DBLabel1: TDBText
…
end
Object DBImage1: TDBImage
…
end
end
Object Panel2: TPanel
Left = 6
…
Object Label1: TLable
…
end
end
Object Panel3: TPanel
Left = 6
…
Object DBLabel2: TDBText
…
end
end
end
關(guān)于DFM文件中存儲(chǔ)屬性值的規(guī)則,請(qǐng)參見(jiàn)自定義部件開(kāi)發(fā)這一章。
對(duì)照TFiler對(duì)象的屬性。Root屬性就表示部件樹(shù)的根──窗體。Filer對(duì)象的許多方法都是讀從根起始的樹(shù)中所有的部件。Ancestor屬性表示根的祖先對(duì)象,IgnoreChildren屬性則是讀部件時(shí)忽略根的子結(jié)點(diǎn)。
下面介紹Filer對(duì)象的屬性和方法。
20.2.1.1 TFiler對(duì)象的屬性和方法
1. Root屬性
聲明:property Root: TComponent;
Root 屬性給Filer對(duì)象指出被讀寫(xiě)的對(duì)象中哪一個(gè)對(duì)象是根或主要擁有者。RootComponent和WriteRootComponent方法在讀和寫(xiě)部件及其擁有的部件前先設(shè)置Root的值。
2. Ancestor屬性
聲明:property Ancestor: TPersistent;
Ancestor屬性用于往繼承下來(lái)的窗體中寫(xiě)部件,因?yàn)楫?dāng)寫(xiě)部件時(shí),Write對(duì)象只需要寫(xiě)入與所繼承的部件不同的屬性,所以在寫(xiě)之前要跟蹤每個(gè)繼承的部件,并且比較它們的屬性。
如果Ancestor為nil,就表示沒(méi)有相應(yīng)的繼承的部件,Writer對(duì)象應(yīng)當(dāng)將部件完全寫(xiě)入流。Ancestor一般為nil,只有當(dāng)調(diào)用WriteDescendant和WriteDescendantRes時(shí),才給賦值。當(dāng)編寫(xiě)和覆蓋DefineProperties時(shí),必須設(shè)置Ancestor的值。
3. IgnoreChildren屬性
聲明:property Ignorechildren: Boolean;
IgnoreChildren屬性使一個(gè)Writer對(duì)象存儲(chǔ)部件時(shí)可以不存儲(chǔ)該部件擁有的部件。如果IgnoreChildren屬性為T(mén)rue,則Writer對(duì)象存儲(chǔ)部件不存它擁有的子部件。否則,Writer對(duì)象將所有其擁有的對(duì)象寫(xiě)入流。
4. Create方法
聲明:constructor Create(Stream: TStream; BufSize: Cardinal);
Create方法創(chuàng)建一個(gè)新的Filer對(duì)象,建立它和流Stream的聯(lián)系;并且給它分配一個(gè)緩沖區(qū)Buffer。Buffer的大小由BufSize指定。
5. Defineproperty方法
聲明:procedure Defineproperty(const Name: String; ReadData: TReaderProc;
WriteData: TWriterProc; HasData: Boolean); virtual; abstract;
Defineproperty方法定義Filer對(duì)象將作為屬性存儲(chǔ)的數(shù)據(jù)。Name參數(shù)描述接受的屬性名,該屬性不在published部分定義。ReadData和WriteData參數(shù)指定在存取對(duì)象時(shí)讀和寫(xiě)所需數(shù)據(jù)的方法。HasData參數(shù)在運(yùn)行時(shí)決定了屬性是否有數(shù)據(jù)要存儲(chǔ)。
只有當(dāng)對(duì)象有數(shù)據(jù)要存儲(chǔ)時(shí),才在該對(duì)象的DefineProperties中調(diào)用DefineProperty。DefineProperties有一個(gè)Filer對(duì)象作為它的參數(shù),調(diào)用的就是該Filer對(duì)象的DefineProperty和DefineBinaryProperty方法。當(dāng)定義屬性時(shí),Writer對(duì)象應(yīng)當(dāng)引用Ancestor屬性,如果該屬性非空,Writer對(duì)象應(yīng)當(dāng)只寫(xiě)入與從Ancestor繼承的不同的屬性的值。
一個(gè)最簡(jiǎn)單的例子是TComponent的DefineProperties方法。盡管TComponent 沒(méi)有在published中定義Left、Top屬性,但該方法存儲(chǔ)了部件的位置信息。
procedure TComponent.DefineProperties(Filer: TFiler);
begin
Filer.DefineProperty('Left', ReadLeft, WriteLeft, LongRec(FDesignInfo).Lo <> 0);
Filer.DefineProperty('Top', ReadTop, WriteTop, LongRec(FDesignInfo).Hi <> 0);
end;
6. DefineBinaryproperty方法
聲明:procedure DefineBinaryproperty(const Name: String;
ReadData, WriteData: TStreamProc;
HisData: Boolean); virtual; abstract;
DefineBinaryProperty方法定義Filer對(duì)象作為屬性存儲(chǔ)的二進(jìn)制數(shù)據(jù)。Name參數(shù)描述屬性名。ReadData和WriteData參數(shù)描述所存儲(chǔ)的對(duì)象中讀寫(xiě)所需數(shù)據(jù)的方法。HasData參數(shù)在運(yùn)行時(shí)決定屬性是否有數(shù)據(jù)要存。
DefineBinaryProperty和DefineProperty方法的不同之處在于,二進(jìn)制型的屬性直接用Stream對(duì)象讀寫(xiě),而不是通過(guò)Filer對(duì)象。通過(guò)ReadData和WriteData傳入的方法,直接將對(duì)象數(shù)據(jù)寫(xiě)入流或從流讀出。
DefineBinaryProperty屬性用得較少。只有標(biāo)準(zhǔn)的VCL對(duì)象定義了象圖形、圖像之類(lèi)的二進(jìn)制屬性的部件中才用它。
7. FlushBuffer方法
聲明:procedure FlushBuffer; virtual: abstract;
FlushBuffer方法用于使Filer對(duì)象的緩沖區(qū)與相聯(lián)的Stream對(duì)象同步。對(duì)Reader對(duì)象來(lái)說(shuō),是通過(guò)重新分配緩沖區(qū);對(duì)于Writer對(duì)象是通過(guò)寫(xiě)入當(dāng)前緩沖區(qū)。
FlushBuffer是一個(gè)抽象方法,TReader和TWriter都覆蓋了它,提供了具體實(shí)現(xiàn)。
20.2.1.2 TFiler對(duì)象的實(shí)現(xiàn)原理
TFiler對(duì)象是Filer對(duì)象的基礎(chǔ)類(lèi),它定義的大多數(shù)方法都是抽象類(lèi)型的,沒(méi)有具體實(shí)現(xiàn)它,這些方法要在TReader和TWrite中覆蓋。但它們提供了Filer對(duì)象的框架,了解它無(wú)疑是很重要的。
1. TFiler對(duì)象屬性的實(shí)現(xiàn)
TFiler對(duì)象定義了三個(gè)屬性:Root、Ancestor和IgnoreChildren。正如定義對(duì)象屬性通常所采用的方法那樣,要在private部分定義存儲(chǔ)屬性值的數(shù)據(jù)域,然后在public或Published部分定義該屬性,并按需要增加讀寫(xiě)控制。它們的定義如下:
TFiler = class(TObject)
private
…
FRoot: TComponent;
FAncestor: TPersistent;
FIgnoreChildren: Boolean;
public
…
property Root: TComponent read FRoot write FRoot;
property Ancestor: TPersistent read FAncestor write FAncestor;
property IgnoreChildren: Boolean read FIgnoreChildren write FIgnoreChildren;
end;
它們?cè)谧x寫(xiě)控制上都是直接讀寫(xiě)私有的數(shù)據(jù)域。
在介紹TReader和TWriter的實(shí)現(xiàn),我們還會(huì)看到這幾個(gè)屬性的原理介紹。
2. TFiler對(duì)象方法的實(shí)現(xiàn)
在TFiler對(duì)象定義的眾多方法中很多都是抽象類(lèi)方法,沒(méi)有具體實(shí)現(xiàn)。在TFiler 的后繼對(duì)象TReader中覆蓋了這些方法。在后面章節(jié),會(huì)介紹這些方法的實(shí)現(xiàn)。
在TFiler對(duì)象中有具體實(shí)現(xiàn)的有兩個(gè)方法Create和Destroy。
、 Create方法的實(shí)現(xiàn)
Create方法是TFiler的構(gòu)造方法,它有兩個(gè)參數(shù)Stream和BufSize。Stream是指定與TFiler對(duì)象相聯(lián)系的Stream對(duì)象,F(xiàn)iler對(duì)象都是用Stream對(duì)象完成具體的讀寫(xiě)。BufSize是TFiler對(duì)象內(nèi)部開(kāi)設(shè)的緩沖區(qū)的大小。Filer對(duì)象內(nèi)部開(kāi)設(shè)緩沖區(qū)是為了加快數(shù)據(jù)的讀寫(xiě),它的實(shí)現(xiàn)如下:
constructor TFiler.Create(Stream: TStream; BufSize: Integer);
begin
FStream := Stream;
GetMem(FBuffer, BufSize);
FBufSize := BufSize;
end;
FStream、FBuffer和FBufSize都是TFiler在private部分定義的數(shù)據(jù)域。FStream表示與Filer對(duì)象相聯(lián)的Stream對(duì)象,F(xiàn)Buffer指向Filer對(duì)象內(nèi)部開(kāi)設(shè)的緩沖區(qū),F(xiàn)BufSize是內(nèi)部緩沖區(qū)的大小。Create方法用Stream參數(shù)值給FStream賦值,然后用GetMem分配BufSize大小的動(dòng)態(tài)內(nèi)存作為內(nèi)部緩沖區(qū)。
、 Destroy方法的實(shí)現(xiàn)
Destroy方法是TFiler對(duì)象的析構(gòu)函數(shù),它的作用就是釋放動(dòng)態(tài)內(nèi)存。
destructor TFiler.Destroy;
begin
if FBuffer <> nil then FreeMem(FBuffer, FBufSize);
end;
20.2.2 TWriter對(duì)象
TWriter 對(duì)象是可實(shí)例化的,往流中寫(xiě)數(shù)據(jù)的Filer對(duì)象。TWriter對(duì)象直接從TFiler繼承而來(lái),除了覆蓋從TFiler繼承的方法外,還增加了大量的關(guān)于寫(xiě)各種數(shù)據(jù)類(lèi)型(如Integer、String和Component等)的方法。TWriter對(duì)象和TReader 對(duì)象配合使用將使對(duì)象讀寫(xiě)發(fā)揮巨大作用。
20.2.2.1 TWriter對(duì)象的屬性和方法
1. Position屬性
聲明:property Position: Longint;
TWriter對(duì)象的Position屬性表示相關(guān)聯(lián)的流中的當(dāng)前要寫(xiě)的位置,TReader 對(duì)象也有這個(gè)屬性,但與TReader對(duì)象不同的是TWriter對(duì)象的Position的值比流的Position值小,這一點(diǎn)一看屬性實(shí)現(xiàn)就清楚了。
2. RootAncesstor屬性
聲明:property RootAncestor: TComponent;
RootAncestor屬性表示的是Root屬性所指的部件的祖先。如果Root 是繼承的窗體,Writer對(duì)象將窗體擁有部件與祖先窗體中的相應(yīng)部件依次比較,然后只寫(xiě)入那些與祖先中的不同的部件。
3. Write方法
聲明:procedure Write(const Buf; Count: Longint);
Write方法從Buf中往與Writer相關(guān)聯(lián)的流中寫(xiě)入Count個(gè)字節(jié)。
4. WriteListBegin方法
聲明:procedure WriteListBegin;
WriteListBegin方法往Write對(duì)象的流中寫(xiě)入項(xiàng)目列表開(kāi)始標(biāo)志,該標(biāo)志意味著后面存儲(chǔ)有一連串的項(xiàng)目。Reader對(duì)象,在讀這一連串項(xiàng)目時(shí)先調(diào)用ReadListBegin方法讀取該標(biāo)志位,然后用EndOfList判斷是否列表結(jié)束,并用循環(huán)語(yǔ)句讀取項(xiàng)目。在調(diào)用WriteListBegin方法的后面必須調(diào)用WriteListEnd方法寫(xiě)列表結(jié)束標(biāo)志,相應(yīng)的在Reader對(duì)象中有ReadListEnd方法讀取該結(jié)束標(biāo)志。
5. WriteListEnd方法
聲明:procedure WriteListEnd;
WriteListEnd方法在流中,寫(xiě)入項(xiàng)目列表結(jié)束標(biāo)志,它是與WriteListBegin相匹配的方法。
6. WriteBoolean方法
聲明:procedure WriteBoolean(Value: Boolean);
WriteBoolean方法將Value傳入的布爾值寫(xiě)入流中。
7. WriteChar方法
聲明:procedure WriteChar(Value: char);
WriteChar方法將Value中的字符寫(xiě)入流中。
8. WriteFloat方法
聲明:procedure WriteFloat(Value: Extended);
WriteFloat方法將Value傳入的浮點(diǎn)數(shù)寫(xiě)入流中。
9. WriteInteger方法
聲明:procedure WriteInteger(Value: Longint);
WriteInteger方法將Value中的整數(shù)寫(xiě)入流中。
10. WriteString方法
聲明:procedure WriteString(const Value: string);
WriteString方法將Value中的字符串寫(xiě)入流中。
11. WriteIdent方法
聲明:procedure WriteIdent(const Ident: string);
WriteIdent方法將Ident傳入的標(biāo)識(shí)符寫(xiě)入流中。
相關(guān)推薦:2010年9月計(jì)算機(jī)等級(jí)考試試題及答案解析專(zhuān)題北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內(nèi)蒙古 |