用实例讲解Variant类型在VB、C#、VC中的参数传递

  几年前我用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 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 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 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(bytType),cmbNo.ListIndex + 1)

        Else

            For i = 0 To UBound(strData)

                lngData(i) = Val(strData(i))

            Next

            lngRet = S7_PPI1.WriteData(Val(txtAddr),lngData,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),1,intAddr);

            }

            else

            {

                Int32[] intData = new Int32[100];

                intData[0] = Int32.Parse(txtData.Text);

                lngRet = axS7_PPI1.WriteData(int.Parse(txtDataAddr.Text),intData,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,&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,255 );    

       }

       else

       {

           vData.vt = VT_I4 | VT_ARRAY;

           vData.parray=SafeArrayCreateVector(VT_I4,255 );     //SAFEARRAY vd;

       }   

       lngRet=m_PPI.ReadData(lngDataAddr,&vData,m_DataNum.GetCurSel()+1,m_DataMode.GetCurSel(),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);

 

    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,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,255 );     //SAFEARRAY vd;

             

              long *lngDatas,lngData;

              SafeArrayAccessData(vData.parray,(void**)&lngDatas );

              m_Data.GetWindowText(strAddr,&lngData);

              lngDatas[0]=lngData;

              SafeArrayUnaccessData(vData.parray);

       }

 

    lngRet=m_PPI.WriteData(lngDataAddr,(const VARIANT &)vData,lngFixAddr);

       if(lngRet!=0)

       {

              m_Data.SetWindowText(_T("ERROR"));

       }

       SafeArrayUnaccessData(vData.parray);

       SafeArrayDestroy(vData.parray);

    VariantClear(&vData);

}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Format[$] ( expr [ , fmt ] ) format 返回变体型 format$ 强制返回为文本 -------------------------------- 数字类型的格式化 --------------------------------     固定格式参数:     General Number 普通数字,如可以用来去掉千位分隔号     format$("100,1
VB6或者ASP 格式化时间为 MM/dd/yyyy 格式,竟然没有好的办法, Format 或者FormatDateTime 竟然结果和系统设置的区域语言的日期和时间格式相关。意思是尽管你用诸如 Format(Now, "MM/dd/yyyy"),如果系统的设置格式区域语言的日期和时间格式分隔符是"-",那他还会显示为 MM-dd-yyyy     只有拼凑: <%response.write
在项目中添加如下代码:新建窗口来显示异常信息。 Namespace My ‘全局错误处理,新的解决方案直接添加本ApplicationEvents.vb 到工程即可 ‘添加后还需要一个From用来显示错误。如果到这步还不会则需要先打好基础啦 ‘======================================================== ‘以下事件
转了这一篇文章,原来一直想用C#做k3的插件开发,vb没有C#用的爽呀,这篇文章写与2011年,看来我以前没有认真去找这个方法呀。 https://blog.csdn.net/chzjxgd/article/details/6176325 金蝶K3 BOS的插件官方是用VB6编写的,如果  能用.Net下的语言工具开发BOS插件是一件很愉快的事情,其中缘由不言而喻,而本文则是个人首创,实现在了用V
Sub 分列() ‘以空格为分隔符,连续空格只算1个。对所选中的单元格进行处理 Dim m As Range, tmpStr As String, s As String Dim x As Integer, y As Integer, subStr As String If MsgBox("确定要分列处理吗?请确定分列的数据会覆盖它后面的单元格!", _
  窗体代码 1 Private Sub Text1_OLEDragDrop(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single) 2 Dim path As String, hash As String 3 For Each fil
  Imports MySql.Data.MySqlClient Public Class Form1 ‘ GLOBAL DECLARATIONS Dim conString As String = "Server=localhost;Database=net2;Uid=root;Pwd=123456;" Dim con As New MySqlConnection
‘導入命名空間 Imports ADODB Imports Microsoft.Office.Interop   Private Sub A1() Dim Sql As String Dim Cnn As New ADODB.Connection Dim Rs As New ADODB.Recordset Dim S As String   S = "Provider=OraOLEDB.Oracl
Imports System.IO Imports System.Threading Imports System.Diagnostics Public Class Form1 Dim A(254) As String    Function ping(ByVal IP As Integer) As String Dim IPAddress As String IPAddress = "10.0.
VB运行EXE程序,并等待其运行结束 参考:https://blog.csdn.net/useway/article/details/5494084 Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal hHandle As Long, ByVal dwMilliseconds As Long) As Long Pr
今天碰到一个问题,登陆的时候,如果不需要验证手机号为空,则不去验证手机号 因为登陆的时候所有的验证信息都存放在一个数组里 Dim CheckUserInfo() As String ={UserBirthday, SecEmail, UserMob, UserSex, RealNameFirst, RealName, CheckCardID, CheckCardType, Contactemail
在VB6.0中,数据访问接口有三种: 1、ActiveX数据对象(ADO) 2、远程数据对象(RDO) 3、数据访问对象(DAO) 1.使用ADO(ActiveX Data Objec,ActiveX数据对象)连接SQL Server 1)使用ADO控件连接 使用ADO控件的ConnectionString属性就可以连接SQL Server,该属性包含一个由分号分隔的argument=value语
注:大家如果没有VB6.0的安装文件,可自行百度一下下载,一般文件大小在200M左右的均为完整版的软件,可以使用。   特别提示:安装此软件的时候最好退出360杀毒软件(包括360安全卫士,电脑管家等,如果电脑上有这些软件的话),因为现如今的360杀毒软件直接会对VB6.0软件误报,这样的话就可能会在安装过程中被误报阻止而导致安装失败,或者是安装后缺乏很多必须的组件(其它的杀毒软件或安全卫士之类的
Private Sub Form_Load() Call conndb End Sub Private Function conndb() Dim cn As New ADODB.Connection Dim rs As New ADODB.Recordset Dim strCn, sql As String Dim db_host As String Dim db_user As String
  PPSM06S70:  Add  moddate  EDITSPRINTJOB:  MAX(TO_CHAR(ETRN.MODDATE, ‘yyyy/mm/dd/HH24:MI AM‘)) ACTUAL_SHIPDATE   4.Test Scenario (1) :Query SQL Test DN:8016578337 SELECT CTRN.TKCTID TRUCK_ID,        
  沒有出現CrystalReportViewer時,須安裝CRforVS_13_0. 新增1個數據集,新增1個數據表,添加二列,列名要和資料庫名一樣. 修改目標Framework 修改app.config, <startup >改成<startup useLegacyV2RuntimeActivationPolicy ="true">  CrystalReport1.rpt增加數據庫專家 在表單
Imports System.Threading Imports System Public Class Form1 Dim th1, th2 As Thread Public Sub Method1() Dim i As Integer For i = 1 To 100 If Me.Label1.BackColor =
Friend Const PROCESS_ALL_ACCESS = &H1F0FFF = 2035711 Friend Const PROCESS_VM_READ = &H10 Friend Const PROCESS_VM_WRITE = &H20 Friend Const PAGE_READONLY = &H2 Friend Const PAGE_READWRITE = &H4 Friend
以下代码随手写的 并没有大量测试 效率也有待提升 如果需要C#的请自行转换 Function SplitBytes(Data As Byte(), Delimiter As Byte()) As List(Of Byte()) Dim i = 0 Dim List As New List(Of Byte()) Dim bytes As New
Imports System.Data.SqlClient Public Class Form1 REM Public conn1 As SqlConnection = New SqlConnection("server=.; Integrated Security=False;Initial Catalog= mydatabase1; User ID= sa;password")