第二十章 开发Delphi对象式数据管理功能二.docx
- 文档编号:5497765
- 上传时间:2022-12-17
- 格式:DOCX
- 页数:14
- 大小:22KB
第二十章 开发Delphi对象式数据管理功能二.docx
《第二十章 开发Delphi对象式数据管理功能二.docx》由会员分享,可在线阅读,更多相关《第二十章 开发Delphi对象式数据管理功能二.docx(14页珍藏版)》请在冰豆网上搜索。
第二十章开发Delphi对象式数据管理功能二
第二十章 开发Delphi对象式数据管理功能
(二)
20.1.6TResourceStream对象
TResourceStream对象是另一类MemoryStream对象,它提供对Windows应用程序资源的访问,因此称它为资源流。
TResourceSream也是从TCustomMemoryStream继承的。
因此在TCustomMemoryStream对象的基础上,定义了与指定资源模块或资源文件建立连接的构造方法,并且还覆盖了Write,以实现向资源文件中写数据。
下面介绍TResourceStream的实现
1.私有域
TResourceStream没有定义新的属性,但它在private部分定义了两个数据域HResInfo和HGlobol和一个私有方法Initialize,它们的定义如下:
TResourceStream=class(TCustomMemoryStream)
private
HResInfo:
HRSRC;
HGlobal:
THandle;
procedureInitialize(Instance:
THandle;Name,ResType:
PChar);
…
end;
HRSRC是描述Windows资源信息的结构句柄。
HGlobal变量代表资源所在模块的句柄。
如果操作的是应用程序资源,HGlohal就代表EXE程序的句柄;如果是动态链接库(DLL),则HGlobal代表动态链接库的句柄。
TResourceStream对象使用这两上变量访问应用程序或动态链接库中的资源。
Initialize方法是TResourceStream对象内部使用的。
它的构造方法Create和CreateFromID都是调用Initialize方法完成对TResourceStream的初始化。
它的实现如下:
procedureTResourceStream.Initialize(Instance:
THandle;Name,ResType:
PChar);
procedureError;
begin
raiseEResNotFound.Create(FmtLoadStr(SResNotFound,[Name]));
end;
begin
HResInfo:
=FindResource(Instance,Name,ResType);
ifHResInfo=0thenError;
HGlobal:
=LoadResource(Instance,HResInfo);
ifHGlobal=0thenError;
SetPointer(LockResource(HGlobal),SizeOfResource(Instance,HResInfo));
end;
该方法实现中,首先调用Windows函数FoundResource得到由参数Instance指定的模块中的名为Name和类型为ResType的资源,然后调用LoadResource将资源调用内存,并返回该资源在内存中的句柄,最后,将该资源复制到ResourceStream中。
方法的Instance参数代表要调用的资源所在的模块句柄。
模块可以是可执行文件,也可以是动态链接库。
如果在读取资源时没在模块中发现要找的资源则产生异常事件。
2.构造方法Create和CreateFromID
这两个方法在实现上没有大的不同。
顾名思义,第一个方法是通过资源名构造TResourceStream;第二个方法通过资源ID构造TResourceStream,而且在实现过程中,它们都调用了Initialize方法。
下面是它们的实现:
constructorTResourceStream.Create(Instance:
THandle;constResName:
string;
ResType:
PChar);
begin
inheritedCreate;
Initialize(Instance,PChar(ResName),ResType);
end;
constructorTResourceStream.CreateFromID(Instance:
THandle;ResID:
Integer;
ResType:
PChar);
begin
inheritedCreate;
Initialize(Instance,PChar(ResID),ResType);
end;
这两个方法中都有Instance参数,该参数值的含义在Insitialize中介绍过。
3.Write方法
TResourceStream的Write方法只完成一件事,就产生这个异常事件,其实现如下:
functionTResourceStream.Write(constBuffer;Count:
Longint):
Longint;
begin
raiseEStreamError.CreateRes(SCantWriteResourceStreamError);
end;
从方法实现中可以看到,TSourceStream对象是不允许写数据的。
一旦往资源流中写数据将产生异常事件。
4.析构方法Destroy
该方法产生给资源解锁,然后释放该资源,最后调用继承的Destroy方法释放ResourceStream。
其实现如下:
destructorTResourceStream.Destroy;
begin
UnlockResource(HGlobal);
FreeResource(HResInfo);
inheritedDestroy;
end;
回顾Initialize方法,我们不难发现:
●ResourceStream没有额外地给资源重新分配内存,而是直接使用HGlobal句柄所指的内存域
●ResourceStream中的资源在流的生存期,始终是Lock状态,因此要根据Windows的内存使用规则合理安排ResourceStream的使用
●ResourceStream只是用于访问应用程序和动态链接库中的资源的
在Classes在单元中提供了InternalReadComponentRes函数,该函数使用了TResourceStream对象从Delphi应用程序中读取部件。
Delphi是将窗体和部件信息放在模块资源的RCDATA段的。
20.1.7TBlobStream对象
从Delphi数据库开发平台这个意义上说,TBlobStream对象是个很重要的对象。
TBlobStream对象提供了修改TBlobField、TBytesField或TVarBytesField中数据的技术。
开发者可以象对待文件或流那样在数据库域中读写数据。
传统数据库发展的一个重要趋向是往多媒体数据库发展。
目前比较著名和流行的数据库都支持多媒体功能,多媒体数据存储中的一大难点是数据结构不规则,数据量大。
各大数据库产品是采用BLOB技术解决多媒体数据存储中的问题。
Delphi的TBlobStream对象的意义就在于:
一方面可以使Delphi应用程序充分利用多媒体数据库的数据管理能力;另一方面又能利用ObjectPascal的强大程序设计能力给多媒体数据库提供全方向的功能扩展余地。
使用TBlobStream对象可以在多媒体数据库的BLOB字段存储任意格式的数据。
一般说来,许多多媒体数据库只能支持图像、语音或者OLE服务器支持的数据。
利用TBlobStream则不同,只要是程序能够定义的数据格式,它都能在BLOB字段中读写,而不需要其它辅助工具。
TBlobStream用构造方法Create建立数据库域和BLOB流的联接。
用Read或Write方法访问和改变域中的内容;用Seek方法,在域中定位;用Truncate方法删除域中当前位置起所有的数据。
20.1.7.1TBlobStream的属性和方法
TBlobStream对象从TStream直接继承,没有增添新的属性。
它覆盖了Read、Write和Seek方法,提供了对BLOB字段的访问操作;它增添了Truncate方法以实现BLOB字段中的删除操作。
1.Read方法
声明:
functionRead(varBuffer;Count:
Longint):
Longint;
Read方法从数据库域的当前位置起复制Count个字节的内容到Buffer中。
Buffer也必须至少分配Count个字节。
Read方法返回实际传输的字节数,因为传输的字节数可能小于Count,所以需要选择符的边界判断。
2.Write方法
声明:
functionWrite(constBuffer;Count:
Longint);override;Longint;
Write方法从Buffer中向数据库域的当前位置复制Count个字节的内容。
Buffer必须分配有Count个字节的内存空间,函数返回实际传输的字节数,传输过程也要进行选择符边界判断。
3.Seek方法
声明:
functionSeek(Offset:
Longint;Origin:
Word):
Longint;
Seek方法重新设置BLOB流中的指针位置。
如果Origin的值是soFromBeginning,则新的指针位置是Offset;如Origin的值是soFromCurrent,则新的指针位置是Position+Offset;如果Origin的值是SoFromCurrent,则新的指针位置是Size+Offset。
函数返回新的指针位置值。
当Origin为0(SoFromBegin)时,Offset的值必须大于等于零;当Origin的值为2(SoFromEnd),Offset的值必须小于等于零。
4.Truncate方法
声明:
procedureTruncate;
Truncate方法撤消TBlobField、TBytesField或TVarBytesField中从当前位置起的数据。
5.Create方法
声明:
constructorCreate(Field:
TBlobField;Mode:
TBlobStreamMode);
Create方法使用Field参数建立BLOB流与BLOB字段的联接。
Mode的值可为bmRead、bmWrite和bmReadWrite。
20.1.7.2TBlobStream的实现原理
说明TBlobStream对象的实现原理,不可避免地要涉及它的私有域,下面是私有域的定义:
TBlobStream=class(TStream)
private
FField:
TBlobField;
FDataSet:
TDataSet;
FRecord:
PChar;
FBuffer:
PChar;
FFieldNo:
Integer;
FOpened:
Boolean;
FModified:
Boolean;
FPosition:
Longint;
…
public
…
end;
FField是与BLOB流相联的数据库BLOB域,该域用于BLOB流的内部访问。
FDataSet是代表FField所在的数据库,它可以是TTable部件,也可以是TQuery部件。
FRecord和FBuffer都是BLOB流内部使用的缓冲区,用于存储FField所在记录的数据,该数据记录中不包含BLOB数据,TBlobStream使用FRecord作为调用BDEAPI函数的参数值。
FFieldNo代表BLOB字段的字段号,也用于BDEAPI的参数传递,FOpened和FMocified都是状态信息,FPosition表示BLOB流的当前位置,下面介绍TBlobStream方法实现。
1.Create方法和Destroy方法的实现
Create方法的功能主要是建立BlobStream流与BLOB字段的联系并初始化某些私有变量。
其实现如下:
constructorTBlobStream.Create(Field:
TBlobField;Mode:
TBlobStreamMode);
var
OpenMode:
DbiOpenMode;
begin
FField:
=Field;
FDataSet:
=Field.DataSet;
FRecord:
=FDataSet.ActiveBuffer;
FFieldNo:
=Field.FieldNo;
ifFDataSet.State=dsFilterthen
DBErrorFmt(SNoFieldAccess,[FField.DisplayName]);
ifnotFField.FModifiedthen
begin
ifMode=bmReadthen
begin
FBuffer:
=AllocMem(FDataSet.RecordSize);
FRecord:
=FBuffer;
ifnotFDataSet.GetCurrentRecord(FBuffer)thenExit;
OpenMode:
=dbiReadOnly;
endelse
begin
ifnot(FDataSet.Statein[dsEdit,dsInsert])thenDBError(SNotEditing);
OpenMode:
=dbiReadWrite;
end;
Check(DbiOpenBlob(FDataSet.Handle,FRecord,FFieldNo,OpenMode));
end;
FOpened:
=True;
ifMode=bmWritethenTruncate;
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分配的缓冲区,其实现如下:
destructorTBlobStream.Destroy;
begin
ifFOpenedthen
begin
ifFModifiedthenFField.FModified:
=True;
ifnotFField.FModifiedthen
DbiFreeBlob(FDataSet.Handle,FRecord,FFieldNo);
end;
ifFBuffer<>nilthenFreeMem(FBuffer,FDataSet.RecordSize);
ifFModifiedthen
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,而直接调用BDEAPI函数。
这一点是在应用BDEAPI编程中很重要,即一定要修改相应数据库部件的状态。
2.Read和Write方法的实现
Read和Write方法都调用BDEAPI函数完成数据库BLOB字段的读写,其实现如下:
functionTBlobStream.Read(varBuffer;Count:
Longint):
Longint;
var
Status:
DBIResult;
begin
Result:
=0;
ifFOpenedthen
begin
Status:
=DbiGetBlob(FDataSet.Handle,FRecord,FFieldNo,FPosition,
Count,@Buffer,Result);
caseStatusof
DBIERR_NONE,DBIERR_ENDOFBLOB:
begin
ifFField.FTransliteratethen
NativeToAnsiBuf(FDataSet.Locale,@Buffer,@Buffer,Result);
Inc(FPosition,Result);
end;
DBIERR_INVALIDBLOBOFFSET:
{Nothing};
else
DbiError(Status);
end;
end;
end;
Read方法使用了BDEAPI的DbiGetBlob函数从FDataSet中读取数据,在本函数中,各参数的含义是这样的:
FDataSet.Handle代表DataSet的BDE句柄,FReacord表示BLOB字段所在记录,FFieldNo表示BLOB字段号,FPosition表示要读的的数据的起始位置,Count表示要读的字节数,Buffer是读出数据所占的内存,Result是实际读出的字节数。
该BDE函数返回函数调用的错误状态信息。
Read方法还调用了NativeToAnsiBuf进行字符集的转换。
functionTBlobStream.Write(constBuffer;Count:
Longint):
Longint;
var
Temp:
Pointer;
begin
Result:
=0;
ifFOpenedthen
begin
ifFField.FTransliteratethen
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;
endelse
Check(DbiPutBlob(FDataSet.Handle,FRecord,FFieldNo,FPosition,
Count,@Buffer));
Inc(FPosition,Count);
Result:
=Count;
FModified:
=True;
end;
end;
Write方法调用了BDEAPI的DbiPutBlob函数实现往数据库BLOB字段存储数据。
该函数的各参数含义如下:
表20.2调用函数DbiPutBlob的各传入参数的含义
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
参数名 含义
──────────────────────────────
FDataSetHandle写入的数据库的BDE句柄
FRecord写入数据的BLOB字段所在的记录
FFieldNoBLOB字段号
FPosition写入的起始位置
Count写入的数据的字节数
Buffer所写入的数据占有的内存地址
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
方法中还根据FField和FTransliterate的值判断是否进行相应的字符集转换,最后移动BLOB流的位置指针,并将修改标志FModified置为True。
3.Seek和GetBlobSize方法的实现
Seek方法的功能主要是移动BLOB流的位置指针。
GetBlobSize方法是私有的,在Seek方法中被调用,其功能是得到BLOB数据的大小。
它们的实现如下:
functionTBlobStream.GetBlobSize:
Longint;
begin
Result:
=0;
ifFOpenedthen
Check(DbiGetBlobSize(FDataSet.Handle,FRecord,FFieldNo,Result));
end;
functionTBlobStream.Seek(Offset:
Longint;Origin:
Word):
Longint;
begin
caseOriginof
0:
FPosition:
=Offset;
1:
Inc(FPosition,Offset);
2:
FPosition:
=GetBlobSize+Offset;
end;
Result:
=FPosition;
end;
GetBlobSize调用了BDEAPI的DbiGetBlobSize函数,该函数的参数的含义同前面的API函数相同。
4.Truncate方法
该方法是通过调用BDEAPI函数实现的。
其实现如下:
procedureTBlobStream.Truncate;
begin
ifFOpenedthen
begin
Check(DbiTruncateBlob(FDataSet.Handle,FRecord,FFieldNo,FPosition));
FModified:
=True;
end;
end;
该方法从BLOB流的当前位置起删除所有数据,并设置修改标志FModified为True。
在DelphiVCL中许多部件特别是数据库应用方面的部件都用BDEAPI函数完成对数据库的访问,如DataAccess和DataControl部件。
各种数据库部件都是BDEAPI函数外层的包装简化了对数据库的访问操作。
BDEAPI中还提供了避开BDE配置工具在程序中直接处理Alias(建立、修改、删除等)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第二十章 开发Delphi对象式数据管理功能二 第二十 开发 Delphi 对象 数据管理 功能