由于项目开发需要,原有的程序用C++开发,但是现有用C#开发,所以需要对用C++开发的接口利用COM进行封装,供c#语言调用,在此处用到一个字节数组,所以此处对其进行封装。
(一)用到的COM接口:
1.1 SAFEARRAY的创建
SAFEARRAY* SafeArrayCreate(
VARTYPE vt,
unsigned int cDims,
SAFEARRRAYBOUND * rgsabound
);
VARTYPE 数组数据的类型,此处为:VT_UI1表示1个字节的BYTE类型。具体类型如下:
VT_UI1 无符号1字节整数(BYTE)数组
VT_UI2 无符号2字节整数(WORD)数组
VT_UI4 无符号4字节整数(DWORD)数组
VT_UINT 无符号整数(UINT)数组
VT_INT 有符号整数(INT)数组
VT_I1 有符号1字节整数数组
VT_I2 有符号2字节整数数组
VT_I4 有符号4字节整数数组
VT_R4 IEEE 4字节浮点数(float)数组
VT_R8 IEEE 8字节浮点数(double)数组
VT_CY 8字节定点数货币值数组
VT_BSTR VB字符串数组
VT_DECIMAL 12字节定点数(大数字)数组
VT_ERROR 标准错误编号数组
VT_BOOL 布尔值数组
VT_DATE 日期型数组
VT_VARIANT VB Variant类型数组
cDims 表示数组维数,此处赋值为1,表示一维数组
SAFEARRAYBOUND* rgsabound:Pointer to a vector of bounds (one for each dimension) to allocate for the array.
存放的是一组数据,每一组代表一维中数据的个数以及下标下界。
typedef struct tagSAFEARRAYBOUND
{
unsigned long cElements; //该维元素个数
unsigned long lLbound; //该未下标的下界
} SAFEARRAYBOUND;
1.2. 数组数据的访问:
HRESULT SafeArrayAccessData(
SAFEARRAY * psa,
void HUGEP ** ppvData
);
SAFEARRAY* psa [in] SAFEARRAY指针
ppvData [out],数组指针,若获取成功,可以用下标方式访问数组中的数据, (*ppvData)[i];
1.3. 对数组访问的释放:对访问上锁数量减一,并使指针无效
HRESULT SafeArrayUnaccessData(
SAFEARRAY FAR* psa
);
Parameters
-
psa
- [in, out] psa是由 SafeArrayCreate产生的。
Return Values
操作结果
E_UNEXPECTED 数组不能被解锁
S_OK 操作成功 | |
E_INVALIDARG 不合法的数针 |
(二).BYTE类型的数组类接口定义以及实现
[
object,
uuid(2D33B425-58DE-4D4C-BAC2-B39502E0644F),
dual,
nonextensible,
helpstring("IComByteArray 接口"),
pointer_default(unique)
]
interface IComByteArray : IDispatch{
[id(1), helpstring("方法CreateArray")]
HRESULT CreateArray([in] UINT nArraySize, [out,retval] VARIANT_BOOL* retVal);
[id(2), helpstring("方法PutItem")]
HRESULT PutItem([in]UINT itemIndex ,[in] BYTE content, [out,retval] VARIANT_BOOL* retVal);
[id(3), helpstring("方法GetItem")]
HRESULT GetItem([in]UINT itemIndex, [out,retval] BYTE* retVal);
};
IDL文件中定义了如下接口:数组的创建、数据的put和get操作
.h文件定义如下:
/***********************************************************************
文件名: ComByteArray.h
类名: CComByteArray
作者:
定义时间:2012-4-27
功能:字节数组COM封装
************************************************************************/
#pragma once
#include "resource.h" // 主符号
#include "COMM_COM_Client.h"
#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
#error "Windows CE 平台(如不提供完全 DCOM 支持的 Windows Mobile 平台)上无法正确支持单线程 COM 对象。定义 _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA 可强制 ATL 支持创建单线程 COM 对象实现并允许使用其单线程 COM 对象实现。rgs 文件中的线程模型已被设置为“Free”,原因是该模型是非 DCOM Windows CE 平台支持的唯一线程模型。"
#endif
class ATL_NO_VTABLE CComByteArray :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CComByteArray, &CLSID_ComByteArray>,
public IDispatchImpl<IComByteArray, &IID_IComByteArray, &LIBID_COMM_COM_ClientLib,
{
public:
CComByteArray();
~CComByteArray();
DECLARE_REGISTRY_RESOURCEID(IDR_COMBYTEARRAY)
BEGIN_COM_MAP(CComByteArray)
COM_INTERFACE_ENTRY(IComByteArray)
COM_INTERFACE_ENTRY(IDispatch)
END_COM_MAP()
DECLARE_PROTECT_FINAL_CONSTRUCT()
HRESULT FinalConstruct()
{
return S_OK;
}
void FinalRelease()
{
}
public:
private:
UINT m_nSize; //数组大小
SAFEARRAY* m_pSafeArray; //SAFEARRAY指针
VARIANT_BOOL m_bAutoDelete;//是否自动删除
BYTE HUGEP* m_pContent; //数组数据内存首指针
STDMETHOD(CreateArray)(UINT nArraySize, VARIANT_BOOL* retVal);
STDMETHOD(PutItem)(UINT itemIndex, BYTE content, VARIANT_BOOL* retVal);
STDMETHOD(GetItem)(UINT itemIndex, BYTE* retVal);
};
OBJECT_ENTRY_AUTO(__uuidof(ComByteArray), CComByteArray)
c.pp文件主要说明定义的三个接口:
1. 数组的创建:
/************************************************************************
函数名:Create
功能:创建指定大小的数组内容
参数:[in]nArraySize 数组大小
[out]retVal 操作结果
/************************************************************************/
STDMETHODIMP CComByteArray::CreateArray(UINT nArraySize, VARIANT_BOOL* retVal)
{
// TODO: 在此添加实现代码
HRESULT hr;
if (nArraySize < 0)
{
*retVal = VARIANT_FALSE;
return E_INVALIDARG;
}
if (0 == nArraySize)
{
*retVal = VARIANT_TRUE;
return S_OK;
}
SAFEARRAYBOUND rgsabound[1]; //定义数组维度信息并创建数组
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = nArraySize;
m_nSize = nArraySize;
m_pSafeArray = SafeArrayCreate(VT_UI1, 1, rgsabound);
if (NULL == m_pSafeArray)
{
hr = ResultFromScode(E_OUTOFMEMORY);
return hr;
}
hr = ::SafeArrayAccessData(m_pSafeArray, (void**)&m_pContent); //获取数据数据内存首指针
if (FAILED(hr)) return hr;
return S_OK;
}
数据的插入:
/************************************************************************
函数名:PutItem
功能:向指定下标的数组中存放内容
参数:[in]itemIndex 数组下标
[in]content 内容
[out]retVal 操作结果
/************************************************************************/
STDMETHODIMP CComByteArray::PutItem(UINT itemIndex, BYTE content, VARIANT_BOOL* retVal)
{
// TODO: 在此添加实现代码
if (m_nSize <= itemIndex || itemIndex < 0)
{
*retVal = VARIANT_FALSE;
return E_INVALIDARG;
}
m_pContent[itemIndex] = content;
*retVal = VARIANT_TRUE;
return S_OK;
}
数据的获取:
/************************************************************************
函数名:GetItem
功能:获取指定下标的数组内容
参数:[in]itemIndex 数组下标
[out]retVal 输出数据位置指针
/************************************************************************/
STDMETHODIMP CComByteArray::GetItem(UINT itemIndex, BYTE* retVal)
{
// TODO: 在此添加实现代码
if (m_nSize <= itemIndex || itemIndex < 0)
{
*retVal = VARIANT_FALSE;
return E_INVALIDARG;
}
*retVal = m_pContent[itemIndex];
return S_OK;
}
(三)测试实例
先用C++语言调用COM进行测试,然后再用C#调用测试
原因:用C++测试时可以跟到COM内部,但是C#是不可以的。
3.1 c#测试代码:
using System;
using System.Collections.Generic;
using System.Text;
using COMM_COM_ClientLib;
namespace COMMClientTest
{
class Program
{
static void Main(string[] args)
{
IComByteArray comByteArray = new ComByteArray(); //COM对象创建
comByteArray.CreateArray((uint)100);
for (uint i = 0; i < 5; i++) //放入数据
{
bool bRet = comByteArray.PutItem(i, (Byte)i);
if (bRet) Console.WriteLine("Put Item Succ");
else Console.Write("Put Item Fail");
}
Console.WriteLine("-----------------------------------");
for (uint i = 0; i < 5; i++) //读取放入的数据
{
Byte value;
value = comByteArray.GetItem(i);
Console.WriteLine(value);
}
Console.ReadLine();
}
}
}
测试结果:
![](http://static.oschina.net/uploads/space/2012/0427/205723_L7xk_100374.gif)
3.2 C++测试:
//COM byteArray测试
void comTest()
{
CoInitialize(NULL);
{
IComByteArrayPtr ptr = NULL; //COM对象创建
ptr.CreateInstance(__uuidof(ComByteArray));
if (!ptr)
{
cout<<"Com Object Init Failed"<<endl;
return ;
}
ptr->CreateArray(UINT(100)); //创建并放入数据
for (UINT i = 0; i < 5; i++)
{
ptr->PutItem(i, (BYTE)i);
}
for (UINT i = 0; i < 5; i++) //读取数据
{
BYTE value;
value = ptr->GetItem(i);
cout<<(int)value<<endl;
}
}
CoUninitialize();
}
测试结果:
![](http://static.oschina.net/uploads/space/2012/0427/210027_13x1_100374.gif)