用实例讲解Variant类型在VB、C#、VC中的参数传递
2012-03-17 12:34
302 查看
几年前我用VB开发了一个西门子PPI通信控件,由于VB开发的控件是标准的COM组件,所以想当然的认为VC、C#、Delphi等开发语言可以非常容易的使用。
前段时间由于该控件基于微软的MSCOMM控件,这个控件如果系统没有安装VB,单独注册好像很难成功,这害的一些没有装VB的用户,为了这个小控件必须安装一次VB,这实在是划算不来,所以直接用API串口函数进行了封装改进,这样不仅效率提高了,并且再也不需要MSCOMM控件了。
这一次,我不仅使该控件支持了浮点运算,并且在VC、C#(VB当然就不用多试了,以前就很好的支持)进行了兼容测试。
一试问题就来了,没有改进之前的控件,由于C#封装性能甚好,还能使用,唯一不足的是,控件方法中如果不要求返回的参数前面没有添加ByVal参数,在C#中就转换为 ref ***,害的你还得专门定义临时变量进行传参。
在VC中直接就不行,ReadData和WriteData等几个主要方法根本在VC中无法转换成对应函数,具体错误信息如下:
// method 'ReadData' not emitted because of invalid return type or parameter type
// method 'WriteData' not emitted because of invalid return type or parameter type
// method 'PlcLogin' not emitted because of invalid return type or parameter type
// method 'PlcRun' not emitted because of invalid return type or parameter type
// method 'PlcStop' not emitted because of invalid return type or parameter type
经过测试,最后发现VB函数中的byte和数组在VC中根本找不到合适的对应。
由于控件中又新添加了浮点数的支持,所以对参数接口又增加了一层复杂性。突然想到微软的MSCOMM控件各语言肯定都能很好的支持,它的接口参数是怎样的定义的。我在VB、VC、C#分别试了一下,VB中和input和Output属性的类型就是Variant类型,在VC中是VARIANT,在C#中是Object。用Variant还有个好处,就是各种类型的数据都可以传输,没有必要在另外添加接口函数了。
最后我定义的接口如下(主要接口):
在VC中对应的接口如下:
在C#中的接口如下:
以为这样定义就万事大吉了,事后一试我又错了,在C#中没有任何问题(看了微软还是在C#上下了很大的功夫),在VC简单的定义一个VARIANT变量直接传递给控件,VB控件老是报错,根本无法使用。后来想为什么MSCOMM控件可以,我的控件不可以。天杀的MSCOMM肯定是VC开发的,而我的控件是VB开发的,VB和C#的包容性都很强,而VC却高高在上不肯屈就。
正一筹莫展准备放弃的时候,突然想到了以前用VC开发的OPC程序,上面有很多关于VARIANT的应用,一看就明白了,原来在VC中VARIANT的用法是有讲究的。
下面我就详细说一下控件同样的接口在不同语言中如何使用。
在VB中:
在C#中:
在VC中:
前段时间由于该控件基于微软的MSCOMM控件,这个控件如果系统没有安装VB,单独注册好像很难成功,这害的一些没有装VB的用户,为了这个小控件必须安装一次VB,这实在是划算不来,所以直接用API串口函数进行了封装改进,这样不仅效率提高了,并且再也不需要MSCOMM控件了。
这一次,我不仅使该控件支持了浮点运算,并且在VC、C#(VB当然就不用多试了,以前就很好的支持)进行了兼容测试。
一试问题就来了,没有改进之前的控件,由于C#封装性能甚好,还能使用,唯一不足的是,控件方法中如果不要求返回的参数前面没有添加ByVal参数,在C#中就转换为 ref ***,害的你还得专门定义临时变量进行传参。
在VC中直接就不行,ReadData和WriteData等几个主要方法根本在VC中无法转换成对应函数,具体错误信息如下:
// method 'ReadData' not emitted because of invalid return type or parameter type
// method 'WriteData' not emitted because of invalid return type or parameter type
// method 'PlcLogin' not emitted because of invalid return type or parameter type
// method 'PlcRun' not emitted because of invalid return type or parameter type
// method 'PlcStop' not emitted because of invalid return type or parameter type
经过测试,最后发现VB函数中的byte和数组在VC中根本找不到合适的对应。
由于控件中又新添加了浮点数的支持,所以对参数接口又增加了一层复杂性。突然想到微软的MSCOMM控件各语言肯定都能很好的支持,它的接口参数是怎样的定义的。我在VB、VC、C#分别试了一下,VB中和input和Output属性的类型就是Variant类型,在VC中是VARIANT,在C#中是Object。用Variant还有个好处,就是各种类型的数据都可以传输,没有必要在另外添加接口函数了。
最后我定义的接口如下(主要接口):
Public Function ReadData(ByVal lngAddr As Long, vData As Variant, Optional ByVal lngNum As Long = 1, Optional ByVal bytLen As PPILEN = PPI_B, Optional ByVal bytType As PPITYPE = PPI_V, Optional ByVal Addr As Long = 0) As Long Public Function WriteData(ByVal lngAddr As Long, ByVal vData As Variant, Optional ByVal lngNum As Long = 1, Optional ByVal bytLen As PPILEN = PPI_B, Optional ByVal bytType As PPITYPE = PPI_V, Optional ByVal Addr As Long = 0) As Long
在VC中对应的接口如下:
long ReadData(long lngAddr, VARIANT* vData, long lngNum, long bytLen, long bytType, long Addr); long WriteData(long lngAddr, const VARIANT& vData, long lngNum, long bytLen, long bytType, long Addr);
在C#中的接口如下:
public virtual int ReadData(int lngAddr, ref object vData); public virtual int ReadData(int lngAddr, ref object vData, int lngNum, PPILEN bytLen, PPITYPE bytType, int addr); public virtual int WriteData(int lngAddr, object vData); public virtual int WriteData(int lngAddr, object vData, int lngNum, PPILEN bytLen, PPITYPE bytType, int addr);
以为这样定义就万事大吉了,事后一试我又错了,在C#中没有任何问题(看了微软还是在C#上下了很大的功夫),在VC简单的定义一个VARIANT变量直接传递给控件,VB控件老是报错,根本无法使用。后来想为什么MSCOMM控件可以,我的控件不可以。天杀的MSCOMM肯定是VC开发的,而我的控件是VB开发的,VB和C#的包容性都很强,而VC却高高在上不肯屈就。
正一筹莫展准备放弃的时候,突然想到了以前用VC开发的OPC程序,上面有很多关于VARIANT的应用,一看就明白了,原来在VC中VARIANT的用法是有讲究的。
下面我就详细说一下控件同样的接口在不同语言中如何使用。
在VB中:
Private Sub cmdReadData_Click() On Error GoTo ToExit '打开错误陷阱 '------------------------------------------------ Dim i As Long Dim bytType As Byte Dim lngRet As Long Dim lngData() As Long Dim fData() As Single Dim vData As Variant Select Case cmbType.ListIndex Case 0: bytType = PPI_I Case 1: bytType = PPI_Q Case 2: bytType = PPI_M Case 3: bytType = PPI_V Case 4: bytType = PPI_S Case 5: bytType = PPI_SM End Select S7_PPI1.FixAddr = cmbNo.ListIndex + 1 lngRet = S7_PPI1.ReadData(Val(txtAddr), vData, Val(cmbNum.Text), Val(cmbLen.ListIndex), Val(bytType)) If lngRet = 0 Then txtData = "" If cmbLen.ListIndex = 3 Then fData = vData For i = 1 To Val(cmbNum.Text) txtData = txtData & Format(fData(i - 1), "0.00") & " " Next Else lngData = vData For i = 1 To Val(cmbNum.Text) txtData = txtData & Format(lngData(i - 1), "0") & " " Next End If Else txtData = "Error" End If '------------------------------------------------ Exit Sub '---------------- ToExit: MsgBox Err.Description End Sub Private Sub cmdWriteData_Click() On Error GoTo ToExit '打开错误陷阱 '------------------------------------------------ Dim bytType As Byte Dim strData() As String Dim lngRet As Long Dim lngData(100) As Long Dim fData(100) As Single Dim i As Long Select Case cmbType.ListIndex Case 0: bytType = PPI_I Case 1: bytType = PPI_Q Case 2: bytType = PPI_M Case 3: bytType = PPI_V Case 4: bytType = PPI_S Case 5: bytType = PPI_SM End Select If Len(txtData) > 0 Then strData = Split(txtData, " ") If cmbLen.ListIndex = 3 Then For i = 0 To UBound(strData) fData(i) = Val(strData(i)) Next lngRet = S7_PPI1.WriteData(Val(txtAddr), fData, UBound(strData) + 1, Val(cmbLen.ListIndex), Val(bytType), cmbNo.ListIndex + 1) Else For i = 0 To UBound(strData) lngData(i) = Val(strData(i)) Next lngRet = S7_PPI1.WriteData(Val(txtAddr), lngData, UBound(strData) + 1, Val(cmbLen.ListIndex), Val(bytType), cmbNo.ListIndex + 1) End If If lngRet = 0 Then ' Else txtData = "Error" End If End If '------------------------------------------------ Exit Sub '---------------- ToExit: MsgBox Err.Description End Sub
在C#中:
/// <summary> /// 读数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnRead_Click(object sender, EventArgs e) { int intAddr = int.Parse(txtFixAddr.Text); object vData = new object(); /* [PPI_I] = &H81 [PPI_Q] = &H82 [PPI_M] = &H83 [PPI_V] = &H84 [PPI_S] = 4 [PPI_SM] = 5 */ PPIV2.PPITYPE DataType= PPIV2.PPITYPE.PPI_V; switch (cmbDataType.SelectedIndex) //数据类型 { case 0: DataType = PPIV2.PPITYPE.PPI_I; break; case 1: DataType = PPIV2.PPITYPE.PPI_Q; break; case 2: DataType = PPIV2.PPITYPE.PPI_M; break; case 3: DataType = PPIV2.PPITYPE.PPI_V; break; case 4: DataType = PPIV2.PPITYPE.PPI_S; break; case 5: DataType = PPIV2.PPITYPE.PPI_SM; break; } if (axS7_PPI1.ReadData(int.Parse(txtDataAddr.Text), ref vData, cmbLen.SelectedIndex+1 , (PPIV2.PPILEN)cmbDataMode.SelectedIndex, DataType, intAddr) == 0) { if (cmbDataMode.SelectedIndex == 3) { float[] fData = (float[])vData; txtData.Text = ""; for (int i = 0; i < fData.Length; i++) { txtData.Text += fData[i].ToString("0.00") + " "; } } else { Int32[] intData = (Int32[])vData; txtData.Text = ""; for (int i = 0; i < intData.Length; i++) { txtData.Text += intData[i].ToString() + " "; } } } else { txtData.Text = "ERROR"; } } /// <summary> /// 写数据 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnWrite_Click(object sender, EventArgs e) { int intAddr = int.Parse(txtFixAddr.Text); object vData = new object(); /* [PPI_I] = &H81 [PPI_Q] = &H82 [PPI_M] = &H83 [PPI_V] = &H84 [PPI_S] = 4 [PPI_SM] = 5 */ PPIV2.PPITYPE DataType = PPIV2.PPITYPE.PPI_V; switch (cmbDataType.SelectedIndex) //数据类型 { case 0: DataType = PPIV2.PPITYPE.PPI_I; break; case 1: DataType = PPIV2.PPITYPE.PPI_Q; break; case 2: DataType = PPIV2.PPITYPE.PPI_M; break; case 3: DataType = PPIV2.PPITYPE.PPI_V; break; case 4: DataType = PPIV2.PPITYPE.PPI_S; break; case 5: DataType = PPIV2.PPITYPE.PPI_SM; break; } long lngRet = 0; if (cmbDataMode.SelectedIndex == 3) { float[] fData = new float[100]; fData[0] = float.Parse(txtData.Text); lngRet = axS7_PPI1.WriteData(int.Parse(txtDataAddr.Text), fData, 1, (PPIV2.PPILEN)cmbDataMode.SelectedIndex, DataType, intAddr); } else { Int32[] intData = new Int32[100]; intData[0] = Int32.Parse(txtData.Text); lngRet = axS7_PPI1.WriteData(int.Parse(txtDataAddr.Text), intData, 1, (PPIV2.PPILEN)cmbDataMode.SelectedIndex, DataType, intAddr); } if (lngRet != 0) { txtData.Text = "ERROR"; } }
在VC中:
//读数据 void CPPI_TestDlg::OnReadData() { //pCombo->GetLBText (pCombo->GetCurSel (), strType); long lngFixAddr=0,lngDataAddr=0; char strAddr[255]; m_FixAddr.GetWindowText(strAddr,255); sscanf(strAddr,"%ld",&lngFixAddr); m_DataAddr.GetWindowText(strAddr,255); sscanf(strAddr,"%ld",&lngDataAddr); /* [PPI_I] = &H81 [PPI_Q] = &H82 [PPI_M] = &H83 [PPI_V] = &H84 [PPI_S] = 4 [PPI_SM] = 5 */ long DataType; switch (m_DataType.GetCurSel()) //数据类型 { case 0: DataType=0x81; break; case 1: DataType=0x82; break; case 2: DataType=0x83; break; case 3: DataType=0x84; break; case 4: DataType=0x4; break; case 5: DataType=0x5; break; } long lngRet; VARIANT vData; VariantInit (&vData); if(m_DataMode.GetCurSel()==3) //浮点数 { vData.vt = VT_R4 | VT_ARRAY; vData.parray=SafeArrayCreateVector(VT_R4, 0, 255 ); } else { vData.vt = VT_I4 | VT_ARRAY; vData.parray=SafeArrayCreateVector(VT_I4, 0, 255 ); //SAFEARRAY vd; } lngRet=m_PPI.ReadData(lngDataAddr,&vData,m_DataNum.GetCurSel()+1,m_DataMode.GetCurSel(),DataType,lngFixAddr); if(lngRet==0) { CString strData; if(m_DataMode.GetCurSel()==3) //浮点数 { float *fData; SafeArrayAccessData(vData.parray, (void**)&fData ); for(int i=0;i<m_DataNum.GetCurSel()+1;i++) //(int)vData.parray->cbElements { CString cData; cData.Format("%04.2f ",fData[i]); strData+= cData; } SafeArrayUnaccessData(vData.parray); } else { long *lngData; SafeArrayAccessData(vData.parray, (void**)&lngData ); for(int i=0;i<m_DataNum.GetCurSel()+1;i++) //(int)vData.parray->cbElements { CString cData; cData.Format("%ld ",lngData[i]); strData+= cData; } SafeArrayUnaccessData(vData.parray); } m_Data.SetWindowText(strData); } else { m_Data.SetWindowText(_T("ERROR")); } SafeArrayUnaccessData(vData.parray); SafeArrayDestroy(vData.parray); VariantClear(&vData); } //写数据 void CPPI_TestDlg::OnWrite() { long lngFixAddr=0,lngDataAddr=0; char strAddr[255]; m_FixAddr.GetWindowText(strAddr,255); sscanf(strAddr,"%ld",&lngFixAddr); m_DataAddr.GetWindowText(strAddr,255); sscanf(strAddr,"%ld",&lngDataAddr); long DataType; switch (m_DataType.GetCurSel()) { case 0: DataType=0x81; break; case 1: DataType=0x82; break; case 2: DataType=0x83; break; case 3: DataType=0x84; break; case 4: DataType=0x4; break; case 5: DataType=0x5; break; } long lngRet; VARIANT vData; VariantInit (&vData); if(m_DataMode.GetCurSel()==3) //浮点数 { vData.vt = VT_R4 | VT_ARRAY; vData.parray=SafeArrayCreateVector(VT_R4, 0, 255 ); float *fDatas,fData; SafeArrayAccessData(vData.parray, (void**)&fDatas ); m_Data.GetWindowText(strAddr,255); sscanf(strAddr,"%f",&fData); fDatas[0]=fData; SafeArrayUnaccessData(vData.parray); } else { vData.vt = VT_I4 | VT_ARRAY; vData.parray=SafeArrayCreateVector(VT_I4, 0, 255 ); //SAFEARRAY vd; long *lngDatas,lngData; SafeArrayAccessData(vData.parray, (void**)&lngDatas ); m_Data.GetWindowText(strAddr,255); sscanf(strAddr,"%ld",&lngData); lngDatas[0]=lngData; SafeArrayUnaccessData(vData.parray); } lngRet=m_PPI.WriteData(lngDataAddr,(const VARIANT &)vData,1,m_DataMode.GetCurSel(),DataType,lngFixAddr); if(lngRet!=0) { m_Data.SetWindowText(_T("ERROR")); } SafeArrayUnaccessData(vData.parray); SafeArrayDestroy(vData.parray); VariantClear(&vData); }
相关文章推荐
- C# 如何获取某个类型或类型实例对象的大小
- ASP.NET Web Pages - C# 和 VB 实例
- C#和VB.NET类型相关知识汇总
- java获取泛型参数的类型的方法 - 实例讲解
- VC++/C# 引用 VB 專用函式
- 各种计算机语言的经典书籍(C/C++/Java/C#/VC/VB等)
- 微型ORM——用VB和C#编写的动态类型ORM
- BaseDAO开发及获取泛型的传输数据类型(Class<T>)实例讲解
- 关于ExecuteNonQuery执行存储过程的返回值 、、实例讲解存储过程的返回值与传出参数、、、C#获取存储过程的 Return返回值和Output输出参数值
- C#反射实例讲解
- More Effective C# Item8 :尽可能使用泛型方法,除非需要将类型参数用于实例的字段中
- VC中常用的数据类型之间的转换string/LPCTSTR/LPSTR/VARIANT等
- C# 委托(跨窗体操作控件)实例流程讲解
- C#反射实例讲解
- <win8>(四)实例讲解win8(XAML+C#)开发--------课程表:Snapped模式和动态磁贴,徽章(badge)
- 实例讲解C#操作Xml文件的插入、修改、删除和显示代码
- 实例讲解C# WebService
- C# 继承类实例讲解
- DataTable 导出Excel (VB+C#) 实例源码