C++ ActiveX开发的问题讨论
最近在一個項目中需要開發一個ocx插件,在開發過程中發現了一些問題,所以在此記錄一下。
我想討論的主要是函數的參數問題,我分別使用c++,JavaScript,C#對ocx插件做了測試,發現不同的參數類型在這幾種語言中表現的差異很大。
(1)首先ocx我是使用C++開發的,所以在同樣使用c++語言開發的程序中調用,所有的參數類型都沒有問題。
(2)所有指針類型如:long *,float *,BSTR*作為參數時,在JavaScript中調用都會失敗,可能在JavaScript中就不能支持這種指針傳參方式。
在C#中是能夠正常調用的。指針類型在C#中會被轉換為ref type類型,指明這個參數的值是能夠被函數內部所修改的。像以下的調用方式是成功的。
ActiveX中的函數原型:
LONG CSimpleActiveXCtrl::GetData(LONG v1, LONG* v2)
{
?? ?AFX_MANAGE_STATE(AfxGetStaticModuleState());
?? ?*v2 = 10;
?? ?return v1;
}
C#自動生成的函數定義:
public virtual int GetData(int v1, ref int v2);
C#中調用:
?int a=12, b=0;
??????? int c=axSimpleActiveX1.GetData(a,ref b);
雖然在C#中能夠正常調用,但是在JavaScript沒辦法調用,說明這種類型不是通用的,如果你想你開發的控件是通用的,應該禁止使用指針類型。
?
(3)數組參數
如果你需要傳遞一個數組,參數類型可以定義為Variant,但是對于數組的處理,在C#中和JavaScript中又是不一樣的。JavaScript傳入的數組,其vt類型是VT_DISPATCH,
而C#傳入的數組不是VT_DISPATCH類型,所以取值或賦值的方式又不同了。如果你只需要取得數組的值,那么只需要定義為Variant類型就可以了,如果你需要修改數組
的值,用于C#需要定義為Variant*類型,而對于JavaScript只需要定義為Variant類型就可以。所以說在不同語言里面的表現很不一樣。很顯然Variant類型也沒辦法做到通用。
ActiveX中的定義:
1)取數組長度:
long CSimpleActiveXCtrl::GetArrayLength(VARIANT &v1)
{
??? if (v1.vt == VT_DISPATCH)?? //JS數組
?? ?{
?? ??? ?IDispatch* pDisp = v1.pdispVal;
?? ??? ?int ret;
?? ??? ?BSTR varName = L"length";
?? ??? ?VARIANT varValue;
?? ??? ?DISPPARAMS noArgs = { NULL, NULL, 0, 0 };
?? ??? ?DISPID dispId;
?? ??? ?HRESULT hr = 0;
?? ??? ?hr = pDisp->GetIDsOfNames(IID_NULL, &varName, 1, LOCALE_USER_DEFAULT, &dispId);
?? ??? ?if (FAILED(hr))
?? ??? ??? ?return hr;
?? ??? ?hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &noArgs, &varValue, NULL, NULL);
?? ??? ?if (SUCCEEDED(hr))
?? ??? ?{
?? ??? ??? ?ret = varValue.intVal;
?? ??? ??? ?return ret;
?? ??? ?}
?? ??? ?else
?? ??? ?{
?? ??? ??? ?return 0;
?? ??? ?}
?? ?}
?? ?else?? //C#數組
?? ?{
?? ??? ?long Low(0), High(0);
?? ??? ?SAFEARRAY* pArr1 = v1.parray;
?? ??? ?SafeArrayGetLBound(pArr1, 1, &Low);
?? ??? ?SafeArrayGetUBound(pArr1, 1, &High);
?? ??? ?long len = High - Low + 1;
?? ??? ?return len;
?? ?}
}
2)數組取值
bool CSimpleActiveXCtrl::GetArrayDataI(VARIANT &v1, int index, int &data)
{
if (v1.vt == VT_DISPATCH)??? //JS數組
?? ?{
?? ??? ?IDispatch* pDisp = v1.pdispVal;
?? ??? ?ATL::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 false;
?? ??? ?//
?? ??? ?// 調用COM過程,訪問指定下標數組元素,根據dispId 將元素值保存在varValue中
?? ??? ?//
?? ??? ?hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &noArgs, &varValue, NULL, NULL);
?? ??? ?if (SUCCEEDED(hr))
?? ??? ?{
?? ??? ??? ?data = varValue.intVal;??? // 將數組元素按int類型取出
?? ??? ??? ?return true;
?? ??? ?}
?? ??? ?else
?? ??? ?{
?? ??? ??? ?return false;
?? ??? ?}
?? ?}
?? ?else?? //C#數組
?? ?{
?? ??? ?SAFEARRAY* pArr = v1.parray;
?? ??? ?int *pValue;
?? ??? ?SafeArrayAccessData(pArr, (void**)&pValue);
?? ??? ?data = pValue[index];
?? ??? ?SafeArrayUnaccessData(pArr);
?? ??? ?return true;
?? ?}
}
3)數組賦值
bool CSimpleActiveXCtrl::SetArrayDataI(VARIANT &v1, int index, int data)
{
if (v1.vt == VT_DISPATCH)?? //JS數組
?? ?{
?? ??? ?IDispatch* pDisp = v1.pdispVal;
?? ??? ?ATL::CComVariant varName(index, VT_I4);
?? ??? ?DISPID dispId;
?? ??? ?ATL::CComVariant varValue;
?? ??? ?HRESULT hr = 0;
?? ??? ?varName.ChangeType(VT_BSTR); // 將數組下標轉為數字型,以進行GetIDsOfNames
?? ??? ?hr = pDisp->GetIDsOfNames(IID_NULL, &varName.bstrVal, 1, LOCALE_USER_DEFAULT, &dispId);
?? ??? ?if (FAILED(hr))
?? ??? ??? ?return false;
?? ??? ?DISPID dispidPut = DISPID_PROPERTYPUT; // put操作
?? ??? ?DISPPARAMS dispparams;
?? ??? ?dispparams.rgvarg = new VARIANTARG[1]; // 初始化rgvarg
?? ??? ?dispparams.rgvarg[0].vt = VT_I4; // 數據類型
?? ??? ?dispparams.rgvarg[0].intVal = data; // 更新值
?? ??? ?dispparams.cArgs = 1; // 參數數量
?? ??? ?dispparams.cNamedArgs = 1; // 參數名稱
?? ??? ?dispparams.rgdispidNamedArgs = &dispidPut; // 操作DispId,表明本參數適用于put操作
?? ??? ?hr = pDisp->Invoke(dispId, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
?? ??? ?return true;
?? ?}
?? ?else??? //C#數組
?? ?{
?? ??? ?long Low(0), High(0);
?? ??? ?SAFEARRAY* pArr = v1.parray;
?? ??? ?int *pValue;
?? ??? ?SafeArrayAccessData(pArr, (void**)&pValue);
?? ??? ?pValue[index]=data;
?? ??? ?SafeArrayUnaccessData(pArr);
?? ??? ?return true;
?? ?}
}
?
4)JS數組拷貝
LONG CSimpleActiveXCtrl::CopyArrayData(VARIANT &v1, VARIANT &v2)
{
?? ?AFX_MANAGE_STATE(AfxGetStaticModuleState());
?? ?long len = GetArrayLength(v1);
?? ?for (int i = 0; i < len; i++)
?? ?{
?? ??? ?int value;
?? ??? ?GetArrayDataI(v1, i, value);
?? ??? ?SetArrayDataI(v2, i, value);
?? ?}
?? ?return 0;
}
5)C#數組拷貝
LONG CSimpleActiveXCtrl::CopyArray_CS(VARIANT &v1, VARIANT* v2)
{
?? ?AFX_MANAGE_STATE(AfxGetStaticModuleState());
?? ?// TODO:? 在此添加調度處理程序代碼
?? ?long len = GetArrayLength(v1);
?? ?for (int i = 0; i < len; i++)
?? ?{
?? ??? ?int value;
?? ??? ?GetArrayDataI(v1, i, value);
?? ??? ?SetArrayDataI(*v2, i, value);
?? ?}
?? ?return 0;
}
?
JS中的調用例子:
function CopyArrayClick()
{
?? ?? var arr1 = new Array();
?? ?? var arr2 = new Array();
?? ?? for (var i = 0; i < 10; i++)
?? ?? {
?? ??? ??? arr1[i] = i;
?? ??? ??? arr2[i] = 0;
?? ?? }
?? ?? var c = SimpleActiveX.CopyArrayData(arr1, arr2);
?? ?? var tmp = "";
?? ?? for (var j = 0; j < 10; j++)
?? ??? ??? tmp += arr2[j];
?? ?? document.getElementById("ResultDisplay").value = "arr2:" + tmp;
}
C#中的調用例子:
private void button1_Click(object sender, EventArgs e)
{
??????????? int[] arr1 = new int[10];
??????????? int[] arr2 = new int[10];
??????????? for (int i = 0; i < 10; i++)
??????????????? arr1[i] = i;
??????????? object var = new System.Runtime.InteropServices.VariantWrapper(arr2);
??????????? axSimpleActiveX1.CopyArray_CS(arr1, ref var);
??????????? int[] arr3 = var as int[];
??????????? string te="";
??????????? for (int i = 0; i < 10; i++)
??????????????? te += string.Format("{0} ", arr3[i]);
??????????????? textBox1.AppendText(te);
}
從上面的測試結果來看,對于ocx控件開發中,參數的選擇我有以下的建議:
1.避免使用指針類型;
2.如果需要輸出參數應該以函數返回值的方式返回,如果有多個參數要返回可以分成多個函數來實現或者通過屬性來返回;
3.個人認為可以使用BSTR來替代數組,不管作為參數或者返回值都更容易實現,在不同語言中對字符串的操作都是類似的。
轉載于:https://www.cnblogs.com/WushiShengFei/p/9298768.html
總結
以上是生活随笔為你收集整理的C++ ActiveX开发的问题讨论的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 花生壳绑定顶级域名
- 下一篇: MQTT客户端库-Paho GO