热度 1|
“你很想推开门去看看门后的世界,你始终没有推开那扇门,你只是静静地等,等着有人从那扇门出来,把你轻轻抱起。只是你等了很久很久,来看你的人都走了,来劝你的人都走了,来爱你的人都走了,那扇门依然寂寞地对你轻轻微启……”
这是trick大神为vb6提供了一个补丁,让vb6可以直接调用cdecl的函数。这种简单易用的方式,看起来不起眼,但无论如何它还是为vb6/vba打开了一扇门。因为太多的c函数库默认是cdecl,c++库的导出函数也是cdecl方式,很多知名的c/c++ 数学计算库,图形库,音、视频库、pdf库……,对于一些人来说,在特定的场合,如果能用上这些知名而且性能很好的库,还是能带来不少方便的地方。
例子1:
声明windows自带的msvcrt的c函数库 和普通的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_HANDLE在stdole中定义了,这是每个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包装非常的多,很容易轻易获得到。
1、vb/vba的string(bstr)与c的char的转换问题
字符串在跨语言调用上,是相当麻烦的事情。常常会把人搞得晕头转向。
不少的c库函数的字符串是用char 数组,相当于ansi 字符串。在vb/vba中常用byte和char对应,byte()数组来对应 c库的字符串。如果在 声明中 传参是byref 方式,那传 byte(0)就相当于传 c 函数的字符串指针。如果是byval方式,那就是varptr(byte(0)) 也是相当于传 c函数的字符串指针。
Vb的string和c的char字符串 转换函数:
Strconv() 这个会频繁调用进行互转。参数 vbfromunicode 就是将vb的string转成ansi字符串。参数vbunicode 就是将 ansi字符串转成 vb的string.
SysAllocStringByteLen() 将 ansi字符串转成 bstr
注:ansi字符串 英文是单字节,中文是双字节。
2、vb/vba的string(bstr)与c/c++的wchar_T的转换问题
Vb/vba的string本身在内部使用unicode,c/c++的wchar_t就是unicode。
所以如果传给c/c++函数的wchar_t参数,直接 strptr( bstr) 就可以了。
接收 c/c++函数的返回值,一般常用SysAllocString() 将unicode字符串转成bstr
Sys字符串系列函数在oleaut32.dll中,这个系列函数在跨语言调用中,会经常用到。
不少的winapi.tlb类型库会有这个系列函数,引用tlb后,可直接调用。
3、数字类型的缺失。
Vb/vba的数字类型没有 ulong,uinteger(Ushort),longlong(64位vba有),Ulonglong。在某些场合下,会带来很多的不方便。特别是ulong和longlong。虽然有不少方法勉强可以迂回补救,但它确实是不直观。最糟糕的是,vb/vba的运算符无法重载,简直是一场灾难。
4、variant数据类型转换
要精细操作variant数据类型转换。转入variant,主要使用 oleaut32.dll的系列函数,variant转出,主要使用propsys.dll的系列函数。但一般我们不怎么需要使用到它。Vb/vba自身的转换函数足够用了。
5、vb的数组(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
|站长邮箱|小黑屋|手机版|Office中国/Access中国 ( 粤ICP备10043721号-1 )
GMT+8, 2024-12-23 23:56 , Processed in 0.078288 second(s), 18 queries .
Powered by Discuz! X3.3
© 2001-2017 Comsenz Inc.