首页 考试吧论坛 Exam8视线 考试商城 网络课程 模拟考试 考友录 实用文档 求职招聘 论文下载
2011中考 | 2011高考 | 2012考研 | 考研培训 | 在职研 | 自学考试 | 成人高考 | 法律硕士 | MBA考试
MPA考试 | 中科院
四六级 | 职称英语 | 商务英语 | 公共英语 | 托福 | 雅思 | 专四专八 | 口译笔译 | 博思 | GRE GMAT
新概念英语 | 成人英语三级 | 申硕英语 | 攻硕英语 | 职称日语 | 日语学习 | 法语 | 德语 | 韩语
计算机等级考试 | 软件水平考试 | 职称计算机 | 微软认证 | 思科认证 | Oracle认证 | Linux认证
华为认证 | Java认证
公务员 | 报关员 | 银行从业资格 | 证券从业资格 | 期货从业资格 | 司法考试 | 法律顾问 | 导游资格
报检员 | 教师资格 | 社会工作者 | 外销员 | 国际商务师 | 跟单员 | 单证员 | 物流师 | 价格鉴证师
人力资源 | 管理咨询师考试 | 秘书资格 | 心理咨询师考试 | 出版专业资格 | 广告师职业水平
驾驶员 | 网络编辑
卫生资格 | 执业医师 | 执业药师 | 执业护士
会计从业资格考试会计证) | 经济师 | 会计职称 | 注册会计师 | 审计师 | 注册税务师
注册资产评估师 | 高级会计师 | ACCA | 统计师 | 精算师 | 理财规划师 | 国际内审师
一级建造师 | 二级建造师 | 造价工程师 | 造价员 | 咨询工程师 | 监理工程师 | 安全工程师
质量工程师 | 物业管理师 | 招标师 | 结构工程师 | 建筑师 | 房地产估价师 | 土地估价师 | 岩土师
设备监理师 | 房地产经纪人 | 投资项目管理师 | 土地登记代理人 | 环境影响评价师 | 环保工程师
城市规划师 | 公路监理师 | 公路造价师 | 安全评价师 | 电气工程师 | 注册测绘师 | 注册计量师
缤纷校园 | 实用文档 | 英语学习 | 作文大全 | 求职招聘 | 论文下载 | 访谈 | 游戏
您现在的位置: 考试吧(Exam8.com) > 计算机等级考试 > 计算机二级 > Delphi > 复习资料 > 正文

2011等考Delphi:开发Delphi对象式数据管理功能

来源:考试吧Exam8.com) 2010-10-30 9:21:41 考试吧:中国教育培训第一门户 模拟考场
在本章中将介绍Stream对象和Filer对象的实现原理、应用方法以及在超媒体系统中的应用。这对于运用Delphi 开发高级应用是很重要的。

  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中。

  20.1.6 TResourceStream对象

  TResourceStream对象是另一类MemoryStream对象,它提供对Windows 应用程序资源的访问,因此称它为资源流。TResourceSream也是从TCustomMemoryStream 继承的。因此在TCustomMemoryStream对象的基础上,定义了与指定资源模块或资源文件建立连接的构造方法,并且还覆盖了Write,以实现向资源文件中写数据。

  下面介绍TResourceStream的实现

  1. 私有域

  TResourceStream没有定义新的属性,但它在private部分定义了两个数据域HResInfo和HGlobol和一个私有方法Initialize,它们的定义如下:

  TResourceStream = class(TCustomMemoryStream)

  private

  HResInfo: HRSRC;

  HGlobal: THandle;

  procedure Initialize(Instance: THandle; Name, ResType: PChar);

  …

  end;

  HRSRC是描述Windows资源信息的结构句柄。HGlobal变量代表资源所在模块的句柄。如果操作的是应用程序资源,HGlohal就代表EXE程序的句柄;如果是动态链接库(DLL),则HGlobal 代表动态链接库的句柄。TResourceStream对象使用这两上变量访问应用程序或动态链接库中的资源。

  Initialize方法是TResourceStream对象内部使用的。它的构造方法Create和CreateFromID都是调用Initialize方法完成对TResourceStream的初始化。它的实现如下:

  procedure TResourceStream.Initialize(Instance: THandle; Name, ResType: PChar);

  procedure Error;

  begin

  raise EResNotFound.Create(FmtLoadStr(SResNotFound, [Name]));

  end;

  begin

  HResInfo := FindResource(Instance, Name, ResType);

  if HResInfo = 0 then Error;

  HGlobal := LoadResource(Instance, HResInfo);

  if HGlobal = 0 then Error;

  SetPointer(LockResource(HGlobal), SizeOfResource(Instance, HResInfo));

  end;

  该方法实现中,首先调用Windows函数FoundResource得到由参数Instance指定的模块中的名为Name和类型为ResType的资源,然后调用LoadResource将资源调用内存,并返回该资源在内存中的句柄,最后,将该资源复制到ResourceStream中。方法的Instance参数代表要调用的资源所在的模块句柄。模块可以是可执行文件,也可以是动态链接库。如果在读取资源时没在模块中发现要找的资源则产生异常事件。

  2. 构造方法Create和CreateFromID

  这两个方法在实现上没有大的不同。顾名思义,第一个方法是通过资源名构造TResourceStream; 第二个方法通过资源ID构造TResourceStream,而且在实现过程中,它们都调用了Initialize方法。下面是它们的实现:

  constructor TResourceStream.Create(Instance: THandle; const ResName: string;

  ResType: PChar);

  begin

  inherited Create;

  Initialize(Instance, PChar(ResName), ResType);

  end;

  constructor TResourceStream.CreateFromID(Instance: THandle; ResID: Integer;

  ResType: PChar);

  begin

  inherited Create;

  Initialize(Instance, PChar(ResID), ResType);

  end;

  这两个方法中都有Instance参数,该参数值的含义在Insitialize中介绍过。

  3. Write方法

  TResourceStream的Write方法只完成一件事,就产生这个异常事件,其实现如下:

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

  begin

  raise EStreamError.CreateRes(SCantWriteResourceStreamError);

  end;

  从方法实现中可以看到,TSourceStream对象是不允许写数据的。一旦往资源流中写数据将产生异常事件。

  4. 析构方法Destroy

  该方法产生给资源解锁,然后释放该资源,最后调用继承的Destroy方法释放ResourceStream。其实现如下:

  destructor TResourceStream.Destroy;

  begin

  UnlockResource(HGlobal);

  FreeResource(HResInfo);

  inherited Destroy;

  end;

  回顾Initialize方法,我们不难发现:

  ● ResourceStream没有额外地给资源重新分配内存,而是直接使用HGlobal句柄所指 的内存域

  ● ResourceStream中的资源在流的生存期,始终是Lock状态,因此要根据Windows 的内存使用规则合理安排ResourceStream的使用

  ● ResourceStream只是用于访问应用程序和动态链接库中的资源的

  在Classes在单元中提供了InternalReadComponentRes函数,该函数使用了TResourceStream对象从Delphi应用程序中读取部件。Delphi是将窗体和部件信息放在模块资源的RCDATA段的。

  20.1.7 TBlobStream对象

  从Delphi 数据库开发平台这个意义上说,TBlobStream 对象是个很重要的对象。TBlobStream对象提供了修改TBlobField、TBytesField或TVarBytesField中数据的技术。开发者可以象对待文件或流那样在数据库域中读写数据。

  传统数据库发展的一个重要趋向是往多媒体数据库发展。目前比较著名和流行的数据库都支持多媒体功能,多媒体数据存储中的一大难点是数据结构不规则,数据量大。各大数据库产品是采用BLOB技术解决多媒体数据存储中的问题。Delphi的TBlobStream对象的意义就在于:一方面可以使Delphi应用程序充分利用多媒体数据库的数据管理能力;另一方面又能利用Object Pascal的强大程序设计能力给多媒体数据库提供全方向的功能扩展余地。

  使用TBlobStream对象可以在多媒体数据库的BLOB字段存储任意格式的数据。一般说来,许多多媒体数据库只能支持图像、语音或者OLE服务器支持的数据。利用TBlobStream则不同,只要是程序能够定义的数据格式,它都能在BLOB字段中读写,而不需要其它辅助工具。

  TBlobStream用构造方法Create建立数据库域和BLOB流的联接。用Read或Write 方法访问和改变域中的内容;用Seek方法,在域中定位;用Truncate方法删除域中当前位置起所有的数据。

  20.1.7.1 TBlobStream的属性和方法

  TBlobStream对象从TStream直接继承,没有增添新的属性。它覆盖了Read、Write 和Seek方法,提供了对BLOB字段的访问操作;它增添了Truncate方法以实现BLOB字段中的删除操作。

  1. Read方法

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

  Read方法从数据库域的当前位置起复制Count个字节的内容到Buffer中。Buffer也必须至少分配Count个字节。Read方法返回实际传输的字节数,因为传输的字节数可能小于Count,所以需要选择符的边界判断。

  2. Write方法

  声明:function Write(const Buffer; Count: Longint); override; Longint;

  Write方法从Buffer中向数据库域的当前位置复制Count个字节的内容。Buffer必须分配有Count个字节的内存空间,函数返回实际传输的字节数,传输过程也要进行选择符边界判断。

  3. Seek方法

  声明:function Seek(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方法

  声明:procedure Truncate;

  Truncate方法撤消TBlobField、TBytesField或TVarBytesField中从当前位置起的数据。

  5. Create方法

  声明:constructor Create(Field: TBlobField; Mode: TBlobStreamMode);

  Create方法使用Field参数建立BLOB流与BLOB字段的联接。Mode 的值可为bmRead、bmWrite和bmReadWrite。

  20.1.7.2 TBlobStream的实现原理

  说明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作为调用BDE API函数的参数值。FFieldNo代表BLOB字段的字段号,也用于BDE API的参数传递,FOpened和FMocified都是状态信息,FPosition表示BLOB流的当前位置,下面介绍TBlobStream方法实现。

  1. Create方法和Destroy方法的实现

  Create方法的功能主要是建立BlobStream流与BLOB字段的联系并初始化某些私有变量。其实现如下:

  constructor TBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);

  var

  OpenMode: DbiOpenMode;

  begin

  FField := Field;

  FDataSet := Field.DataSet;

  FRecord := FDataSet.ActiveBuffer;

  FFieldNo := Field.FieldNo;

  if FDataSet.State = dsFilter then

  DBErrorFmt(SNoFieldAccess, [FField.DisplayName]);

  if not FField.FModified then

  begin

  if Mode = bmRead then

  begin

  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;

上一页  1 2 3 4 5 6 7 8 9 下一页
  相关推荐:2010年9月计算机等级考试试题及答案解析专题
       预告:名师解析2010年9月计算机等级考试试题答案
       2010年9月计算机等级考试成绩查询时间及入口
       2010年9月计算机等考成绩查询短信免费提醒开通
文章搜索
版权声明:如果计算机等级考试网所转载内容不慎侵犯了您的权益,请与我们联系800@exam8.com,我们将会及时处理。如转载本计算机等级考试网内容,请注明出处。