注册 登录
Office中国论坛/Access中国论坛 返回首页

ganlinlao的个人空间 http://www.office-cn.net/?230471 [收藏] [复制] [分享] [RSS]

日志

Vb/vba 与cdecl的故事—那扇寂寞的门始终轻轻微启

热度 1已有 1032 次阅读2022-5-25 11:25 |个人分类:vb入门

          Vb/vba能不能用cdecl?很像一个门的故事,套用我年轻时滥情的话说。

         “你很想推开门去看看门后的世界,你始终没有推开那扇门,你只是静静地等,等着有人从那扇门出来,把你轻轻抱起。只是你等了很久很久,来看你的人都走了,来劝你的人都走了,来爱你的人都走了,那扇门依然寂寞地对你轻轻微启……”

       这是trick大神为vb6提供了一个补丁,让vb6可以直接调用cdecl的函数。这种简单易用的方式,看起来不起眼,但无论如何它还是为vb6/vba打开了一扇门。因为太多的c函数库默认是cdeclc++库的导出函数也是cdecl方式,很多知名的c/c++ 数学计算库,图形库,音、视频库、pdf库……,对于一些人来说,在特定的场合,如果能用上这些知名而且性能很好的库,还是能带来不少方便的地方。

 

例子1

声明windows自带的msvcrtc函数库 和普通的winapi声明差不多,就是多了一个Cdecl关键字

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

 

调用msvrt的函数:

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

回调函数的调用。比如qsort vb的数组排序。这个我们在普通使用中,估计会用得很多。

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数组的排序非常的快。

 

通过以上例子,我们可以看出,在使用c/c++ 的函数时,指针会用得很频繁,一般强烈建议把指针用longptr来替换long,这样子代码易读性一目了然。强烈推荐 msvbvm60.tlb里的指针系列函数,会带来非常方便的指针操作。

 

例子3 cairo图形库的简单调用。注: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

 

这个例子只是为了简单说明可以轻易调用cdecl 的函数库。Vbrichclient6已经提供有完整的cario的包装类。

 

例子4:简单调用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

 

这个例子也只是简单调用sqlite3操作数据库,com版的sqlite3包装非常的多,很容易轻易获得到。

 

 

1vb/vbastring(bstr)cchar的转换问题

字符串在跨语言调用上,是相当麻烦的事情。常常会把人搞得晕头转向。

不少的c库函数的字符串是用char 数组,相当于ansi 字符串。在vb/vba中常用bytechar对应,byte()数组来对应 c库的字符串。如果在 声明中 传参是byref 方式,那传 byte(0)就相当于传 c 函数的字符串指针。如果是byval方式,那就是varptr(byte(0)) 也是相当于传 c函数的字符串指针。

 

Vbstringcchar字符串 转换函数:

Strconv() 这个会频繁调用进行互转。参数 vbfromunicode  就是将vbstring转成ansi字符串。参数vbunicode 就是将 ansi字符串转成 vbstring.

SysAllocStringByteLen() ansi字符串转成 bstr

:ansi字符串 英文是单字节,中文是双字节。

 

2vb/vbastring(bstr)c/c++wchar_T的转换问题

Vb/vbastring本身在内部使用unicodec/c++wchar_t就是unicode

所以如果传给c/c++函数的wchar_t参数,直接 strptr( bstr) 就可以了。

接收 c/c++函数的返回值,一般常用SysAllocString() unicode字符串转成bstr

 

Sys字符串系列函数在oleaut32.dll中,这个系列函数在跨语言调用中,会经常用到。

不少的winapi.tlb类型库会有这个系列函数,引用tlb后,可直接调用。

 

3、数字类型的缺失。

Vb/vba的数字类型没有 ulonguinteger(Ushort),longlong(64vba)Ulonglong。在某些场合下,会带来很多的不方便。特别是ulonglonglong。虽然有不少方法勉强可以迂回补救,但它确实是不直观。最糟糕的是,vb/vba的运算符无法重载,简直是一场灾难。

 

 

 

4variant数据类型转换

要精细操作variant数据类型转换。转入variant,主要使用 oleaut32.dll的系列函数,variant转出,主要使用propsys.dll的系列函数。但一般我们不怎么需要使用到它。Vb/vba自身的转换函数足够用了。

 

5vb的数组(safeArray)c/c++的数组。

这种情况应该是罕见,但如果遇到了。还是利用variant作为中间桥梁,用propsys.dll里的函数。

接收用initvariantFrom系列数组函数 转成variant,再利用。

传数组参数,用variantTo系列数组函数。传c的数组指针。

或许还有其它更好的方法。

总之,variant虽然缺点也不少,看着都难受。但还是可以作为跨语言传递的很好用的中间桥梁。

 

6、为vba制作提供vb6类的静态方法。

      我尝试想为32位的VBE打上这个补丁,可惜并未成功。目前对vba来说,只能通过vb6的类的静态方法来为

Vba作一种补充。Vb6创建类的静态方法如下:

首先,添加一个类模块(导入cls文件或者编写创建一个新的类模块)

然后,在右侧的属性窗口,按照如下的表格设置类的属性

属性名

属性值

(名称)

你的类名称

DataBindingBehavior

0 - vbNone

DataSourceBehavior

0 - vbNone

Instancing

6 - GlobalMultiUse

MTSTransactionMode

0 - NotAnMTSObject

Persistable

0 - NotPersistable

 

编译生成 ****.dll

7、对我个人来说。cdecl能直接调用,毫无疑问又近一步拉近了vb6与Freebasic之间的距离。毕竟freebasic的runtime也是cdecl方式。


vb6的cdecl补丁下载地址:https://wwi.lanzoup.com/io1JL0o0odzc

发表评论 评论 (2 个评论)

回复 tmtony 2022-6-1 11:47
冬瓜终于出新文章了。
回复 tmtony 2022-6-1 11:49
我转到知乎也宣传一下

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

QQ|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )  

GMT+8, 2024-4-18 14:45 , Processed in 0.064276 second(s), 18 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

返回顶部