如何构造一个元素类型为Struct的SafeArray:
在有些时候,我们需要构造一个元素类型为Struct的SafeArray,在MSDN并没有文档解释到底应该如何去做到这一点,下面的代码片断解释了如何去构造这样一个SafeArray。
假设我们有如下的Struct:
struct myStruct
{
unsigned char Name[255];
short Kind;
};
为了构造一个SafeArray,元素类型为mxStruct,首先我们必须得拿到mxStruct所对应的IRecordInfo接口,这可以通过调用GetRecordInfoFromGuids函数实现:
#import "TestStruct.tlb" no_namespace
HRESULT hr;
IRecordInfo *pRecordInfo;
hr = GetRecordInfoFromGuids(
__uuidof(TestStruct),
1,
0,
LOCALE_USER_DEFAULT,
__uuidof(mxStruct),
&pRecordInfo);
GetRecordInfoFromGuids在注册表中查询对应的Record信息,这个注册表信息位于HKCR/Record下,同时,对应的TypeLib也必须被注册在HKCR/TypeLib下面,这样GetRecordInfoFromGuids才可以查到对应的信息并返回IRecordInfo*指针。当获得了这个指针的时候,便可以通过CreateSafeArrayVectorEx来构造SafeArray:
SAFEARRAY *pArray = SafeArrayCreateVectorEx(VT_RECORD, 0, 3, pRecordInfo);
该行调用SafeArrayCreateVectorEx构造一个元素为pRecordInfo指定的结构,也就是myStruct的SafeArray,LowBound为0,元素个数为3。
对这个SafeArray中的元素赋值可以通过SafeArrayAccessData和SafeArrayUnaccessData做到:
myStruct *pStructs;
SafeArrayAccessData(pArray, (void **)&pStructs);
strcpy((char *)&pStructs[0].Name[0], "N1");
pStructs[0].Kind = 0;
strcpy((char *)&pStructs[1].Name[0], "N2");
pStructs[0].Kind = 1;
strcpy((char *)&pStructs[2].Name[0], "N3");
pStructs[0].Kind = 2;
SafeArrayUnaccessData(pArray);
SafeArrayAccessData获得数组的指针,用于修改数据,并Lock该SafeArray,防止被SafeArray被释放。而SafeArrayUnaccessData则Unlock这个SafeArray。
SAFEARRAY
SAFEARRAY的主要目的是用于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描述符:
uIsRead=f.Read(bVal,ChunkSize);//read array from a file.
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)))
::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=bVal;
}
::SafeArrayUnaccessData(psa);
varChunk.vt = VT_ARRAY|VT_UI1;
varChunk.parray = psa;
这种方法读写SafeArray都可以,它直接操纵SafeArray的数据缓冲区,比用SafeArrayGetElement和 SafeArrayPutElement速度快。特别适合于读取数据。但用完之后不要忘了调用::SafeArrayUnaccessData (psa),否则会出错的。
如果SafeArray中存的是BSTR的二维数组,则代码如下:
if(varChunk.vt = VT_ARRAY | VT_BSTR)
{
BSTR* buf;
long LBound; // 数组下界
long UBound; // 数组上界
SafeArrayAccessData(varChunk.parray, (void **)&buf);
SafeArrayGetLBound(varChunk.parray, 1, &LBound);
SafeArrayGetUBound(varChunk.parray, 1, &UBound);
for(long i = LBound; i < UBound; i ++)
{
CString str(buf);
MessageBox(str);
}
SafeArrayUnaccessData(varChunk.parray);
}