SegeX SgxVariantArrayT:VC封装支持多维数组的变体类型(VRIANT 、SafeArray)(附免费免积分源代码)

----哆啦刘小洋 原创,转载需说明出处 2023-01-04

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();
}

如果你觉得有帮助,别忘了帮我点个关注或点个赞!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值