最近碰到方正客户提出的一个问题,就是对于js定义的数组传递给我们的接口(ATL控件),值设置不进去,一直返回false。
经过查找原因,发现控件代码只处理了vb脚本类的数组,而js数组要特殊处理vbscript传进来的是个SafeArray。而javascript的情况就复杂了,javascript中得数组并不是真正意义上的数组,这个“数组”传到COM中被放进一个集合里,参数VARIANT的类型被置为VT_DISPATCH,我们得通过这个IDispatch指针调用invoke 才能得到用来读取集合的枚举接口。也就是说JS中的Array在COM中是一个实现了IDispatch的对象,可通过IDispatch接口api进行操作。
知道原因后就对接口实现做出如下调整:
首先定义两个辅助函数分别用来获取js数组的长度及指定index元素值
/***********************************************
书写人 :zhichao.wang
函数类型:辅助函数
函数名称:lcl_GetJSArrayLength
函数功能:获取Javascript数组中长度
返回值 :
***********************************************/
HRESULTCNsoControl::lcl_GetJSArrayLength(IDispatch* pDisp, int&pLength)
{
BSTR varName = L"length";
VARIANT varValue;
DISPPARAMS noArgs = {NULL, NULL, 0, 0};
DISPID dispId;
HRESULT hr;
hr = pDisp->GetIDsOfNames(IID_NULL,&varName, 1, LOCALE_USER_DEFAULT, &dispId);
if(FAILED(hr))
returnhr;
hr = pDisp->Invoke(dispId, IID_NULL,LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &noArgs, &varValue, NULL,NULL);
if(SUCCEEDED(hr))
{
pLength = varValue.intVal;
returnhr;
}
else
{
returnhr;
}
}
/***********************************************
书写人 :zhichao.wang
函数类型:辅助函数
函数名称:lcl_GetJSArrayLength
函数功能:获取Javascript数组中指定位置的元素值
返回值 :
***********************************************/
HRESULTCNsoControl::lcl_GetJSArrayDataOfIndex(IDispatch* pDisp, int index, VARIANT& pValue)
{
CComVariant varName(index, VT_I4); // 数组下标
DISPPARAMS noArgs = {NULL, NULL, 0, 0};
DISPID dispId;
VARIANT varValue;
HRESULT hr = 0;
varName.ChangeType(VT_BSTR); // 将数组下标转为数字型,以进行GetIDsOfNames
//
// 获取通过下标访问数组的过程,将过程名保存在dispId中
//
hr = pDisp->GetIDsOfNames(IID_NULL,&varName.bstrVal, 1, LOCALE_USER_DEFAULT, &dispId);
if(FAILED(hr))
return hr;
//
// 调用COM过程,访问指定下标数组元素,根据dispId 将元素值保存在varValue中
//
hr = pDisp->Invoke(dispId, IID_NULL,LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET,&noArgs, &varValue, NULL,NULL);
if(SUCCEEDED(hr))
{
pValue = varValue;
return hr;
}
else
{
return hr;
}
}
然后在对应的接口中做如下处理:
STDMETHODIMPCNsoControl::SetCompoundBoxCodeAndValueByArray(BSTR sName,VARIANTlstCode,VARIANT lstValue,int iType,VARIANT_BOOL*pVal)
{
*pVal = VARIANT_FALSE;
HRESULT hr;
**************此处省略若干业务相关代码************************
if( lstCode.vt == VT_DISPATCH &&lstValue.vt == VT_DISPATCH )//处理JS数组
{
//MessageBox(L"BeginToSetNo1");
intiCodeLen,iValueLen;
lcl_GetJSArrayLength(lstCode.pdispVal,iCodeLen);
lcl_GetJSArrayLength(lstValue.pdispVal,iValueLen);
VARIANT vCode,vValue;
for(int i=0;i<iCodeLen;i++ )
{
lcl_GetJSArrayDataOfIndex(lstCode.pdispVal,i,vCode);
lcl_GetJSArrayDataOfIndex(lstValue.pdispVal,i,vValue);
vParam[1] =CComVariant(vCode.bstrVal);
vParam[0] =CComVariant(vValue.bstrVal);
**************此处省略若干业务相关代码************************
}
*pVal = VARIANT_TRUE;
}
else//处理VB数组等
{
if((lstCode.vt^VT_ARRAY) <1 || (lstCode.vt^VT_ARRAY)>73)//用户传入数组类型不正确直接返回
return S_OK;
if((lstValue.vt^VT_ARRAY) <1 || (lstValue.vt^VT_ARRAY)>73)//用户传入数组类型不正确直接返回
return S_OK;
try
{
longdim1=SafeArrayGetDim(lstCode.parray);
longdim2=SafeArrayGetDim(lstValue.parray);
long ubound;
long lbound;
SafeArrayGetUBound(lstCode.parray,dim1,&ubound);
SafeArrayGetLBound(lstCode.parray,dim1,&lbound);
BSTR* buf1,*buf2;
SafeArrayAccessData(lstCode.parray,(void**)&buf1);
SafeArrayAccessData(lstValue.parray,(void**)&buf2);
for(int i=lbound;i<ubound-lbound+1;i++)
{
vParam[1] =CComVariant(buf1[i]);
vParam[0] =CComVariant(buf2[i]);
**************此处省略若干业务相关代码************************
}
SafeArrayUnaccessData(lstCode.parray);// slove the delphiproblem "variant or safe array is locked"
SafeArrayUnaccessData(lstValue.parray);
*pVal =VARIANT_TRUE;
}
catch(...)
{
*pVal =VARIANT_FALSE;
return S_OK;
}
}
returnS_OK;
}
经过修改后,对于js或其他语言传递的数组该接口都可以正确运行