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结构定义该数组结构的细节。以下就是该结构成员的简要描述:
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,或者使用一个不同的开始。该数组的详细情况如下所示:
关于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(),该功能释放该参数的计数。