学会使用SafeArray 之 Delphi 篇

来源:互联网 发布:ubuntu离开root 编辑:程序博客网 时间:2024/06/08 05:04
在学习的时候曾经百度了一下,想查找关于delphi中SafeArray的用法,没想到资料竟然如此之少,甚至连一篇相对完整的都没有。也许正如田师傅所说,现在高手们都在实行“技术封锁”了?
 
  在CSDN技术中心有一篇关于C++的使用,说的比较详细。鉴于手中有田师傅写的代码,特拿来一部分对照自己学习的,翻译为Delphi版本。留作笔记,日后翻阅。

===============================================================================
  SAFEARRAY的主要目的是用于automation中的数组型参数的传递。因为在网络环境中,数组是不能直接传递的,而必须将其包装成SafeArray。实质上SafeArray就是将通常的数组增加一个描述符,说明其维数、长度、边界、元 素类型等信息。SafeArray也并不单独使用,而是将其再包装到VARIANT类型的变量中,然后才作为参数传送出去。在VARIANT的vt成员的 值如果包含VT_ARRAY|...,那么它所封装的就是一个SafeArray,它的parray成员即是指向SafeArray的指针。 SafeArray中元素的类型可以是VARIANT能封装的任何类型,包括VARIANT类型本身。
  Delphi中com组件或外部调用需要传递一个对象或者数组,这个时候可以考虑使用SafeArray。比如三层架构中通常需要将使用的ClientDataSet数据集传出,那么就可以将DataSet转换为SafeArray,再由其他模块转换接收。
===============================================================================

  代码块:
  pSafeArray是一个结构体,其定义在ActiveX单元中,相关定义如下:

  PSafeArray = ^TSafeArray;
  {$EXTERNALSYM tagSAFEARRAY}
  tagSAFEARRAY = record
    cDims: Word;//数组的维数
    fFeatures: Word;//用来描述数组如何分配和如何被释放的标志
    cbElements: Longint;//数组元素的大小
    cLocks: Longint;//一个计数器,用来跟踪该数组被锁定的次数
    pvData: Pointer;//指向数据缓冲的指针
    rgsabound: array[0..0] of TSafeArrayBound;//描述数组每维的数组结构,该数组的大小是可变的
  end;
  TSafeArray = tagSAFEARRAY;
  {$EXTERNALSYM SAFEARRAY}
  SAFEARRAY = TSafeArray;
===============================================================================
  一维数组的传递:
  //一维数组传递
procedure TForm1.ArrayToSafeArray(DataArray: array of string);
var
  VarBound: TVarArrayBound;
  psa: PSafeArray;
  i: Integer;
  AV1: OleVariant;
begin
  //初始化OleValue
  VariantInit(OleValue);
  VarBound.LowBound := 0;
  VarBound.ElementCount := High(DataArray) + 1;

  psa := SafeArrayCreate(VT_BSTR, 1, VarBound);//创建SafeArray对象
  //将数组元素放到SafeArray中
  for i := Low(DataArray) to High(DataArray) do
  begin
    AV1 := DataArray[i];
    SafeArrayPutElement(psa, i, TVarData(AV1).VPointer^);
  end;
  //封装到Varaint变量中
  TVarData(OleValue).VType := VT_ARRAY or VT_BSTR;
  TVarData(OleValue).VArray := pVarArray(psa);
end;

读取一维safeArray的步骤:
//使用SafeArrayGetLBound、SafeArrayGetUBound方法获取safeArray数组上下限
function TForm1.SafeArrayToText(OleValue: OleVariant): string;
var
  RVarData: TVarData;
  RBound: TVarArrayBound;
  j, UCount: Integer;
  aValue: WideString;
begin
  Result := '';
  //获取OleVaraint对象指针
  RVarData := FindVarData(OleValue)^;
  if RVarData.VArray = nil then
    Exit;
  VarResultCheck((SafeArrayGetLBound(pSafeArray(RVarData.VArray), 1, RBound.LowBound)));
  VarResultCheck(SafeArrayGetUBound(pSafearray(RVarData.VArray), 1, UCount));
  RBound.ElementCount := UCount - RBound.LowBound;
  for j := RBound.LowBound to RBound.ElementCount do
  begin
    VarResultCheck(SafeArrayGetElement(PSafeArray(RVarData.VArray), j, aValue));
    if Result = '' then
      Result := Result + aValue
    else
      Result := Result + ',' + aValue;
  end;
end;

//另外附上将DataSet通过SafeArray传递的方法,其实为多维数组的传递:
procedure TForm1.DataSetToSafeArray(ClientData: TClientDataSet);
var
  DataArray: TDoubleArray;
  VarBound: array[0..1] of TVarArrayBound;
  Demen: array[0..1] of DWORD;
  psa: PSafeArray;
  i, j, k, Count: Integer;
  AV1: OleVariant;
begin
  SetLength(DataArray, ClientData.FieldCount + 1, ClientData.RecordCount + 1);
  Count := 0;
  ClientData.First;
  while not ClientData.Eof do
  begin
    for i := 0 to ClientData.FieldCount - 1 do
    begin
      if Count = 0 then
        DataArray[i][Count] := ClientData.Fields[i].FieldName
      else
        DataArray[i][Count] := ClientData.Fields[i].Value;
    end;
    if Count > 0 then
      ClientData.Next;
    inc(Count);
  end;
  //--------开始转换为SafeArray-----------
  //初始化OleValue
  VariantInit(OleValue);
  VarBound[0].LowBound := 0;
  VarBound[0].ElementCount := ClientData.FieldCount + 1;
  VarBound[1].LowBound := 0;
  VarBound[1].ElementCount := ClientData.RecordCount + 1;

  psa := SafeArrayCreate(VT_BSTR, 2, VarBound);
  for i := Low(DataArray) to High(DataArray) do
  begin
    Demen[0] := i;
    for j := Low(DataArray[i]) to High(DataArray[i]) do
    begin
      Demen[1] := j;
      AV1 := DataArray[i][j];
      SafeArrayPutElement(psa, Demen, TVarData(AV1).VPointer^);
    end;
  end;

  TVarData(OleValue).VType := VT_ARRAY or VT_BSTR;
  TVarData(OleValue).VArray := pVarArray(psa);
end;

//读多维数组,读出后再转换为想要的格式:
function TForm1.SafeArrayToDataSet(oleValue: OleVariant): string;
var
  RVarData: TVarData;
  RBound: array[0..1] of TVarArrayBound;
  Demen: array[0..1] of DWORD;
  i, j, DCount, UCount, iCount: Integer;
  aValue: WideString;
begin
  Result := '';
  RVarData := FindVarData(OleValue)^;
  if RVarData.VArray = nil then
    Exit;
  DCount := RVarData.Varray^.DimCount; //取维数
  for i := 0 to DCount - 1 do
  begin
    VarResultCheck((SafeArrayGetLBound(pSafeArray(RVarData.VArray), i + 1, RBound[i].LowBound)));
    VarResultCheck(SafeArrayGetUBound(pSafearray(RVarData.VArray), i + 1, UCount));
    RBound[i].ElementCount := UCount - RBound[i].LowBound;
  end;
  for j := RBound[1].LowBound to RBound[1].ElementCount do
  begin
    Demen[1] := j;
    for iCount := RBound[0].LowBound to RBound[0].ElementCount do
    begin
      Demen[0] := iCount;
      VarResultCheck(SafeArrayGetElement(PSafeArray(RVarData.VArray), demen, aValue));
      if Result = '' then
        Result := Result + aValue
      else
        Result := Result + ',' + aValue;
    end;
  end;
end;

原创粉丝点击