用例項講解Variant型別在VB、C#、VC中的引數傳遞

iDotNetSpace發表於2009-02-25
 幾年前我用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

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-557791/,如需轉載,請註明出處,否則將追究法律責任。

相關文章