航行日志

SAFEARRAY(安全数组)使用详解

SAFEARRAY(安全数组)使用详解
一,何谓SAFEARRAY(安全数组):
winddows操作系统与使用 SAFEARRAY 定义。 下列 SAFEARRAY 结构是 SAFEARRAY 典型、 一般定义:
[cpp]
typedef struct FARSTRUCT tagSAFEARRAY { unsigned short cDims; // 安全数组的大小
unsigned short fFeatures; //安全数组的标记,取值见下表

#if defined(WIN32) //如果在windows系统上
unsigned long cbElements; //安全数组元素的大小 //不包括安全数组数据的大小
unsigned long cLocks; // 被锁定次数

#else
unsigned short cbElements;
unsigned short cLocks;
unsigned long handle; // 仅用在 Macintosh 系统上

#endif void HUGEP* pvData; // 数据指针
SAFEARRAYBOUND rgsabound[1]; //安全数组的下界,元素数, 结构见下说明 } SAFEARRAY;

typedef struct tagSAFEARRAYBOUND {
unsigned long cElements;// 元素数
long lLbound;// 数组的下界
} SAFEARRAYBOUND;

fFeatures; //安全数组的标记说明
FADF_AUTO 0x0001在栈上创建数组
FADF_STATIC 0x0002在堆上创建数组
FADF_EMBEDDED 0x0004在结构中创建
FADF_FIXEDSIZE 0x0010不能改变数组大小
FADF_RECORD 0x0020记录容器
FADF_HAVEIID 0x0040有IID 身份标记 数组
FADF_HAVEVARTYPE 0x0080VT 类型数组
FADF_BSTR 0x0100BSTR数组
FADF_UNKNOWN 0x0200IUnknown 数组
FADF_DISPATCH 0x0400IDispatch
数组
FADF_VARIANT 0x0800VARIANTs数组
FADF_RESERVED 0xF0E8余留,将来使用
[/cpp]
Microsoft Windows 2000 和 WindowsXP 等操作系统系统使用整个 Win32API。 因此, 是运行 Windows 2000 或 WindowsXP, 计算机上之后处理条件指令, Oaidl.h 文件与以下示例代码中可能定义 SAFEARRAY:
[cpp]
typedef struct tagSAFEARRAY {
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[ 1 ];
} SAFEARRAY;
Oaidl.h 文件, 中 SAFEARRAYBOUND 是如以下示例代码中定义一个结构:
typedef struct tagSAFEARRAYBOUND {
unsigned long cElements;
long lLbound;
} SAFEARRAYBOUND;
[/cpp]

Oaidl.h 文件还包含对于, 可用于访问 SAFEARRAY 函数原型。
二,什么情况下使用
1:在使用vc编写COM组件的时候,需要一次传递很多的数据时,使用SAFEARRAY就会很方便
2:VisualBasic 和 C 之间或在 VisualBasic 和 VisualC++, 之间传递数组或字符串,用 C、 C++ 或 ATL 创建 DLL时用SAFEARRAY。

三,使用实例

1:使用SafeArrayAllocDescriptor在栈上创建一维数组
[cpp]
//创建SAFEARRAY数组,每个元素为long型,该数组是一维数组
long nData[10]={1,2,3,4,5,6,7,8,9,10};

SAFEARRAY* pArray=NULL;
HRESULT hr=SafeArrayAllocDescriptor(1,&pArray);//创建SAFEARRAY结构的对象
pArray->cbElements=sizeof(nData[0]);
pArray->rgsabound[0].cElements=10;
pArray->rgsabound[0].lLbound=0;
pArray->pvData=nData;
pArray->fFeatures=FADF_AUTO|FADF_FIXEDSIZE;//FADF_AUTO指定在栈上分配数据,并且大小不可以改变(固定为10)

//访问SAFEARRAY数组
long pValue=NULL;
SafeArrayAccessData(pArray,(void*
)&pValue);//取得数据存到指针pValue
long Low(0),High(0);
hr=SafeArrayGetLBound(pArray,1,&Low);//维数索引从1开始
hr=SafeArrayGetUBound(pArray,1,&High);//维数索引从1开始

SafeArrayUnaccessData(pArray);
SafeArrayDestroy(pArray);
[/cpp]
这种方法在栈上分配数组元素所占的空间,即nData数组所用的空间
2:vc++创建dll, VisualBasic向dll中传数组
Vc++中创建dll工程StdDLL,把下边函数复制到cpp中
[cpp]
long declspec (dllexport) stdcall Func2(SAFEARRAY *ppsaMyArray) {
HRESULT hr;
HUGEP byte
plData;
hr = SafeArrayAccessData(ppsaMyArray, (void HUGEP FAR)&plData);//取得数组数据
byte baa;
baa=plData[0];//第一个数据
SafeArrayUnaccessData(
ppsaMyArray);//取消数据访问
SafeArrayDestroy(*ppsaMyArray);//销毁
return 2;
}
[/cpp]
将以下文本追加到 StdDLL.def 文件中现有文本:
Func2
VisualBasic中创建工程
添加以下声明代码,
Private Declare Function Func2 Lib”C:StdDLLDebugStdDLL.dll” (MyArray() As Long) As Long ‘注意文件路径和你机器的一致
添加 Command1命令按钮,把以下代码附加到 Command 1 _ Click 事件过程中
[cpp]Dim Result As Long Dim MyArrayOfLongs(3) As Long
MyArrayOfLongs(0) = 0
MyArrayOfLongs(1) = 1
MyArrayOfLongs(2) = 2
Result = Func2(MyArrayOfLongs())
MsgBox (“Func2 returned “ & Result) [/cpp]
四,注意问题
1) 在堆上创建SAFEARRAY数组
2) 一方创建,一方回收
3) 接收方不可以修改SAFEARRAY的数据,只能读或者销毁

 

 

首先介绍SafeArray使用,在介绍SafeArray中的结构。看完该节文章,SafeArray的陌生感一扫而去。

 

SafeArray 在ADO编程中经常使用。它的主要目的是用于automation中的数组型参数的传递。因为在网络环境中,数组是不能直接传递的,而必须将其包装成 SafeArray。实质上SafeArray就是将通常的数组增加一个描述符,说明其维数、长度、边界、元素类型等信息。SafeArray也并不单独使用,而是将其再包装到VARIANT类型的变量中,然后才作为参数传送出去。在VARIANT的vt成员的值如果包含VT_ARRAY|…,那么它所封装的就是一个SafeArray,它的parray成员即是指向SafeArray的指针。SafeArray中元素的类型可以是VARIANT能封装的任何类型,包括VARIANT类型本身。

 

使用SafeArray的具体步骤:

方法一:

包装一个SafeArray:

(1)定义变量,如:

VARIANT varChunk;

SAFEARRAY *psa;

SAFEARRAYBOUND rgsabound[1];

 

(2)创建SafeArray描述符:

//read array from a file.

uIsRead=f.Read(bVal,ChunkSize);

if(uIsRead==0)

break;

rgsabound[0].cElements = uIsRead;

rgsabound[0].lLbound = 0;

psa = SafeArrayCreate(VT_UI1,1,rgsabound);

 

(3)放置数据元素到SafeArray:

for(long index=0;index<uIsRead;index++)

{

if(FAILED(SafeArrayPutElement(psa,&index,&bVal[index])))

::MessageBox(NULL,”出毛病了。”,”提示”,MB_OK | MB_ICONWARNING);

}

一个一个地放,挺麻烦的。

 

(4)封装到VARIANT内:

varChunk.vt = VT_ARRAY|VT_UI1;

varChunk.parray = psa;

这样就可以将varChunk作为参数传送出去了。

 

 

读取SafeArray中的数据的步骤:

(1)用SafeArrayGetElement一个一个地读

BYTE buf[lIsRead];

for(long index=0; index<lIsRead; index++)

{

::SafeArrayGetElement(varChunk.parray,&index,buf+index);

}

就读到缓冲区buf里了。

 

方法二:

使用SafeArrayAccessData直接读写SafeArray的缓冲区:

(1)读缓冲区:

BYTE *buf;

SafeArrayAccessData(varChunk.parray, (void **)&buf);

f.Write(buf,lIsRead);

SafeArrayUnaccessData(varChunk.parray);

 

(2)写缓冲区:

BYTE *buf;

::SafeArrayAccessData(psa, (void **)&buf);

for(long index=0;index<uIsRead;index++)

{

buf[index]=bVal[index];

}

::SafeArrayUnaccessData(psa);

varChunk.vt = VT_ARRAY|VT_UI1;

varChunk.parray = psa;

这种方法读写SafeArray都可以,它直接操纵SafeArray的数据缓冲区,比用SafeArrayGetElement和 SafeArrayPutElement速度快。特别适合于读取数据。但用完之后不要忘了调用::SafeArrayUnaccessData (psa),否则会出错的。

 

以下就是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;

// create a safe array to store the stream data

// 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)。

// define a pointer to a byte array

unsigned char *pData = NULL;

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

… use the safe array

SafeArrayUnaccessData(psa);

相应用来释放数据的函数是SafeArrayUnaccessData(),该功能释放该参数的计数。