几年前我用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还有个好处,就是各种类型的数据都可以传输,没有必要在另外添加接口函数了。
最后我定义的接口如下(主要接口):
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);
分享到:
相关推荐
5. **多用途**:`Variant`常用于数据交换、函数返回值和参数传递,尤其是在那些需要通用性或不确定数据类型的情况下。 6. **局限性**:尽管`Variant`提供了灵活性,但它也有一些缺点。比如,由于自动类型转换,可能...
3. **参数传递方式**:VB使用两种参数传递方式——按值(ByVal)和按引用(ByRef)。按值传递时,函数内部无法改变参数的实际值;按引用传递则可以。 4. **可选参数**:如果你希望函数或子过程能够接受可选参数,...
在COM接口设计中,定义一个接受或返回`VARIANT`类型参数的方法是一种常见的传递二进制数据的方式。例如,可以定义一个名为`TransferBinaryData`的方法,其参数类型为`IN OUT VARIANT *pData`。这样,调用者既可以...
本文将详细介绍如何在 ATL/MFC DLL 中实现数组的有效传递,包括数组作为参数传入函数以及作为返回值从函数传出的情况。 #### 二、将数组打包到 VARIANT 在 COM 编程中,`VARIANT` 类型常用于封装多种数据类型,以...
在控件的接口中,如`SendData()`和`GetData()`,VARIANT被用来作为参数传递不同类型的数据,这允许控件灵活地处理各种数据格式。 `SendData()`方法用于发送数据,它接受VARIANT类型的`Data`参数,表示要发送的数据...
例如,如果一个函数的目的是交换两个整数变量的值,那么这两个变量的地址就需要作为参数传递给函数,然后在函数内部通过解引用指针来交换它们的值。 文档还强调了初始化指针作为函数参数的必要性。通常,当函数的...
在VBScript或VBA等语言中,“ParamArray”关键字被用来定义一个可以接受任意数量参数的子程序或函数。“ParamArray”后的参数必须是最后一个参数,并且必须是变体型(Variant)数组。当调用这样的子程序或函数时,...
在COM中,有一些特殊的内置数据类型,它们被设计用来处理跨语言和跨平台的通信,这些类型包括BSTR、VARIANT和SAFEARRAY。本文将深入探讨这些数据类型的特性和使用方法,以及他们在实际应用中可能遇到的问题和注意...
为了提高代码的灵活性和可读性,许多现代编程语言支持指定参数的缺省值,即在调用时如果没有传递该参数,则会使用预设的值。此外,还可以定义参数为可选参数,这样在调用时可以选择性地提供这些参数。 #### 缺省值...
本篇文章将详细探讨如何使用Java通过JACOB库来调用C++编写的COM(Component Object Model)组件,特别是涉及VARIANT*和BSTR*这两种数据类型的参数传递。 首先,让我们了解什么是COM组件。COM是一种微软开发的技术,...
此外,由于`Variant`类型的多态性质,还需要确保传递给`ThreeAsk`的参数类型能够兼容。 为了提高性能和安全性,可以考虑以下改进措施: - **类型检查**:在函数内部添加对`b`和`c`的类型检查,确保它们具有相同或...
3. **类型系统**:VB.NET中的`Object`和`Variant`在C#中对应的是`object`,VB.NET的`ByVal`和`ByRef`参数传递方式在C#中分别对应`value`和`ref`。 4. **集合和数组**:VB.NET的`ArrayList`在C#中通常被更现代的`...
- `VARIANT`和`variant_t`、`COleVariant`:用于表示和操作复杂数据类型的结构体,如在COM接口中。 - `WNDPROC`:32位指针,指向窗口过程函数。 - `LANGID`和`LCID`:用于表示语言和区域设置的标识符。 在16位...
在ActiveX控件中,VARIANT作为参数传递,允许控件与宿主应用程序之间灵活的数据交换。理解VARIANT的构造和转换是关键,例如如何将VARIANT转换为特定数据类型,以及如何正确初始化和清理VARIANT变量。 **2. ...
- 如果形参类型为Variant的FC/FB/指令在实参中填写了DB块,那么该DB必须基于UDT或系统数据类型建立,并且按照相应的类型进行处理。 #### 三、应用场景举例 为了更好地理解DB_ANY的实用价值,我们可以考虑一个具体...
- 在Automation中,比如在VBA宏或脚本中调用OLE Automation对象,SafeArray用于传递数组参数。 - 自定义COM组件或ActiveX控件的开发,SafeArray可以帮助实现跨语言的数据交换。 总之,SafeArray是COM环境中高效且...
这些类型提供额外的灵活性,尤其是在参数化查询中。 通过深入理解这些字段类型及其对应的值,开发人员可以更加精确地控制数据的存储和处理方式,从而提高应用程序的性能和可靠性。在使用ADOX进行数据库管理时,选择...
由于ActiveX在数据类型方面有严格的定义,变体的存在简化了AutoLISP与ActiveX对象之间参数传递的复杂性,因为变体可以自动地在AutoCAD内部类型与ActiveX所需的特定类型之间进行转换。 创建变体主要通过vlax-make-...
在OCX的接口定义中,数组通常通过`VARIANT*`类型进行传递。`VARIANT`是COM中的一种数据结构,可以容纳各种类型的值,包括数组。当C#项目引用OCX控件时,`VARIANT*`会被自动映射为`ref object`。然而,直接将C#的`...
在这个例子中,`Your.Net.Class.Name`是.NET DLL中的类名,`YourMethodName`是你要调用的方法名,`参数1`和`参数2`是传递给方法的参数。 4. **注意事项** - 记得在使用完毕后释放COM资源,以避免内存泄漏,这通常...