----哆啦刘小洋 原创,转载需说明出处 2023-01-04
SgxVariantArrayT:支持多维数组的变体类型
1 简介
VRIANT变体类型是Com技术中的一种标准数据类型,Vb中的变体也是这种类型,在针对Com编程过程中,比如AutoCAD、Surfer、Excel、Word中使用自动化Automation(OLE)功能时,经常遇到VARIANT数组的情况,在VC中使用VARIANT数组很繁琐,特别是多维数组。本文介绍了一种封装VARIANT数组的方法并附带了完整的免费代码,具备以下主要功能:1)最多4维数组的支持;2)使用VARIANT数组类似于CArray、vector,也类似与Vb中的动态数组;3)支持CArray、vector与VARIANT的转换(1~4维)。本文的代码基于VC2012及以上(将代码中的">>"中间留个空格,也可以用于vc6)。
2 方法原理
其实没有什么原理,就是把VC中繁琐的SafeArray申明、赋值、设置都封装到一个VAIRANT继承类中。采用模版类,是为了读取和写入数组数据书写更方便。主要关键点就两个:
2.1 继承于_variant_t
如下:
template <class T>
class CSgxVariantArrayT : public _variant_t
{
//没有成员变量,保持和VARIANT最大的兼容。
};
2.2 模板类型到VARIANT VT的转换
VARIANT是一种标准数据格式,因此只能对应有限的常用数据类型。常用的VT类型和c语言类型对照关系如下:
VT_R8 -> double
VT_R4 -> float
VT_I4 / VT_INT -> int / long
VT_BSTR -> BSTR
VT_I1 -> char
VT_I2 -> short
因此在构造时根据模板参数来获取对应的vt值,代码如下:
template <class T>
class CSgxVariantArrayT : public _variant_t
{
//...
void CalcVT()
{
if (typeid(T) == typeid(double))
vt = VT_R8;
else if (typeid(T) == typeid(int))
vt = VT_I4;
else if (typeid(T) == typeid(UINT))
vt = VT_UI4;
else if (typeid(T) == typeid(float))
vt = VT_R4;
else if (typeid(T) == typeid(char))
vt = VT_I1;
else if (typeid(T) == typeid(UCHAR))
vt = VT_UI1;
else if (typeid(T) == typeid(BOOL))
vt = VT_I4;
else if (typeid(T) == typeid(short))
vt = VT_I2;
else if (typeid(T) == typeid(unsigned short))
vt = VT_UI2;
else if (typeid(T) == typeid(bool))
vt = VT_BOOL;
else if (typeid(T) == typeid(BSTR))
vt = VT_BSTR;
else if (typeid(T) == typeid(__int64))
vt = VT_I8;
else
{
ASSERT(FALSE);
throw ("data type not supported");
}
vt |= VT_ARRAY;
}
};
3 主要方法(接口)
template <class T>
class CSgxVariantArrayT : public _variant_t
{
public:
//构造
CSgxVariantArrayT(void);
CSgxVariantArrayT(ULONG nCount, T* pData = NULL);
CSgxVariantArrayT(const VARIANT& v);
//获取某一维的元素个数
long GetSize(long nDim);
//设置数组大小
BOOL Redim(long n1);
BOOL Redim(long n1, long n2);
BOOL Redim(long n1, long n2, long n3);
BOOL Redim(long n1, long n2, long n3, long n4);
//获取数组元素(读写)
T& operator()(long n1);
T& operator()(long n1, long n2);
T& operator()(long n1, long n2, long n3);
T& operator()(long n1, long n2, long n3, long n4);
//写数组元素(另一种写方案,用于数组转换)
HRESULT SetAt(long nIndex, variant_t v);
HRESULT SetAt(long n1, long n2, variant_t v);
HRESULT SetAt(long n1, long n2, long n3, variant_t v);
HRESULT SetAt(long n1, long n2, long n3, long n4, variant_t v);
};
最多支持到4维数组。实现都是利用Windows系统函数SafeArrayRedim、SafeArrayCreate、SafeArrayDestroy等,一句一句把他们封装起来。另外,采用了重载()的方式,很遗憾,多维数组无法采用重载[]的方式提供类似 arr[][][] 读取数据的方法。
想了解具体实现,请参见文末附带的源代码。
使用方法的示例代码如下:
#include "SgxVariantArrayT.h"
void test()
{
CSgxVariantArrayT<double> darr(5); //申明一个变体数组,初始化5个元素
darr(0) = 1.0;
darr(4) = 5.0;//读取索引[4]位置的数据
darr.Redim(2, 3);//变成二维数组
darr(0, 0) = 1.0;
darr(0, 1) = 2.0;//读取索引[0][1]位置的数据
double d = darr(0, 1);
}
特别的,VARIANT支持的字符串为BSTR类型,因此使用时要注意:
#include "SgxVariantArrayT.h"
#include <afxtempl.h> //for CString
#include <string> //for std::string
void test_str()
{
CSgxVariantArrayT<BSTR> sarr(5); //必须为BSTR
sarr(0) = bstr_t("xx").copy();//注意这里要用.copy()得到一个新的bstr_t实例,否则临时对象会共用一个实例,导致数组赋值后只能得到一个相同的值
CString s("yy"), s1;
sarr(4) = bstr_t(s).copy();//用CString 赋值
s1 = sarr(0);//赋值给CString
std::string ss("ss"), ss1;
sarr(2) = bstr_t(ss.c_str()).copy();//用std::string 赋值
ss1 = bstr_t(sarr(2));//赋值给std::string
}
4 与CArray、vector数组的转换
支持MFC CArray、std::vector数组以及一维指针数组。
# define CArray1T CArray<T, T>
# define CArray2T CArray<CArray<T, T>>
# define vector1T std::vector<T>
# define vector2T std::vector<std::vector<T>>
//...
template <class T>
class CSgxVariantArrayT : public _variant_t
{
//一维指针数组互换
BOOL From(T* arr, int n1);
BOOL ToArray(T* arr, int n1);
//MFC CArray数组互换
BOOL From(CArray1T& arr);
BOOL From(CArray2T& arr);
...
BOOL ToArray(CArray1T& arr);
BOOL ToArray(CArray2T& arr);
...
//std::vector数组互换
BOOL From(vector1T& arr);
BOOL From(vector2T& arr);
...
BOOL ToArray(vector1T& arr);
BOOL ToArray(vector2T& arr);
};
对CString std::string数组的转化必须要做特殊处理。略过。感兴趣可查看源代码FromStringArray,ToStringArray函数。
示例代码:
#include "SgxVariantArrayT.h"
void test_SgxVariantArrayT()
{
long i, j, k, m, nn;
//test with CArray
{
//一维
CSgxVariantArrayT<double> vd;
CArray<double, double> ad;
ad.SetSize(6);
for (i = 0; i < 6; ++i)
ad[i] = i;
vd.From(ad);
vd.ToArray(ad);
//二维
nn = 0;
CArray2(double) ad2;
CSgxVariantArrayT<double>::SetArraySize(ad2, 2, 3);
for (j = 0; j < 3; ++j)
for (i = 0; i < 2; ++i)
ad2[i][j] = nn++;
vd.From(ad2);
vd.ToArray(ad2);
}
//test with CStringArray
{
CStringArray as;
CSgxVariantArrayT<BSTR> sv;
as.SetSize(6);
for (i = 0; i < 6; ++i)
as[i].Format(_T("%d"), i * 10);
sv.FromStringArray(as);
sv(0) = bstr_t(as[1]).copy();//important with copy(), or sv(0) = sv(1)
sv(1) = bstr_t(as[0]).copy();
sv.ToStringArray(as);
}
//test with vector
{
//一维
CSgxVariantArrayT<double> vd;
vector1(double) ad;
ad.resize(6);
for (i = 0; i < 6; ++i)
ad[i] = i;
vd.From(ad);
vd.ToArray(ad);
//二维
nn = 0;
vector2(double) ad2;
CSgxVariantArrayT<double>::SetArraySize(ad2, 2, 3);
for (j = 0; j < 3; ++j)
for (i = 0; i < 2; ++i)
ad2[i][j] = nn++;
vd.From(ad2);
vd.ToArray(ad2);
}
//test with std::string Array
{
std::vector<std::string> as;
char* cs[] = { "a", "b" , "c" , "d" , "e" , "f" };
CSgxVariantArrayT<BSTR> sv;
as.resize(6);
for (i = 0; i < 6; ++i)
as[i] = cs[i];
sv.FromStringArray(as);
sv(0) = bstr_t("xxx").copy();//important with copy(), or sv(0) = sv(1)
sv(1) = bstr_t("yyy").copy();
sv.ToStringArray(as);
}
}
5 Automation中的应用实例
比如VC调用AutoCAD自动化:
#import "acax20chs.tlb" no_namespace named_guids
#include "SgxVariantArrayT.h"
void OnTest_Interface_vc2022()
{
CoInitialize(NULL);
IAcadApplicationPtr pApp;
IAcadDocumentsPtr pDocs;
IAcadDocumentPtr pDoc;
IAcadModelSpacePtr pMs;
HRESULT h = NOERROR;
CLSID clsid;
//获取Class ID
h = ::CLSIDFromProgID(OLESTR("AutoCad.Application"), &clsid);
if (FAILED(h))
return;
//获取正在运行的对象
h = pApp.GetActiveObject(clsid);
if (!SUCCEEDED(h))
{
//没有正在运行的对象,就创建一个
h = pApp.CreateInstance(clsid, NULL, CLSCTX_ALL);
if (FAILED(h))
return;
}
pApp->WindowState = AcWindowState::acMax;
pApp->Visible = VARIANT_TRUE;
//新建一个AutoCAD文档
pApp->get_Documents(&pDocs);
pDoc = pDocs->Add();
//模型空间
pDoc->get_ModelSpace(&pMs);
//添加一条多义线
CSgxVariantArrayT<double> arr(6);
arr(0) = 0.0; arr(1) = 0.0; arr(2) = 0.0;
arr(3) = 10.0; arr(4) = 10.0; arr(5) = 0.0;
pMs->AddPolyline(arr);
pApp->ZoomAll();
}
下载源代码。(免费、免积分)。源代码测试请调用静态函数 CSgxVariantArrayT::test()。
#include "SgxVariantArrayT.h"
void test()
{
CSgxVariantArrayT<int>::test();
}
如果你觉得有帮助,别忘了帮我点个关注或点个赞!