===============================================================================
⊙TMemoryStream对象的实现原理
===============================================================================
1、TMemoryStream 从 TCustomMemoryStream 对象直接继承
2、因此可以享用 TCustomMemoryStream 的属性和方法。
3、前面讲过,TCustomMemoryStream 是用于内存中数据操作的抽象对象
4、它为 MemoryStream 对象的实现提供了框架
5、框架中的内容还要由具体 MemoryStream 对象去填充。
6、TMemoryStream 对象就是按动态内存管理的需要填充框架中的具体内容。
7、下面介绍 TMemoryStream 对象的实现。
===============================================================================
1. TMemoryStream属性的实现
===============================================================================
1、TMemoryStream 在其protected 部分增加了一个 Capacity 属性
2、该属性决定了 MemoryStream 所占动态内存的大小。
3、TMemoryStream 首先在 private 部分声明了 FCapacity 变量作为存储 Capacity 属性值的数据域
4、然后在 protected 部分声明了该属性。
5、在属性声明的读控制部分简单读取 FCapacity 的值
6、在写控制处调用了方法 SetCapacity。
7、该方法除了给 FCapacity 赋值外还执行了修改 Capacity 属性所必需操作如状态改变等。
===============================================================================
1. TMemoryStream属性的实现
===============================================================================
TMemoryStream = class(TCustomMemoryStream)
private
FCapacity: Longint;
procedure SetCapacity(NewCapacity: Longint);
protected
…
property Capacity: Longint read FCapacity write SetCapacity;
public
…
end;
procedure TMemoryStream.SetCapacity(NewCapacity: Longint);
begin
SetPointer(Realloc(NewCapacity), FSize);
FCapacity := NewCapacity;
end;
1、在 SetCapacity 方法先是调用 Realloc 重新分配内存
2、然后用 NewCapacity 的值给 FCapacity 赋值。
3、Realloc 方法进行某些对象状态的改变。
const
MemoryDelta = $2000; { Must be a power of 2 }
function TMemoryStream.Realloc(var NewCapacity: Longint): Pointer;
begin
if (NewCapacity > 0) and (NewCapacity <> FSize) 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;
procedure TCustomMemoryStream.SetPointer(Ptr: Pointer; Size: Longint);
begin
FMemory := Ptr;
FSize := Size;
end;
===============================================================================
2. Realloc方法
===============================================================================
1、Realloc 方法是 TMemoryStream 动态内存分配的核心
2、它的 SetSize、SetCapacity 等方法最终都是调用 Realloc 进行内存的分配和初始化工作的。
3、它的实现如下:
const
MemoryDelta = $2000; { Must be a power of 2 }
function TMemoryStream.Realloc(var NewCapacity: Longint): Pointer;
begin
if (NewCapacity > 0) and (NewCapacity <> FSize) 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;
1、Realloc 方法是以 8K 为单位分配动态内存的
2、方法中的第一句 if 语句就是执行该操作。
3、如果传入的 NewCapacity 参数值为 0,则释放流中的内存。
4、Realloc 方法用 GLobalFreePtr 函数释放内存
5、用 GlobalAllocPtr 分配内存
6、用 GlobalReallocPtr 进行内存的重分配。
7、如果原来的 Capacity 属性值为 0,则调用 GlobalAllocPtr
8、否则调用 GlobalReallocPtr。
9、最后如果 Result 为 nil 则触发内存流错的异常事件,
10、否则返回指向分配的内存的指针。
===============================================================================
3. Write方法
===============================================================================
1、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;
1、Buffer 中存储要写入流的二进制数据
2、如果要写入的数据的字节超出了流的内存池的大小,则调用 SetCapacity 方法再分配内存
3、然后用内存复制函数将 Buffer 中的数据复制到 FMemory 中。
4、接着移动位置指针,并返回写入数据的字节数。
5、分析这段程序可以知道,FCapacity 的值和 FSize 的值是不同的。
===============================================================================
4.Clear方法
===============================================================================
1、Clear 方法消除内存流中的数据
2、将 Memory 属性置为 nil
3、并将 FSize 和 FPosition 的值设为 0。
4、其实现如下:
procedure TMemoryStream.Clear;
begin
SetCapacity(0);
FSize := 0;
FPosition := 0;
end;
===============================================================================
5.LoadFromStream和LoadFromFile方法
===============================================================================
1、LoadFromStream 方法首先根据传入的 Stream 的 Size 属性值重新分配动态内存
2、然后调用 Stream 的 ReadBuffer 方法往 FMemory 中复制数据
3、结果 Stream 的全部内容在内存中有了一份完整拷贝。
4、其实现如下:
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;
procedure TMemoryStream.LoadFromFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
1、LoadFromFile 与 LoadFromStream 是一对方法。
2、LoadFromFile 首先创建了一个 TFileStream 对象
3、然后调用 LoadFromStream 方法
4、将 FileStream 文件流中的数据写入 MemoryStream 中。
===============================================================================
⊙ TCustomMemoryStream对象
===============================================================================
1、TCustomMemoryStream 被用做祖先类作为内存流的一个抽象的基类。
2、当定义一个能够被存储在内存中的数据流对象时,
使用TCustomMemoryStream 做为基类
3、内存流对提供对被储存在一个比较可接近媒介中的数据像文件一样的存取。
4、 当内存流被建立的时候 , 数据能被移到一个内部内存缓冲区。
5、在操纵一条内存流的数据之后,当内存流被消毁的时候 , 数据能被写出到它的真实储存介质。
6、TCustomMemoryStream 不能实例化,它是一个描象类,实现对所有的内存流通用性
7、实例化内存流,使用TMemoryStream 或TResourceStream
{ TCustomMemoryStream abstract class }
TCustomMemoryStream = class(TStream)
private
FMemory: Pointer;
FSize, FPosition: Longint;
protected
procedure SetPointer(Ptr: Pointer; Size: Longint);
public
function Read(var Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
procedure SaveToStream(Stream: TStream);
procedure SaveToFile(const FileName: string);
property Memory: Pointer read FMemory;
end;
{ TCustomMemoryStream }
procedure TCustomMemoryStream.SetPointer(Ptr: Pointer; Size: Longint);
begin
FMemory := Ptr;
FSize := Size;
end;
function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
Result := FSize - FPosition;
if Result > 0 then
begin
if Result > Count then Result := Count;
Move(Pointer(Longint(FMemory) + FPosition)^, Buffer, Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;
function TCustomMemoryStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
case Origin of
soFromBeginning: FPosition := Offset;
soFromCurrent: Inc(FPosition, Offset);
soFromEnd: FPosition := FSize + Offset;
end;
Result := FPosition;
end;
procedure TCustomMemoryStream.SaveToStream(Stream: TStream);
begin
if FSize <> 0 then Stream.WriteBuffer(FMemory^, FSize);
end;
procedure TCustomMemoryStream.SaveToFile(const FileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
SaveToStream(Stream);
finally
Stream.Free;
end;
end;
===============================================================================
⊙ TResourceStream对象
===============================================================================
1、TResourceStream 对象是另一类 MemoryStream 对象,它提供对 Windows 应用程序资源的访问
2、因此称它为资源流。
3、TResourceSream 也是从 TCustomMemoryStream 继承的。
4、因此在 TCustomMemoryStream 对象的基础上,定义了与指定资源模块或资源文件建立连接的构造方法,
5、并且还覆盖了 Write,以实现向资源文件中写数据。
===============================================================================
⊙ TResourceStream的实现
===============================================================================
1、TResourceStream 没有定义新的属性
2、但它在 private 部分定义了两个数据域 HResInfo 和 HGlobol
3、一个私有方法 Initialize
4、它们的定义如下:
TResourceStream = class(TCustomMemoryStream)
private
HResInfo: HRSRC;
HGlobal: THandle;
procedure Initialize(Instance: THandle; Name, ResType: PChar);
…
end;
1、HRSRC 是描述 Windows 资源信息的结构句柄。
2、HGlobal 变量代表资源所在模块的句柄。
3、如果操作的是应用程序资源,HGlohal 就代表 EXE 程序的句柄;
4、如果是动态链接库(DLL),则 HGlobal 代表动态链接库的句柄。
5、TResourceStream 对象使用这两个变量访问应用程序或动态链接库中的资源。
1、Initialize 方法是 TResourceStream 对象内部使用的。
2、它的构造方法 Create 和 CreateFromID 都是调用 Initialize 方法完成对 TResourceStream的初始化。
3、它的实现如下:
procedure TResourceStream.Initialize(Instance: THandle; Name, ResType: PChar);
procedure Error;
begin
raise EResNotFound.CreateFmt(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;
1、该方法实现中,首先调用 Windows 函数 FoundResource 得到由参数 Instance
指定的模块中的名为 Name 和类型为 ResType 的资源
2、然后调用 LoadResource 将资源调用内存,并返回该资源在内存中的句柄
3、最后,将该资源复制到 ResourceStream 中。
4、方法的 Instance 参数代表要调用的资源所在的模块句柄。
5、模块可以是可执行文件,也可以是动态链接库。
6、如果在读取资源时没在模块中发现要找的资源则产生异常事件。
===============================================================================
⊙ 构造方法Create和CreateFromID
===============================================================================
1、这两个方法在实现上没有大的不同。
2、顾名思义,第一个方法是通过资源名构造 TResourceStream;
3、第二个方法通过资源 ID 构造 TResourceStream,
4、而且在实现过程中,它们都调用了Initialize方法。
5、下面是它们的实现:
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;
===============================================================================
⊙ Write方法
===============================================================================
1、TResourceStream 的 Write 方法只完成一件事,就产生这个异常事件,其实现如下:
function TResourceStream.Write(const Buffer; Count: Longint): Longint;
begin
raise EStreamError.CreateRes(@SCantWriteResourceStreamError);
end;
1、从方法实现中可以看到,TSourceStream对象是不允许写数据的。
2、一旦往资源流中写数据将产生异常事件。
===============================================================================
⊙ 析构方法Destroy
===============================================================================
1、该方法产生给资源解锁
2、然后释放该资源
3、最后调用继承的 Destroy 方法释放 ResourceStream。
4、其实现如下:
destructor TResourceStream.Destroy;
begin
UnlockResource(HGlobal);
FreeResource(HGlobal);
inherited Destroy;
end;
===============================================================================
⊙ 总结
===============================================================================
回顾Initialize方法,我们不难发现:
● ResourceStream 没有额外地给资源重新分配内存,而是直接使用HGlobal句柄所指 的内存域
● ResourceStream中的资源在流的生存期,始终是Lock状态,
因此要根据 Windows 的内存使用规则合理安排 ResourceStream 的使用
● ResourceStream 只是用于访问应用程序和动态链接库中的资源的
1、在 Classes 在单元中提供了 InternalReadComponentRes 函数
2、该函数使用了 TResourceStream 对象
3、从 Delphi 应用程序中读取部件。
4、Delphi 是将窗体和部件信息放在模块资源的 RCDATA 段的。
===============================================================================
⊙ TBlobStream对象
===============================================================================
1、从 Delphi 数据库开发平台这个意义上说,TBlobStream 对象是个很重要的对象。
2、TBlobStream 对象提供了修改 TBlobField、TBytesField 或 TVarBytesField 中数据的技术。
3、开发者可以象对待文件或流那样在数据库域中读写数据。
4、传统数据库发展的一个重要趋向是往多媒体数据库发展。
5、目前比较著名和流行的数据库都支持多媒体功能
6、多媒体数据存储中的一大难点是数据结构不规则,数据量大。
7、各大数据库产品是采用 BLOB 技术解决多媒体数据存储中的问题。
8、Delphi 的 TBlobStream 对象的意义就在于:
1、一方面可以使 Delphi 应用程序充分利用多媒体数据库的数据管理能力;
2、另一方面又能利用 Object Pascal 的强大程序设计能力给多媒体数据库提供全方向的功能扩展余地。
9、使用 TBlobStream 对象可以在多媒体数据库的BLOB字段存储任意格式的数据。
10、一般说来,许多多媒体数据库只能支持图像、语音或者 OLE 服务器支持的数据。
11、利用 TBlobStream 则不同,只要是程序能够定义的数据格式,它都能在 BLOB 字段中读写,而不需要其它辅助工具。
12、TBlobStream 用构造方法 Create 建立数据库域和BLOB流的联接。
13、用 Read 或 Write 方法访问和改变域中的内容;
14、用 Seek 方法,在域中定位;
15、用 Truncate 方法删除域中当前位置起所有的数据。
===============================================================================
⊙ TBlobStream的属性和方法
===============================================================================
1、TBlobStream 对象从 TStream 直接继承,没有增添新的属性。
2、它覆盖了 Read、Write 和 Seek 方法,提供了对 BLOB 字段的访问操作;
3、它增添了 Truncate 方法以实现 BLOB 字段中的删除操作
===============================================================================
⊙ Read方法
===============================================================================
声明:function Read(var Buffer; Count: Longint): Longint;
1、Read 方法从数据库域的当前位置起复制 Count 个字节的内容到 Buffer 中。
2、Buffer 也必须至少分配 Count 个字节。
3、Read 方法返回实际传输的字节数,因为传输的字节数可能小于 Count,
所以需要选择符的边界判断。
===============================================================================
⊙ Write方法
===============================================================================
声明:function Write(const Buffer; Count: Longint); override; Longint;
1、Write 方法从 Buffer 中向数据库域的当前位置复制 Count 个字节的内容。
2、Buffer 必须分配有 Count 个字节的内存空间,
函数返回实际传输的字节数,传输过程也要进行选择符边界判断。
===============================================================================
⊙ Seek方法
===============================================================================
声明:function Seek(Offset: Longint; Origin: Word): Longint;
1、Seek 方法重新设置 BLOB 流中的指针位置。
2、如果 Origin 的值是 soFromBeginning,则新的指针位置是Offset;
3、如 Origin 的值是 soFromCurrent,则新的指针位置是Position+Offset;
4、如果 Origin 的值是 SoFromCurrent,则新的指针位置是Size+Offset。
5、函数返回新的指针位置值。
6、当 Origin 为 0(SoFromBegin)时,Offse t的值必须大于等于零;
7、当 Origin 的值为 2(SoFromEnd),Offset 的值必须小于等于零。
===============================================================================
⊙ Truncate方法
===============================================================================
声明:procedure Truncate;
1、Truncate 方法撤消 TBlobField、TBytesField 或 TVarBytesField 中从当前位置起的数据。
===============================================================================
⊙ Create方法
===============================================================================
声明:constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
1、Create 方法使用 Field 参数建立 BLOB 流与 BLOB 字段的联接。
2、Mode 的值可为 bmRead、bmWrite 和 bmReadWrite。
===============================================================================
⊙ TBlobStream的实现原理
===============================================================================
1、说明TBlobStream对象的实现原理,不可避免地要涉及它的私有域,下面是私有域的定义:
TBlobStream = class(TStream)
private
FField: TBlobField;
FDataSet: TBDEDataSet;
FBuffer: PChar;
FMode: TBlobStreamMode;
FFieldNo: Integer;
FOpened: Boolean;
FModified: Boolean;
FPosition: Longint;
FBlobData: TBlobData;
FCached: Boolean;
FCacheSize: Longint;
function GetBlobSize: Longint;
public
constructor Create(Field: TBlobField; Mode: TBlobStreamMode);
destructor Destroy; override;
function Read(var Buffer; Count: Longint): Longint; override;
function Write(const Buffer; Count: Longint): Longint; override;
function Seek(Offset: Longint; Origin: Word): Longint; override;
procedure Truncate;
end;
2、FField 是与 BLOB 流相联的数据库 BLOB 域,该域用于 BLOB 流的内部访问。
3、FDataSet 是代表 FField 所在的数据库,它可以是 TTable 部件,也可以是 TQuery 部件。
4、FRecord 和 FBuffer 都是 BLOB 流内部使用的缓冲区,
5、用于存储 FField 所在记录的数据,
6、该数据记录中不包含 BLOB 数据,TBlobStream使用 FRecord 作为调用 BDE API 函数的参数值。
7、FFieldNo 代表 BLOB 字段的字段号,也用于BDE API 的参数传递,FOpened 和 FMocified 都是状态信息
8、FPosition 表示 BLOB 流的当前位置,下面介绍 TBlobStream 方法实现。
===============================================================================
⊙ Create方法和Destroy方法的实现
===============================================================================
1、Create 方法的功能主要是建立 BlobStream 流与 BLOB 字段的联系并初始化某些私有变量。
2、其实现如下:
constructor TBlobStream.Create(Field: TBlobField; Mode: TBlobStreamMode);
var
OpenMode: DbiOpenMode;
begin
FMode := Mode;
FField := Field;
FDataSet := FField.DataSet as TBDEDataSet;
FFieldNo := FField.FieldNo;
if not FDataSet.GetActiveRecBuf(FBuffer) then Exit;
if FDataSet.State = dsFilter then
DatabaseErrorFmt(SNoFieldAccess, [FField.DisplayName], FDataSet);
if not FField.Modified then
begin
if Mode = bmRead then
begin
FCached := FDataSet.FCacheBlobs and (FBuffer = FDataSet.ActiveBuffer) and
(FField.IsNull or (FDataSet.GetBlobData(FField, FBuffer) <> ''));
OpenMode := dbiReadOnly;
end else
begin
FDataSet.SetBlobData(FField, FBuffer, '');
if FField.ReadOnly then DatabaseErrorFmt(SFieldReadOnly,
[FField.DisplayName], FDataSet);
if not (FDataSet.State in [dsEdit, dsInsert]) then
DatabaseError(SNotEditing, FDataSet);
OpenMode := dbiReadWrite;
end;
if not FCached then
begin
if FDataSet.State = dsBrowse then
FDataSet.GetCurrentRecord(FDataSet.ActiveBuffer);
Check(DbiOpenBlob(FDataSet.Handle, FBuffer, FFieldNo, OpenMode));
end;
end;
FOpened := True;
if Mode = bmWrite then Truncate;
end;
1、该方法首先是用传入的 Field 参数给 FField,FDataSet,FRecord 和 FFieldNo赋值。
2、方法中用 AllocMem 按当前记录大小分配内存,
3、并将指针赋给 FBuffer,用 DataSet 部件的 GetCurrentRecord 方法,
4、将记录的值赋给 FBuffer,但不包括BLOB数据。
5、方法中用到的 DbiOpenBlob 函数是 BDE 的 API 函数,该函数用于打开数据库中的 BLOB 字段。
6、最后如果方法传入的 Mode 参数值为 bmWrite,就调用 Truncate 将当前位置指针以后的数据删除。
分析这段源程序不难知道:
● 读写 BLOB 字段,不允许 BLOB 字段所在 DataSet 部件有 Filter,否则产生异常事件
● 要读写 BLOB 字段,必须将 DataSet 设为编辑或插入状态
● 如果 BLOB 字段中的数据作了修改,则在创建 BLOB 流时,不再重新调用 DBiOpenBlob 函数,
而只是简单地将 FOpened 置为 True,这样可以用多个 BLOB 流对同一个 BLOB 字段读写
3、Destroy 方法释放 BLOB 字段和为 FBuffer 分配的缓冲区,其实现如下:
destructor TBlobStream.Destroy;
begin
if FOpened then
begin
if FModified then FField.Modified := True;
if not FField.Modified and not FCached then
DbiFreeBlob(FDataSet.Handle, FBuffer, FFieldNo);
end;
if FModified then
try
FDataSet.DataEvent(deFieldChange, Longint(FField));
except
ApplicationHandleException(Self);
end;
end;
1、如果 BLOB 流中的数据作了修改,就将 FField 的 FModified 置为 True;
2、如果 FField 的 Modified 为 False 就释放 BLOB 字段,
3、如果 FBuffer 不为空,则释放临时内存。
4、最后根据 FModified 的值来决定是否启动 FField 的事件处理过程 DataChanged。
5、不难看出,如果 BLOB 字段作了修改就不释放 BLOB 字段,
6、并且对 BLOB 字段的修改只有到 Destroy 时才提交,
7、这是因为读写 BLOB 字段时都避开了 FField,而直接调用 BDE API函数。
8、这一点是在应用 BDE API 编程中很重要,即一定要修改相应数据库部件的状态。
===============================================================================
⊙ Read和Write方法的实现
===============================================================================
1、Read和Write方法都调用BDE API函数完成数据库BLOB字段的读写,其实现如下:
function TBlobStream.Read(var Buffer; Count: Longint): Longint;
var
Status: DBIResult;
begin
Result := 0;
if FOpened then
begin
if FCached then
begin
if Count > Size - FPosition then
Result := Size - FPosition else
Result := Count;
if Result > 0 then
begin
Move(PChar(FDataSet.GetBlobData(FField, FBuffer))[FPosition], Buffer, Result);
Inc(FPosition, Result);
end;
end else
begin
Status := DbiGetBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition,
Count, @Buffer, Result);
case Status of
DBIERR_NONE, DBIERR_ENDOFBLOB:
begin
if FField.Transliterate then
NativeToAnsiBuf(FDataSet.Locale, @Buffer, @Buffer, Result);
if FDataset.FCacheBlobs and (FBuffer = FDataSet.ActiveBuffer) and
(FMode = bmRead) and not FField.Modified and (FPosition = FCacheSize) then
begin
FCacheSize := FPosition + Result;
SetLength(FBlobData, FCacheSize);
Move(Buffer, PChar(FBlobData)[FPosition], Result);
if FCacheSize = Size then
begin
FDataSet.SetBlobData(FField, FBuffer, FBlobData);
FBlobData := '';
FCached := True;
DbiFreeBlob(FDataSet.Handle, FBuffer, FFieldNo);
end;
end;
Inc(FPosition, Result);
end;
DBIERR_INVALIDBLOBOFFSET:
{Nothing};
else
DbiError(Status);
end;
end;
end;
end;
1、Read 方法使用了BDE API 的 DbiGetBlob 函数从 FDataSet 中读取数据,
2、在本函数中,各参数的含义是这样的:
1、FDataSet.Handle 代表D ataSet 的 BDE 句柄
2、FReacord 表示 BLOB 字段所在记录,
3、FFieldNo 表示 BLOB 字段号,
4、FPosition 表示要读的的数据的起始位置,
5、Count 表示要读的字节数,
6、Buffer 是读出数据所占的内存,
7、Result 是实际读出的字节数。
8、该 BDE 函数返回函数调用的错误状态信息。
9、Read 方法还调用了 NativeToAnsiBuf 进行字符集的转换。
function TBlobStream.Write(const Buffer; Count: Longint): Longint;
var
Temp: Pointer;
begin
Result := 0;
if FOpened then
begin
if FField.Transliterate then
begin
GetMem(Temp, Count);
try
AnsiToNativeBuf(FDataSet.Locale, @Buffer, Temp, Count);
Check(DbiPutBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition,
Count, Temp));
finally
FreeMem(Temp, Count);
end;
end else
Check(DbiPutBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition,
Count, @Buffer));
Inc(FPosition, Count);
Result := Count;
FModified := True;
FDataSet.SetBlobData(FField, FBuffer, '');
end;
end;
1、Write 方法调用了 BDE API 的 DbiPutBlob 函数实现往数据库 BLOB 字段存储数据。
该函数的各参数含义如下:
调用函数DbiPutBlob的各传入参数的含义
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
参数名 含义
──────────────────────────────
FDataSetHandle写入的数据库的BDE句柄
FRecord 写入数据的BLOB字段所在的记录
FFieldNo BLOB 字段号
FPosition 写入的起始位置
Count 写入的数据的字节数
Buffer所写入的数据占有的内存地址
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2、方法中还根据 FField 和 FTransliterate 的值判断是否进行相应的字符集转换
3、最后移动 BLOB 流的位置指针,并将修改标志 FModified 置为 True。
===============================================================================
⊙ Seek和GetBlobSize方法的实现
===============================================================================
1、Seek 方法的功能主要是移动 BLOB 流的位置指针。
2、GetBlobSize 方法是私有的,在 Seek 方法中被调用,其功能是得到 BLOB 数据的大小。
3、它们的实现如下:
function TBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
case Origin of
soFromBeginning: FPosition := Offset;
soFromCurrent: Inc(FPosition, Offset);
soFromEnd: FPosition := GetBlobSize + Offset;
end;
Result := FPosition;
end;
function TBlobStream.GetBlobSize: Longint;
begin
Result := 0;
if FOpened then
if FCached then
Result := Length(FDataSet.GetBlobData(FField, FBuffer)) else
Check(DbiGetBlobSize(FDataSet.Handle, FBuffer, FFieldNo, Result));
end;
GetBlobSize调用了BDE API的DbiGetBlobSize函数,该函数的参数的含义同前面的API函数相同。
===============================================================================
⊙ Truncate方法
===============================================================================
1、该方法是通过调用BDE API函数实现的。其实现如下:
procedure TBlobStream.Truncate;
begin
if FOpened then
begin
Check(DbiTruncateBlob(FDataSet.Handle, FBuffer, FFieldNo, FPosition));
FModified := True;
FDataSet.SetBlobData(FField, FBuffer, '');
end;
end;
2、该方法从 BLOB 流的当前位置起删除所有数据,
3、并设置修改标志 FModified 为 True。
4、在 Delphi VCL 中许多部件特别是数据库应用方面的部件都用 BDE API 函数完成对数据库的访问
5、如 Data Access 和 Data Control 部件。
6、各种数据库部件都是 BDE API 函数外层的包装简化了对数据库的访问操作。
7、BDE API 中还提供了避开 BDE 配置工具在程序中直接处理 Alias(建立、修改、删除等)的函数支持
这也是部件所没有提供的。
8、在 Delphi 数据库应用安装程序中,这些 Alias 操作函数无疑是相当重要的。
9、有关BDE API函数的详细介绍,可阅读Delphi2.0 Client/Server Suite所带的BDE API 帮助文件。