首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > .NET > .NET >

想把ClientDataSet.Data的内容分块写入BUF然后发送出去,该怎么处理

2012-02-26 
想把ClientDataSet.Data的内容分块写入BUF然后发送出去想把ClientDataSet.Data的内容分块写入BUF然后通过S

想把ClientDataSet.Data的内容分块写入BUF然后发送出去
想把ClientDataSet.Data的内容分块写入BUF然后通过SOCKET发送出去。求:
1、怎样知道Data的指针位置和它的大小?
2、希望能给个例子将Data按照1000的大小分解成几个包,然后拼回。(传输过程勿管)。
谢谢了!

[解决办法]
使用SAFEARRAY

  我们将使用SAFEARRAY来通过COM传送串行化的对象。这部分将介绍如何建立和使用SAFEARRAY类型。

  SAFEARRAY是数据结构的一种。这种结构也没有什么特别的,你只需要正确地设置它,当然你要知道其中一些颇为复杂的规定。为了确保SAFEARRAY被正确地使用,它有一系列用作管理的API函数。这些API的函数负责创建、调整大小和删除SAFEARRAY。不幸的是,有关这些API函数的文档是相当少的。

  对于SAFEARRAY的内部结构我们并不关心,但了解一下是值得的。以下就是SAFEARRAY的Win32定义:

  typedef struct tagSAFEARRAY

   {

    unsigned short cDims;

    unsigned short fFeatures;

    unsigned long cbElements;

    unsigned long cLocks;

    void * pvData;

    SAFEARRAYBOUND rgsabound[ 1 ];

   } SAFEARRAY;

  这个结构的成员(cDims,cLocks等)是通过API函数来设置和管理的。真正的数据存放在pvData成员中,而SAFEARRAYBOUND结构定义该数组结构的细节。以下就是该结构成员的简要描述:


成员 描述
cDims 数组的维数
fFeatures 用来描述数组如何分配和如何被释放的标志
cbElements 数组元素的大小
cLocks 一个计数器,用来跟踪该数组被锁定的次数
pvData 指向数据缓冲的指针
rgsabound 描述数组每维的数组结构,该数组的大小是可变的

  rgsabound是一个有趣的成员,它的结构不太直观。它是数据范围的数组。该数组的大小依safe array维数的不同而有所区别。rgsabound成员是一个SAFEARRAYBOUND结构的数组--每个元素代表SAFEARRAY的一个维。

  typedef struct tagSAFEARRAYBOUND

   {

    unsigned long cElements;

    unsigned long lLbound;

   } SAFEARRAYBOUND;

  维数被定义在cDims成员中。例如,一个 'C '类数组的维数可以是[3][4][5]-一个三维的数组。如果我们使用一个SAFEARRAY来表示这个结构,我们定义一个有三个元素的rgsabound数组--一个代表一维。

  cDims = 3;


    ...


  SAFEARRAYBOUND rgsabound[ 3 ];

  rgsabound[0]元素定义第一维。在这个例子中ILBOUND元素为0,是数组的下界。cElements成员的值等于三。数组的第二维([4])可以被rgsabound结构的第二个元素定义。下界也可以是0,元素的个数是4,第三维也是这样。要注意,由于这是一个 "C "数组,因此由0开始,对于其它语言,例如Visual Basic,或者使用一个不同的开始。该数组的详细情况如下所示:


元素 cElements ILbound
rgsabound[0] 3 0
rgsabound[1] 4 0
rgsabound[2] 5 0


  关于SAFEARRAYBOUND结构其实还有很多没说的。我们将要使用的SAFEARRAY只是一个简单的单维字节数组。我们通过API函数创建数组的时候,SAFEARRAYBOUND将会被自动设置。只有在你需要使用复杂的多维数组的时候,你才需要操作这个结构。

  还有一个名字为cLocks的成员变量。很明显,它与时间没有任何的关系--它是一个锁的计数器。该参数是用来控制访问数组数据的。在你访问它之前,你必须锁定数据。通过跟踪该计数器,系统可以在不需要该数组时安全地删除它。
[解决办法]
创建SAFEARRAY

  创建一个单维SAFEARRAY的简单方法是通过使用SafeArrayCreateVector API函数。该函数可分配一个特定大小的连续内存块。

  SAFEARRAY *psa;

  file:// create a safe array to store the stream data

  file:// llen is the number of bytes in the array.


  psa = SafeArrayCreateVector( VT_UI1, 0, llen );

  SafeArrayCreateVector API创建一个SAFEARRAY,并且返回一个指向它的指针。首个参数用来定义数组的类型--它可以是任何有效的变量数据类型。为了传送一个串行化的对象,我们将使用最基本的类型--一个非负的字节数组。VT--UI1代表非负整形的变量类型,1个字节。

  常数 '0 '定义数组的下界;在C++中,通常为0。最后的参数llen定义数组元素的个数。在我们的例子中,这与我们将要传送对象的字节数是一样的。我们还没有提数组大小(llen)是怎样来的,这将在我们重新考查串行化时提及。

  在你访问SAFEARRAY数据之前,你必须调用SafeArrayAccessData。该函数锁定数据并且返回一个指针。在这里,锁定数组意味着增加该数组的内部计数器(cLocks)。

  file:// define a pointer to a byte array

  unsigned char *pData = NULL;

  SafeArrayAccessData( psa, (void**)&pData );

   ... use the safe array

  SafeArrayUnaccessData(psa);

  相应用来释放数据的函数是SafeArrayUnaccessData(),该功能释放该参数的计数。
[解决办法]
定义COM接口

  在COM中传送一个SAFEARRAY是很简单的,你需要设置自己的接口来传送SAFEARRAY结构。SAFEARRAY是一个本地的IDL数据类型。以下就是一个接口要处理的IDL代码:

  [

   object,

    uuid(EEC6D3EF-32F7-11D3-9EA1-00105A132526),

    dual,

    helpstring( "IBlobData Interface "),



    pointer_default(unique)

   ]

   interface IBlobData : IUnknown

  {

   HRESULT GetArray([out] SAFEARRAY(unsigned char) *pData);

   HRESULT SetArray([in] SAFEARRAY(unsigned char) pData );

  };

  只要你定义它包含的数据类型,SAFEARRAY就是一个有效的数据类型。语句SAFEARRAY(unsigned char)可用来传送任何类型的二进制数据。 "Unsigned char "意味着该数据将会是二进制字节,它与VT_UI1变量类型相对应。

  它的两个方法是相对的--GetArray方法从服务器得到一个对象。SetArray方法则发送一个对象给服务器。我们将不会谈及为该接口创建一个COM对象的基本问题。这个工作可通过使用ATL向导来完成。

  接下来,我们会将串行化和SAFEARRAY两部分的知识结合起来,讲述一个例子。

  Fig1.0数据类型允许用在IDispatch接口上。以下这些数据类型是可被一个类库调用的。

类型 名字 描述
byte VT_UI1 非负字节
short VT_I2 有符号16位短整型
long VT_I4 有符号32位长整型
float VT_R4 一个IEEE 4字节实型数字
double VT_R8 一个IEEE 8字节实型数字
VARIANT_BOOL VT_BOOL 16位布尔 0=false, 0xFFFF=true
SCODE VT_ERROR 16位错误码
CY VT_CY 16位货币结构
DATE VT_DATE 使用双精度数字表示的日期
BSTR VT_BSTR visual basic风格的字符结构
DECIMAL VT_DECIMAL 一个十进制的结构
IUnknown VT_UNKNOWN 一个COM接口的指针
IDispatch VT_DISPATCH COM Dispatch接口的指针
SAFEARRAY * VT_ARRAY 一个用作传送数组数据的特别结构
VARIANT * VT_VARIANT 一个VARIANT结构的指针
void * 普通的指针
VT_BYREF 任何类型(除指针外)的指针

[解决办法]
你可以直接用 ado的数据集
function TCustomClientDataSet.GetData: OleVariant;
var
DataPacket: TDataPacket;
begin
if Active then
begin
CheckBrowseMode;
FDSBase.SetProp(dspropXML_STREAMMODE, xmlOFF);
Check(FDSBase.StreamDS(DataPacket));
end else
SafeArrayCopy(FSavedPacket, DataPacket);
DataPacketToVariant(DataPacket, Result);
// 注意这句 Data 由 DataPacket 生成的
end;
我们 再看 下面的两个过程
procedure DataPacketToVariant(const DataPacket: TDataPacket; var V: OleVariant);
begin
if Assigned(DataPacket) then
begin
VarClear(V);
{$IFDEF MSWINDOWS}
tagVariant(V).vt := varByte or varArray;
tagVariant(V).PArray := DataPacket;
{$ENDIF}
{$IFDEF LINUX}
TVarData(V).VType := varByte or varArray;
TVarData(V).VArray := DataPacket;
{$ENDIF}
end else
V := NULL;
end;

function VarToDataPacket(const V: OleVariant): TDataPacket;
begin
if VarIsNull(V) then Result := nil else
begin
if not (VarIsArray(V) and (VarArrayHighBound(V, 1) > 20)) then
DatabaseError(SInvalidDataPacket);
{$IFDEF MSWINDOWS}
Result := tagVARIANT(V).PArray;
{$ENDIF}
{$IFDEF LINUX}
Result := (TVarData(V)).VArray;
{$ENDIF}
end;
end;

然后 看
TDataPacket = PSafeArray;
PSafeArray = ^TSafeArray;
tagSAFEARRAY = record
cDims: Word;
fFeatures: Word;
cbElements: Longint;
cLocks: Longint;
pvData: Pointer;
rgsabound: array[0..0] of TSafeArrayBound;
end;
TSafeArray = tagSAFEARRAY;

基本明白了 吧
我们 再看

function TCustomClientDataSet.GetDataSize: Integer;
begin
if Assigned(DataSetField) then
Result := -1
else if Active then
begin
SaveDataPacket;
Result := DataPacketSize(FSavedPacket);
ClearSavedPacket;
end
else if Assigned(FSavedPacket) then
Result := DataPacketSize(FSavedPacket)
else
Result := 0;
end;
procedure TCustomClientDataSet.WriteDataPacket(Stream: TStream; WriteSize: Boolean;
Format: TDataPacketFormat = dfBinary);
var
Size: Integer;
DataPtr: Pointer;
begin
if Active then CheckBrowseMode;
if IsCursorOpen then
begin
CheckProviderEOF;


SaveDataPacket(Format);
end;
if Assigned(FSavedPacket) then
begin
Size := DataPacketSize(FSavedPacket);
SafeArrayAccessData(FSavedPacket, DataPtr);
try
if WriteSize then
Stream.Write(Size, SizeOf(Size));
Stream.Write(DataPtr^, Size);
finally
SafeArrayUnAccessData(FSavedPacket);
end;
if Active then ClearSavedPacket;
end;
end;


热点排行