在学习的时候曾经百度了一下,想查找关于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;