首页 > 其他分享 >SAFEARRAY 使用转载

SAFEARRAY 使用转载

时间:2022-12-13 11:34:59浏览次数:43  
标签:SAFEARRAY hr pArray long 数组 使用 rgsabound 转载

SAFEARRAY实际上是一个结构,关于这部分可以参考MSDN。

typedef struct tagSAFEARRAY {
  USHORT         cDims;
  USHORT         fFeatures;
  ULONG          cbElements;
  ULONG          cLocks;
  PVOID          pvData;
  SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY;

创建SAFEARRAY
方法一:使用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数组所用的空间

方法二:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上创建一维数组
    // 创建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);
    int nLen = h - l + 1;
    for (int i = 0; i < nLen; ++i)
    {
        pData[i] = nData[i];
    }
    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开始
    CString str = _T("[");
    for (int i = 0; i < High - Low + 1; ++i)
    {
        long value = pValue[i];
        str += (" " + std::to_string(value)).c_str();
    }
    str += _T("]");
    SafeArrayUnaccessData(pArray);

    SafeArrayDestroy(pArray);

方法三:使用SafeArrayAllocDescriptor和SafeArrayAllocData在堆上创建二维数组
 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
方法一:使用SafeArrayAccessData方法
这种方法可以参见 创建SAFEARRAY之方法一
请注意,访问完后要调用SafeArrayUnaccessData方法,并且调用SafeArrayDestroy销毁数组
这种方式通常用于访问一位数组

方法二:使用SafeArrayGetElement和SafeArrayPutElement
这种方法可以参见 创建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方法,为了效率这里省略。
————————————————
版权声明:本文为CSDN博主「htj10」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/htj10/article/details/110441227

 

标签:SAFEARRAY,hr,pArray,long,数组,使用,rgsabound,转载
From: https://www.cnblogs.com/nanfei/p/16978105.html

相关文章