Variant类型在各语言中的参数传递

简介: 几年前我用VB开发了一个西门子PPI通信控件,由于VB开发的控件是标准的COM组件,所以想当然的认为VC、C#、Delphi等开发语言可以非常容易的使用。

几年前我用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中:
image.png

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#中:

image.png

/// <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中:

image.png

//读数据

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);

}

 

详细的示例请在下面的链接下载:

http://www.sky-walker.com.cn/yefan/PPIV2.rar

 

早期版本的控件下载链接:

http://www.sky-walker.com.cn/yefan/S7_PPI.rar

相关文章
|
存储 Python
深度剖析:Python里字典树Trie的构建与查询,让你的代码更优雅!
在编程的世界里,数据结构的选择往往直接决定了程序的效率和可读性。今天,我们将深入探索一种高效处理字符串搜索与匹配的数据结构——字典树(Trie),也称作前缀树或单词查找树。通过Python实现Trie树,我们将看到它如何优雅地解决一系列字符串相关的问题,并提升代码的整体质量。
329 2
|
18天前
|
人工智能 自然语言处理 文字识别
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
Qwen3.7-Max是阿里云百炼面向智能体时代推出的新一代旗舰模型,对标GPT-5.5、Claude Opus 4.7等闭源旗舰。该模型支持百万级token上下文窗口,具备顶级推理能力、多模态搜索与视觉理解增强、流式输出低延迟响应等核心优势,覆盖编程、办公、长周期自主执行等复杂场景。同时支持OpenAI接口兼容,便于系统快速迁移。用户可通过Token Plan团队或节省计划等订阅方式灵活调用,适合企业级高要求场景使用。
6837 30
阿里云百炼Qwen3.7-Max简介:能力、优势、支持订阅计划参考
|
3天前
|
数据采集 人工智能 前端开发
让 Coding Agent 从黑盒到透明:阿里云 Agent 观测审计数据采集实践
AI Agent 规模化落地带来执行黑盒、行为难追溯、成本难度量三大难题。阿里云基于 OTel 标准,面向 Coding Agent、个人通用助理和框架型 Agent,推出 LoongSuite Pilot、插件及探针等无侵入采集方案,让 Agent 实现可看见、可分析、可审计、可治理。
605 138
|
3天前
|
人工智能 弹性计算 运维
阿里云发布堡垒机智能运维Agent,运维交互进入自然语言新时代
支持自然语言运维,提升效率与安全双保障。
1145 0
|
10天前
|
人工智能 安全 定位技术
CodeGraph深度解析 让Claude Code工具调用直降七成的核心原理与实操教程
如今以Claude Code为代表的AI编程智能体已经成为开发者日常编码、项目重构、漏洞修复的必备工具。但在长期使用过程中,几乎所有开发者都会遇到同一个明显痛点:AI虽然具备强大的代码生成与分析能力,却常常陷入盲目探索的循环中。
1171 1
|
13天前
|
存储 定位技术 数据库
CodeGraph 如何让 Claude Code减少 7 成工具调用?
CodeGraph 为 Coding Agent 提供本地代码知识图谱,把函数、类、调用链和框架路由提前整理成“项目地图”,减少盲目搜索和文件读取。它不是新 Agent,而是上下文基础设施,让 Agent 更快找到正确代码路径,平均减少 7 成工具调用。
1273 3
|
11天前
|
人工智能 弹性计算 安全
阿里云618活动时间、活动入口、优惠活动详细解读
2026年阿里云618创新加速季已全面开启,作为年度力度最大的云产品促销活动,本次大促覆盖轻量应用服务器、ECS云服务器、GPU云服务器、数据库、AI算力、安全服务、CDN等全品类产品,推出5亿元算力补贴、新用户限时秒杀、普惠满减、企业专享、免费试用、云大使返佣等多重福利,个人开发者、中小企业、AI团队均可享受专属低价。本文将系统梳理2026年阿里云618活动的完整时间节点、官方参与入口、各类优惠细则、使用规则、热门产品推荐及实操代码,帮助用户精准参与、高效省钱,以最低成本完成上云部署。
982 5
|
9天前
|
人工智能 自然语言处理 安全
Vibe Coding 实战:别盲目跟风,先分清 vibe coding 适合什么场景
本文系统总结vibe coding实战经验:明确其适用场景(原型、小工具、标准化模块),剖析5步落地流程(场景判定→结构化提示词→目录初始化→分模块生成→自动化校验),指出四大常见误区,并推荐适配工具Trae。强调“场景匹配+规则前置”是提效关键,避免盲目套用。
806 1