最近碰到方正客戶提出的一個問題,就是對於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
{
long dim1=SafeArrayGetDim(lstCode.parray);
long dim2=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或其他語言傳遞的數組該接口都可以正確運行