注冊 登錄
Office中國論壇/Access中國論壇 返回首頁

ganlinlao的個人空間 http://m.mzhfr.cn/?230471 [收藏] [復(fù)制] [分享] [RSS]

日志

Vb/vba 與cdecl的故事—那扇寂寞的門始終輕輕微啟

熱度 1已有 1593 次閱讀2022-5-25 11:25 |個人分類:vb入門

          Vb/vba能不能用cdecl?很像一個門的故事,套用我年輕時濫情的話說。

         “你很想推開門去看看門后的世界,你始終沒有推開那扇門,你只是靜靜地等,等著有人從那扇門出來,把你輕輕抱起。只是你等了很久很久,來看你的人都走了,來勸你的人都走了,來愛你的人都走了,那扇門依然寂寞地對你輕輕微啟……”

       這是trick大神為vb6提供了一個補丁,讓vb6可以直接調(diào)用cdecl的函數(shù)。這種簡單易用的方式,看起來不起眼,但無論如何它還是為vb6/vba打開了一扇門。因為太多的c函數(shù)庫默認是cdecl,c++庫的導(dǎo)出函數(shù)也是cdecl方式,很多知名的c/c++ 數(shù)學(xué)計算庫,圖形庫,音、視頻庫、pdf庫……,對于一些人來說,在特定的場合,如果能用上這些知名而且性能很好的庫,還是能帶來不少方便的地方。

 

例子1

聲明windows自帶的msvcrtc函數(shù)庫 和普通的winapi聲明差不多,就是多了一個Cdecl關(guān)鍵字

public Declare Function snwprintf1 CDecl Lib "msvcrt" _

                         Alias "_snwprintf" ( _

                         ByVal pszBuffer As Long, _

                         ByVal lCount As Long, _

                         ByVal pszFormat As Long, _

                         ByRef pArg1 As Any) As Long

public Declare Function snwprintf2 CDecl Lib "msvcrt" _

                         Alias "_snwprintf" ( _

                         ByVal pszBuffer As Long, _

                         ByVal lCount As Long, _

                         ByVal pszFormat As Long, _

                         ByRef pArg1 As Any, _

                         ByRef pArg2 As Any) As Long

public Declare Function wtoi64 CDecl Lib "msvcrt" _

                         Alias "_wtoi64" ( _

                         ByVal psz As Long) As Currency

 

調(diào)用msvrt的函數(shù):

sBuf = Space$(255)   

    Debug.Print Left$(sBuf, snwprintf1(StrPtr(sBuf), Len(sBuf), StrPtr("Test %ld"), ByVal 123&))

   

    Debug.Print Left$(sBuf, snwprintf2(StrPtr(sBuf), Len(sBuf), StrPtr("Test %ld, %s"), ByVal 123&, ByVal StrPtr("Hello")))   

    Debug.Print wtoi64(StrPtr("123456789"))

 

例子2

回調(diào)函數(shù)的調(diào)用。比如qsort vb的數(shù)組排序。這個我們在普通使用中,估計會用得很多。

Public Declare Sub qsort CDecl Lib "msvcrt" ( _

                         ByRef pFirst As Any, _

                         ByVal lNumber As Long, _

                         ByVal lSize As Long, _

                         ByVal pfnComparator As Long)

Sub Main()

    Dim z() As Long

    Dim i As Long

    Dim s As String   

    ReDim z(500)   

    For i = 0 To UBound(z)

        z(i) = Int(Rnd * 10000)

    Next   

    qsort z(0), UBound(z) + 1, LenB(z(0)), AddressOf Comparator   

    For i = 0 To UBound(z)

        Debug.Print z(i)

    Next

End Sub

 

Private Function Comparator CDecl( _

                 ByRef a As Long, _

                 ByRef b As Long) As Long

    Comparator = a - b

End Function

 

vb數(shù)組的排序非常的快。

 

通過以上例子,我們可以看出,在使用c/c++ 的函數(shù)時,指針會用得很頻繁,一般強烈建議把指針用longptr來替換long,這樣子代碼易讀性一目了然。強烈推薦 msvbvm60.tlb里的指針系列函數(shù),會帶來非常方便的指針操作。

 

例子3 cairo圖形庫的簡單調(diào)用。注:OLE_HANDLEstdole中定義了,這是每個vb/vba必須要有的

Private Declare Function cairo_win32_surface_create CDecl Lib "cairo.dll" ( _

                         ByVal hDc As OLE_HANDLE) As OLE_HANDLE

Private Declare Function cairo_create CDecl Lib "cairo.dll" ( _

                         ByVal pSurface As OLE_HANDLE) As OLE_HANDLE

Private Declare Sub cairo_set_line_width CDecl Lib "cairo.dll" ( _

                    ByVal pCr As OLE_HANDLE, _

                    ByVal dValue As Double)

Private Declare Sub cairo_set_source_rgb CDecl Lib "cairo.dll" ( _

                    ByVal pCr As OLE_HANDLE, _

                    ByVal dR As Double, _

                    ByVal dG As Double, _

                    ByVal dB As Double)

Private Declare Sub cairo_rectangle CDecl Lib "cairo.dll" ( _

                    ByVal pCr As OLE_HANDLE, _

                    ByVal dX As Double, _

                    ByVal dY As Double, _

                    ByVal dW As Double, _

                    ByVal dH As Double)

Private Declare Sub cairo_stroke CDecl Lib "cairo.dll" ( _

                    ByVal pCr As OLE_HANDLE)

Private Declare Sub cairo_destroy CDecl Lib "cairo.dll" ( _

                    ByVal pCr As OLE_HANDLE)

Private Declare Sub cairo_surface_destroy CDecl Lib "cairo.dll" ( _

                    ByVal pSurface As OLE_HANDLE)

                   

Private Sub Form_Load()

    Dim pSurf   As Long

    Dim pCr     As Long

 

    pSurf = cairo_win32_surface_create(Me.hDc)

    pCr = cairo_create(pSurf)

   

    cairo_set_line_width pCr, 3

    cairo_set_source_rgb pCr, 1, 0.5, 0.5

    cairo_rectangle pCr, 10, 10, 300, 200

    cairo_stroke pCr

   

    cairo_destroy pCr

    cairo_surface_destroy pSurf

   

End Sub

 

這個例子只是為了簡單說明可以輕易調(diào)用cdecl 的函數(shù)庫。Vbrichclient6已經(jīng)提供有完整的cario的包裝類。

 

例子4:簡單調(diào)用sqlite3.dll

Private Const SQLITE_OK     As Long = 0

Private Const SQLITE_ROW    As Long = 100

Private Declare Function sqlite3_open CDecl Lib "sqlite3" ( _

                         ByVal filename As String, _

                         ByRef ppDB As OLE_HANDLE) As Long

Private Declare Function sqlite3_prepare_v2 CDecl Lib "sqlite3" ( _

                         ByVal db As OLE_HANDLE, _

                         ByVal zSql As String, _

                         ByVal nByte As Long, _

                         ByRef ppStmt As OLE_HANDLE, _

                         ByRef pzTail As Any) As Long

Private Declare Function sqlite3_step CDecl Lib "sqlite3" ( _

                         ByVal pStmt As OLE_HANDLE) As Long

Private Declare Function sqlite3_finalize CDecl Lib "sqlite3" ( _

                         ByVal pStmt As OLE_HANDLE) As Long

Private Declare Function sqlite3_close CDecl Lib "sqlite3" ( _

                         ByVal ppDB As OLE_HANDLE) As Long

Private Declare Function sqlite3_column_text16 CDecl Lib "sqlite3" ( _

                         ByVal pStmt As OLE_HANDLE, _

                         ByVal iCol As Long) As Long

Private Declare Function SysAllocString Lib "oleaut32" ( _

                         ByRef pOlechar As Any) As Long

Private Declare Function PutMem4 Lib "msvbvm60.dll" ( _

                         ByRef pDst As Any, _

                         ByVal lVal As Long) As Long

                        

Sub Main()

    Dim pDB         As OLE_HANDLE

    Dim pStmt       As OLE_HANDLE

    Dim lResult     As Long

    Dim sBstrRes    As String   

    lResult = sqlite3_open(":memory:", pDB)   

    If lResult <> SQLITE_OK Then

        MsgBox "Cannot open database", vbCritical

        GoTo CleanUp

    End If   

    lResult = sqlite3_prepare_v2(pDB, "SELECT SQLITE_VERSION()", -1, pStmt, ByVal 0&)   

    If lResult <> SQLITE_OK Then

        MsgBox "Cannot open database", vbCritical

        GoTo CleanUp

    End If   

    lResult = sqlite3_step(pStmt)   

    If lResult = SQLITE_ROW Then   

        PutMem4 ByVal VarPtr(sBstrRes), SysAllocString(ByVal sqlite3_column_text16(pStmt, 0))        

        Debug.Print sBstrRes       

    End If

   

CleanUp:   

    If pStmt Then sqlite3_finalize pStmt

    If pDB Then sqlite3_close pDB   

End Sub

 

這個例子也只是簡單調(diào)用sqlite3操作數(shù)據(jù)庫,com版的sqlite3包裝非常的多,很容易輕易獲得到。

 

 

1、vb/vbastring(bstr)cchar的轉(zhuǎn)換問題

字符串在跨語言調(diào)用上,是相當(dāng)麻煩的事情。常常會把人搞得暈頭轉(zhuǎn)向。

不少的c庫函數(shù)的字符串是用char 數(shù)組,相當(dāng)于ansi 字符串。在vb/vba中常用bytechar對應(yīng),byte()數(shù)組來對應(yīng) c庫的字符串。如果在 聲明中 傳參是byref 方式,那傳 byte(0)就相當(dāng)于傳 c 函數(shù)的字符串指針。如果是byval方式,那就是varptr(byte(0)) 也是相當(dāng)于傳 c函數(shù)的字符串指針。

 

Vbstringcchar字符串 轉(zhuǎn)換函數(shù):

Strconv() 這個會頻繁調(diào)用進行互轉(zhuǎn)。參數(shù) vbfromunicode  就是將vbstring轉(zhuǎn)成ansi字符串。參數(shù)vbunicode 就是將 ansi字符串轉(zhuǎn)成 vbstring.

SysAllocStringByteLen() ansi字符串轉(zhuǎn)成 bstr

:ansi字符串 英文是單字節(jié),中文是雙字節(jié)。

 

2vb/vbastring(bstr)c/c++wchar_T的轉(zhuǎn)換問題

Vb/vbastring本身在內(nèi)部使用unicode,c/c++wchar_t就是unicode

所以如果傳給c/c++函數(shù)的wchar_t參數(shù),直接 strptr( bstr) 就可以了。

接收 c/c++函數(shù)的返回值,一般常用SysAllocString() unicode字符串轉(zhuǎn)成bstr

 

Sys字符串系列函數(shù)在oleaut32.dll中,這個系列函數(shù)在跨語言調(diào)用中,會經(jīng)常用到。

不少的winapi.tlb類型庫會有這個系列函數(shù),引用tlb后,可直接調(diào)用。

 

3、數(shù)字類型的缺失。

Vb/vba的數(shù)字類型沒有 ulonguinteger(Ushort),longlong(64vba),Ulonglong。在某些場合下,會帶來很多的不方便。特別是ulonglonglong。雖然有不少方法勉強可以迂回補救,但它確實是不直觀。最糟糕的是,vb/vba的運算符無法重載,簡直是一場災(zāi)難。

 

 

 

4、variant數(shù)據(jù)類型轉(zhuǎn)換

要精細操作variant數(shù)據(jù)類型轉(zhuǎn)換。轉(zhuǎn)入variant,主要使用 oleaut32.dll的系列函數(shù),variant轉(zhuǎn)出,主要使用propsys.dll的系列函數(shù)。但一般我們不怎么需要使用到它。Vb/vba自身的轉(zhuǎn)換函數(shù)足夠用了。

 

5、vb的數(shù)組(safeArray)c/c++的數(shù)組。

這種情況應(yīng)該是罕見,但如果遇到了。還是利用variant作為中間橋梁,用propsys.dll里的函數(shù)。

接收用initvariantFrom系列數(shù)組函數(shù) 轉(zhuǎn)成variant,再利用。

傳數(shù)組參數(shù),用variantTo系列數(shù)組函數(shù)。傳c的數(shù)組指針。

或許還有其它更好的方法。

總之,variant雖然缺點也不少,看著都難受。但還是可以作為跨語言傳遞的很好用的中間橋梁。

 

6、為vba制作提供vb6類的靜態(tài)方法。

      我嘗試想為32位的VBE打上這個補丁,可惜并未成功。目前對vba來說,只能通過vb6的類的靜態(tài)方法來為

Vba作一種補充。Vb6創(chuàng)建類的靜態(tài)方法如下:

首先,添加一個類模塊(導(dǎo)入cls文件或者編寫創(chuàng)建一個新的類模塊)

然后,在右側(cè)的屬性窗口,按照如下的表格設(shè)置類的屬性

屬性名

屬性值

(名稱)

你的類名稱

DataBindingBehavior

0 - vbNone

DataSourceBehavior

0 - vbNone

Instancing

6 - GlobalMultiUse

MTSTransactionMode

0 - NotAnMTSObject

Persistable

0 - NotPersistable

 

編譯生成 ****.dll

7、對我個人來說。cdecl能直接調(diào)用,毫無疑問又近一步拉近了vb6與Freebasic之間的距離。畢竟freebasic的runtime也是cdecl方式。


vb6的cdecl補丁下載地址:https://wwi.lanzoup.com/io1JL0o0odzc

發(fā)表評論 評論 (2 個評論)

回復(fù) tmtony 2022-6-1 11:47
冬瓜終于出新文章了。
回復(fù) tmtony 2022-6-1 11:49
我轉(zhuǎn)到知乎也宣傳一下

facelist doodle 涂鴉板

您需要登錄后才可以評論 登錄 | 注冊

QQ|站長郵箱|小黑屋|手機版|Office中國/Access中國 ( 粵ICP備10043721號-1 )  

GMT+8, 2025-7-13 03:07 , Processed in 0.069553 second(s), 18 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

返回頂部