COM明明是一种编程思想,是一门绝世武功。可时代忽然迈入火器,无数人抛弃掉这晦涩难懂的绝世武功,转身投入速成的技艺时代。
“很多不懂 com 的C++程序员会自行发明一个 com 的子集,而且是蹩脚的多的子集。可惜的是,刀与剑的时代结束了,火器时代,掌握 com 这类武功已经没用了。”
---知乎网友
关于使用COM接口返回数组
COM处理了未知语言在COM客户之间传递数组,没错。但是这个并不是什么GO之流,而是C#、VB、Delphi、asp之流甚至于py等。它模糊了语言的边界,那模糊边界自然有一个重要的前提,那就是简单易用,否则都是扯犊子。
而Com所支持的类型其中一种十分重要的即是VARIANT ,而其中有一个很重要的类型即是SAFEARRAY 这便是安全数组类型,该类型其实就是对数组进行了一些描述如起始,边界等,但是该类型又可以修饰VARIANT 为元素的数组,简直是互相套娃无穷尽也。
去百度一搜,好家伙。一来就是SAFEARRAY 和 SAFEARRAYBOUND 。好家伙,这TM,你管这叫简单易用?我真滴服了,而且里面的内容弯弯绕绕,晦涩难懂,这些人写的都是什么博客,互相抄互相爬,感觉像在垃圾堆里找吃的。
COM传递数组涉及结构
SAFEARRAYBOUND:
指示数组维数,简单来说,一维、2维甚至多维数组。因此在传递多维数组时需要一个结构描述其维度。
结构:
typedef struct tagSAFEARRAYBOUND
{
ULONG cElements;//表示本维元素的数目
LONG lLbound;//数组开始的逻辑序号,实际访问需减去这个序号
} SAFEARRAYBOUND;
typedef struct tagSAFEARRAYBOUND *LPSAFEARRAYBOUND;
SAFEARRAY:
用来实际指示数组(因为这玩意不是简简单单的指针,用指示一词),涉及的数组的操作函数的参数主体就是它
结构:
typedef struct tagSAFEARRAY
{
USHORT cDims;//数组的大小
USHORT fFeatures;//数组元素类型标记值
ULONG cbElements;//数组元素的大小
ULONG cLocks;//被锁定次数
PVOID pvData;//数据指针
SAFEARRAYBOUND rgsabound[ 1 ];//数组维数描述,如果多维数组,则本数组依次为低维到高维
} SAFEARRAY;
typedef /* [wire_marshal] */ SAFEARRAY *LPSAFEARRAY;
主要函数:
//为安全数组描述符分配内存。
HRESULT SafeArrayAllocDescriptor(
[in] UINT cDims,
[out] SAFEARRAY **ppsaOut
);
//根据SafeArrayAllocDescriptor创建的描述符为安全数组分配内存。
HRESULT SafeArrayAllocData(
[in] SAFEARRAY *psa
);
//增加数组的锁计数,并检索指向数组数据的指针。
HRESULT SafeArrayAccessData(
SAFEARRAY FAR* psa,
void HUGEP* FAR* ppvData
);
//递减数组的锁定计数,并使SafeArrayAccessData检索到的指针无效。
HRESULT SafeArrayUnaccessData(
[in] SAFEARRAY *psa
);
//获取指定安全数组的任何维度的下边界
HRESULT SafeArrayGetLBound(
[in] SAFEARRAY *psa,
[in] UINT nDim,
[out] LONG *plLbound
);
//获取指定安全数组的任何维度的上限。
HRESULT SafeArrayGetUBound(
[in] SAFEARRAY *psa,
[in] UINT nDim,
[out] LONG *plUbound
);
//创建一个新的数组描述符,为数组分配和初始化数据,并返回一个指向新数组描述符的指针。
SAFEARRAY * SafeArrayCreate(
[in] VARTYPE vt,
[in] UINT cDims,
[in] SAFEARRAYBOUND *rgsabound
);
//将数据元素存储在数组中的指定位置。
HRESULT SafeArrayPutElement(
[in] SAFEARRAY *psa,
[in] LONG *rgIndices,
[in] void *pv
);
//检索数组的单个元素。
HRESULT SafeArrayGetElement(
[in] SAFEARRAY *psa,
[in] LONG *rgIndices,
[out] void *pv
);
用法可参考:https://blog.csdn.net/weikangc/article/details/45745551
注意事项:
1、在堆上创建数组
2、一方创建、另一方回收
3、接收方不能修改数组,只能读取和销毁
上面说的,全是垃圾
按上面这么用,初始化一个数组填值都得写几十行。我只能称之为“垃圾”
而百度去搜索的,大多数都是教你这么用,我简直了
解决办法
CComSafeArray 类和 CComSafeArrayBound 类。
天不生这C++,万古图灵如长夜。
这两个类是SAFEARRAY和SAFEARRAYBOUND 结构的装饰模板类,用c++的类可以十分方便的去完成数组的创建,描述,设值,传出。
上代码:
一维数组创建:
CComSafeArray<long> csa(10); //创建一个10个元素的以0为起始下标的一维数组
CComSafeArray<long> csa(10,0);//创建一个10个元素的以0为起始下标的一维数组
CComSafeArray<long> csa(10,1);//创建一个10个元素的以1为起始下标的一维数组
多维数组创建:
//构建一个[2][3][4]数组
//维度描述
CComSafeArrayBound b1(2,0);
CComSafeArrayBound b2(3,0);
CComSafeArrayBound b3(4,0);
//设值描述数组
CComSafeArrayBound rgBounds[]={b1,b2,b3};
//创建多维数组
CComSafeArray<int> sa(rgBounds, 3);
数组设值和取值:
//GetAt 和 SetAt
CComSafeArray<long> sa(10);
sa.SetAt(1,100);//将第2个元素设置为100
long value=sa.GetAt(2); //获得第3个元素的值,放入到value中。
获得外部数组值:
void SetArray(SAFEARRAY * psa)
{
CComSafeArray<long> sa;
sa.Attach(psa);
//操作
.....
sa.Detach();
}
传出数组:
void GetArray(SAFEARRAY ** ppsa)
{
CComSafeArray <long> sa(10);
//...
//设值数组的值
*ppsa=sa.Detach();
}
多维数组使用实例
//创建一个 【3】【4】的2维数组
CComSafeArrayBound b1[3];
CComSafeArrayBound b2[4];
CComSafeArrayBound rg[]={b1,b2};
CComSafeArray<long> sa(rg,2);
//获得【2】【1】元素的值
int rgIndexElementA[]={1,2};
long lVal
sa.MultiDimGetAt(rgIndexElementA,lVal);
//设置 【0】【1】元素的值为100
int rgIndexElementB[]={1,0};
long newVal=100;
sa.MultiDimSetAt(rgIndexElementB,newVal);
operator []操作符:(一维可用)
//模板自然是美滋滋重载了[]
CComSafeArray <int> sa(5);
sa[2]=100;