`
阿尔萨斯
  • 浏览: 4472058 次
社区版块
存档分类
最新评论

SAFEARRAY使用实例

 
阅读更多

SAFEARRAY使用总是困扰很多人,为了把这个问题说个明白,我把我目前掌握的知识做个总结
SAFEARRAY实际上是一个结构,关于这部分可以参考MSDN。
ms-help://MS.MSDNQTR.2003FEB.2052/automat/htm/chap7_9ntx.htm
我们不需要关心16位操作系统下的定义,因为我们团队只在WIN2000以上平台下开发。
方法一:使用SafeArrayAllocDescriptor在栈上创建一维数组
//创建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);
long Low(0),High(0);
hr=SafeArrayGetLBound(pArray,1,&Low);//维数索引从1开始
hr=SafeArrayGetUBound(pArray,1,&High);//维数索引从1开始
SafeArrayUnaccessData(pArray);
SafeArrayDestroy(pArray);
这种方法在栈上分配数组元素所占的空间,即nData数组所用的空间
方法二:使用SafeArrayAllocDescriptorSafeArrayAllocData在堆上创建一维数组
//创建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;
SafeArrayAllocData(pArray);
long* pData=NULL;
SafeArrayAccessData(pArray,(void**)&pData);
long l(0),h(0);
SafeArrayGetLBound(pArray,1,&l);
SafeArrayGetUBound(pArray,1,&h);
long Size=h-l+1;
SafeArrayAccessData(pArray,(void**)&pData);
for(long Idx=l;Idx<Size;++Idx)
{
pData[Idx]=nData[Idx];
}
SafeArrayUnaccessData(pArray);
//访问SAFEARRAY数组
long* pValue=NULL;
SafeArrayAccessData(pArray,(void**)&pValue);
long Low(0),High(0);
hr=SafeArrayGetLBound(pArray,1,&Low);//维数索引从1开始
hr=SafeArrayGetUBound(pArray,1,&High);//维数索引从1开始
SafeArrayUnaccessData(pArray);
SafeArrayDestroy(pArray);
方法三:使用SafeArrayAllocDescriptorSafeArrayAllocData在堆上创建二维数组
SAFEARRAY* pArray=NULL;
HRESULT hr=SafeArrayAllocDescriptor(2,&pArray);
pArray->rgsabound[0].lLbound=0;
pArray->rgsabound[0].cElements=3;
pArray->rgsabound[1].lLbound=0;
pArray->rgsabound[1].cElements=3;
pArray->cbElements=sizeof(long);
hr=SafeArrayAllocData(pArray);
long lDimension[2];
long x=1;
//为第一行赋值
for(long i=0;i<3;++i)
{
lDimension[1]=0;//
lDimension[0]=i;//
SafeArrayPutElement(pArray,lDimension,&x);
x++;
}
//为第二行赋值
for(long i=0;i<3;++i)
{
lDimension[1]=1;//
lDimension[0]=i;//
SafeArrayPutElement(pArray,lDimension,&x);
x++;
}
//读取SafeArray中第二行第三列的数据
long y(0);
lDimension[1]=1;
lDimension[0]=2;
SafeArrayGetElement(pArray,lDimension,&y);
SafeArrayDestroy(pArray);
二维SAFEARRAY数组使用的时候下标要注意,这里采用的是列主序的方式,即lDimension[1]代表行,lDimension[0]代表列。
方法四:使用SafeArrayCreate在堆上创建一维数组
SAFEARRAYBOUND Bound[1];
Bound[0].lLbound=0;
Bound[0].cElements=10;
SAFEARRAY* pArray=SafeArrayCreate(VT_I4,1,Bound);
long* pData=NULL;
HRESULT hr=SafeArrayAccessData(pArray,(void**)&pData);
long Low(0),High(0);
SafeArrayGetLBound(pArray,1,&Low);
SafeArrayGetUBound(pArray,1,&High);
long Size=High-Low+1;
for(long Idx=Low;Idx<Size;++Idx)
{
pData[Idx]=Idx;
cout<<pData[Idx]<<endl;
}
SafeArrayUnaccessData(pArray);
SafeArrayDestroy(pArray);
方法五:使用SafeArrayCreate在堆上创建二维数组
SAFEARRAYBOUND Bound[2];
Bound[0].lLbound=0;
Bound[0].cElements=3;
Bound[1].lLbound=0;
Bound[1].cElements=3;
SAFEARRAY* pArray=SafeArrayCreate(VT_I4,2,Bound);
long Demen[2];
for(long i=0;i<3;++i)
{
for(long j=0;j<3;++j)
{
Demen[1]=i;
Demen[0]=j;
long x=i*j;
SafeArrayPutElement(pArray,Demen,&x);
}
}
//访问二维数组
for(long i=0;i<3;++i)
{
for(long j=0;j<3;++j)
{
Demen[1]=i;
Demen[0]=j;
long x(0);
SafeArrayGetElement(pArray,Demen,&x);
cout<<"("<<i<<","<<j<<")"<<x<<endl;
}
}
SafeArrayDestroy(pArray);
方法六:使用SafeArrayCreateEx创建包含结构的一维数组
使用SAFEARRAY传递UDT(自定义结构)是一项常用的技术,MSDN文档描述得比较齐全,要注意的一点是,自定义结构要求有自己的GUID,这必须在IDL文件中定义。同时还必须要使用IRecordInfo接口,该接口将和数组一起传递出去,IRecordInfo接口内部记录了UDT的描述信息。
IDL文件中:
[uuid(810930AA-9229-46e7-B20C-41F6218D0B1A)]
struct _BookMarkSchema
{
BSTR Name;
BSTR Context;
BSTR Time;
};
interface IShape : IDispatch
{
[id(6), helpstring("获取属于某用户的书签名称列表")] HRESULT GetBookMarkName([in] BSTR UserID,[out] SAFEARRAY(struct _BookMarkSchema)* pBookMarkNames);
}
library SarstShapeLib
{
importlib("stdole2.tlb");
[
uuid(DBDCC0F1-38F3-4EB4-A5BD-79A3707BDE9C),
helpstring("Shape Class")
]
coclass Shape
{
[default] interface IShape;
};
struct _BookMarkSchema;
};
方法的实现为:
STDMETHODIMP CShape::GetBookMarkName(BSTR UserID,SAFEARRAY** pBookMarkNames)
{
//获得GIS库信息
CSarstConfigure Configure;
string Flag("GIS");
string IP,Database,UserName,Key,Context;
Configure.GetDatabaseInfo(Flag,IP,Database,UserName,Key,Context);
//读取图层属性数据
USES_CONVERSION;
string user(CString(UserID).GetBuffer());
string sql("SELECT 书签名,书签描述,时间 FROM 用户书签表 where 用户ID='"+user+"' order by 时间 desc");
FBData data(IP,Database,UserName,Key);
table t=data.GetTable(sql);
if(t.empty())
{
return S_FALSE;
}
//创建SafeArray
IRecordInfo* pRecordInfo=NULL;
HRESULT hr=::GetRecordInfoFromGuids(LIBID_SarstShapeLib,1,0,GetUserDefaultLCID(),IID_STRUCT_BookMarkSchema,&pRecordInfo);
if(FAILED(hr))
return E_FAIL;
*pBookMarkNames=::SafeArrayCreateVectorEx(VT_RECORD,0,long(t.size()-1),(void*)pRecordInfo);
_BookMarkSchema* pData=NULL;
hr=::SafeArrayAccessData(*pBookMarkNames,(void**)&pData);
for(int i=0;i<int(t.size()-1);i++)
{
t[i+1].at(0).CopyTo(&pData[i].Name);
t[i+1].at(1).CopyTo(&pData[i].Context);
t[i+1].at(2).ChangeType(VT_BSTR);
t[i+1].at(2).CopyTo(&pData[i].Time);
}
::SafeArrayUnaccessData(*pBookMarkNames);
pRecordInfo->Release();
return S_OK;
}
访问SAFEARRAY:
这种方法可以参见创建SAFEARRAY之方法一
请注意,访问完后要调用SafeArrayUnaccessData方法,并且调用SafeArrayDestroy销毁数组
这种方式通常用于访问一位数组
这种方法可以参见创建SAFEARRAY之方法五
这种方式在访问多维数组的时候很有用
CComSafeArray类介绍:
可以参见下面的MSDN链接
ms-help://MS.MSDNQTR.2003FEB.2052/vclib/html/vclrfCComSafeArray.htm
注意,我个人认为本例有错,应该最后加上一句代码delete pSar;
因为虽然pvData指针指向的内存是在堆中,但是tagSAFEARRAY结构对象生存在new开辟的堆上,如果不delete的话,将会内存泄漏。
1)SetAt方法有问题
HRESULT SetAt(LONG lIndex, const T& t, BOOL bCopy = TRUE)
{
bCopy;
ATLASSERT(m_psa != NULL);
if(m_psa == NULL)
return E_FAIL;
LONG lLBound = GetLowerBound();
ATLASSERT(lIndex >= lLBound);
ATLASSERT(lIndex <= GetUpperBound());
if ((lIndex < lLBound) || (lIndex > GetUpperBound()))
return E_INVALIDARG;
((T*)m_psa->pvData)[lIndex-lLBound] = t;
return S_OK;
我们可以看到,MSDN中描述的bCopy如果为true将创建数据的副本,在代码中并没有实现,事实上bCopy参数并没有起到任何作用,不知道怎么会出现这样的错误。因此,如果我们在添加BSTR或者VARIANT类型的元素时必须先复制一份副本,然后传递给Add方法(Add方法内部调用Set方法)。
比如:m_sa.Add(CComVariant(bstr));
看到这里我还有一个奇怪的地方
((T*)m_psa->pvData)[lIndex-lLBound] = t;并没有使用SafeArrayAccessData方法获取指针,实际上SafeArrayAccessData和直接使用m_psa->pvData方法的区别在于前者要增加引用计数,而后者不会增加。因为这里如果使用SafeArrayAccessData的话,必然也要使用SafeArrayUnaccessData方法,为了效率这里省略。
分享到:
评论

相关推荐

    Com_SafeArray 使用实例

    标题"Com_SafeArray 使用实例"暗示了我们将探讨的是COM(Component Object Model)技术中的一种特殊数据结构——SafeArray。SafeArray是COM中用于安全传输和处理数组的一种机制,尤其适用于跨进程通信。描述指出,这...

    Variant输入出参数,ocx传递byte数组,safearray使用方法,vc写ocx在vb下调用

    - VB 接收和处理:在VB中,使用 `CreateObject` 创建 OCX 实例,调用方法接收 SafeArray。可以使用 `SafeArrayGetElement` 和 `SafeArrayUnlock` 来访问和解锁数组。 4. VC 写 OCX 在 VB 下调用: - 编译与注册 ...

    SAFEARRAY代码实例

    这个例子展示了如何创建一个包含BSTR(Unicode字符串)的`SAFEARRAY`,并将字符串写入和读出数组。 ### 在实际编程中的应用 `SAFEARRAY`广泛应用于各种Windows API和COM接口中,特别是在自动化、数据交换和控件编程...

    _SAFEARRAY_User_SafeArray_

    例如,可能会包含创建一个`SAFEARRAY`实例,填充数据,然后读取和显示数据的函数。代码可能还涉及到错误处理,确保在出现异常时正确清理资源。 在实际应用中,`SAFEARRAY`常用于自动化对象(Automation Objects)的...

    SafeArray实现对Excel的导入\导出例子源代码

    使用SafeArray对Excel导入/导出(区域读写数据),比使用单元格循环读写速度快上一百倍以上,经过测试整个工作表65526*10的数据区域,导入时间与导出时间就35秒钟左右,速度上与读写普通的文本文件有得比,基本能让人满意....

    Using a Multidimensional SAFEARRAY to pass data across from

    Using a Multidimensional SAFEARRAY to pass data across from COM objects这个例子使用多维的SAFEARRAY结构收集COM对象(5KB)

    java通过jacob操作word例子

    下面我们将详细探讨如何使用Java和jacob库来处理Word文档。 首先,`MsWordUtils.java`可能是一个包含具体操作Word文档方法的工具类,例如打开文档、读取内容、修改内容、保存文档等。这些方法通常会利用jacob库提供...

    ATLVC源代码:safearray-1

    客户端可能展示了如何在C++中创建SafeArray实例,填充数据,以及如何通过接口调用来接收和解包来自服务器的SafeArray。 2. `COMDynamicData`:这可能是服务器端组件,它定义了一种或多种服务,可能会返回一个或多个...

    使用CArchive下载和存储对象.pdf

    文档中的例子演示了如何使用SAFEARRAY来存储和传递数据。 5. CBlob类的使用: CBlob类用于封装二进制大对象(Blob),可能是一个内存中的数据缓冲区。文档提到了如何使用CBlob类来封装对象数据并将其序列化到内存...

    Java操作ddl使用jacob

    4. **执行DDL语句**:通过`Command`对象,你可以创建一个`ADODB.Command`实例,并设置其`ActiveConnection`属性为已打开的`Connection`。然后,你可以设置`CommandText`属性为DDL语句,并调用`Execute`方法执行。 `...

    标准MFC WinSock ActiveX控件开发实例(II)高级篇

    本实例深入探讨如何结合MFC和WinSock构建一个ActiveX控件,特别关注使用VARIANT类型进行网络数据传输和接收,以及如何有效利用SAFEARRAY和BSTR这两种数据结构。以下将详细介绍这些关键概念和实践技巧。 **1. ...

    如何存储二进制数据方法

    SafeArray并不单独使用,而是作为VARIANT类型的一部分进行封装。VARIANT是一种通用的数据结构,能够容纳多种数据类型,包括其他VARIANT实例。当VARIANT的`vt`成员的值包含`VT_ARRAY`标志时,意味着这个VARIANT变量...

    标准MFC WinSock ActiveX控件开发实例

    文中涉及到VARIANT,SAFEARRAY,BSTR的详细使用方法。 另外还提供了WinSock的详细开发步骤,以及如何响应网络超时,网络断开的事件方法以及在VC,VB调用该控件的方法。 一、MFC ActiveX控件开发步骤(VC 6.0): New-&gt;...

    JavaScript中的ubound函数使用实例

    JavaScript中ubound函数方法是返回在 VBArray 的指定维中所使用的最大索引值。使用方法: 代码如下: safeArray.ubound(dimension) 其中safeArray是必选项。是一个 VBArray 对象。 dimension是可选项。要获知其索引...

    MFC应用程序调用控件中返回值之结构体和数组(控件由C++写成

    在这个例子中,我们使用`SAFEARRAY`来存储和传递数组。首先,创建一个`SAFEARRAY`对象,并设置其类型和边界。接着,分配内存给`SAFEARRAY`,然后将数据复制到这个内存区域。最后,设置`VARIANT`类型的输出参数`msg`...

    VC串口上位机编程实例.pdf

    * 使用COleSafeArray类型变量safearray,用于存放接收到的数据。 * 使用LONG类型变量len、k,用于存放数据的长度。 * 使用BYTE类型数组rxdata[2048],用于存放接收到的数据。 * 使用CString类型变量stremp1、...

    标准MFC WinSock ActiveX控件开发实例.rar

    本文主要介绍如何开发一个ActiveX控件,提供...文中涉及到VARIANT,SAFEARRAY,BSTR的详细使用方法。 另外还提供了WinSock的详细开发步骤,以及如何响应网络超时,网络断开的事件方法以及在VC,VB调用该控件的方法。

    VC 创建MFC WinSock ActiveX控件的方法 实例.rar

    文中涉及到VARIANT,SAFEARRAY,BSTR的详细使用方法。另外还提供了WinSock的详细开发步骤,以及如何响应网络超时,网络断开的事件方法以及在VC,VB调用该控件的方法。  关键字:ActiveX,Socket,VARIANT, ...

    VC++串口实例

    ### VC++串口实例知识点详解 #### 一、项目创建与环境配置 1. **创建MFC应用程序**: - 打开Visual C++ 6.0开发环境。 - 创建一个基于对话框的MFC应用程序,命名为`SCommTest`。 2. **插入MSComm控件**: - 在...

Global site tag (gtag.js) - Google Analytics