寻求Mac原生库中替代DispCallFunc的VBA方法调用方案
Mac VBA调用ObjC方法指针的DispCallFunc替代方案
在Mac平台上,你可以使用**libffi库的ffi_call函数**作为Windows下DispCallFunc的替代,它支持动态调用任意函数指针,完全适配ObjC方法的调用需求。
步骤1:声明libffi相关类型与函数
首先需要在VBA中定义libffi的结构体和函数声明:
' libffi 类型定义 Private Type ffi_type size As Long alignment As Long type As Long elements As LongPtr End Type Private Type ffi_cif abi As Long nargs As Long argtypes As LongPtr rtype As LongPtr bytes As Long flags As Long End Type ' libffi 函数声明 Private Declare Function ffi_prep_cif Lib "/usr/lib/libffi.dylib" (ByVal cif As LongPtr, ByVal abi As Long, ByVal nargs As Long, ByVal rtype As LongPtr, ByVal argtypes As LongPtr) As Long Private Declare Sub ffi_call Lib "/usr/lib/libffi.dylib" (ByVal cif As LongPtr, ByVal fn As LongPtr, ByVal rvalue As LongPtr, ByVal avalues As LongPtr) ' ObjC 相关工具函数 Private Declare Function objc_lookUpClass Lib "/usr/lib/libobjc.A.dylib" (ByVal sClassName As String) As LongPtr Private Declare Function sel_registerName Lib "/usr/lib/libobjc.A.dylib" (ByVal sSelName As String) As LongPtr Private Declare Function class_getMethodImplementation Lib "/usr/lib/libobjc.A.dylib" (ByVal klass As LongPtr, ByVal sel As LongPtr) As LongPtr
步骤2:实现方法指针调用逻辑
以你示例中的NSRegularExpression为例,以下是完整的调用实现:
Sub CallObjCMethod() Dim lKlass As LongPtr Dim lSel As LongPtr Dim lMethod As LongPtr Dim cif As ffi_cif Dim argTypes(0 To 4) As LongPtr ' self, _cmd, pattern, options, error Dim args(0 To 4) As LongPtr Dim returnVal As LongPtr Dim strPattern As String Dim errorPtr As LongPtr ' 1. 获取类和选择器 lKlass = objc_lookUpClass("NSRegularExpression") lSel = sel_registerName("regularExpressionWithPattern:options:error:") lMethod = class_getMethodImplementation(lKlass, lSel) ' 2. 准备FFI调用的类型信息 Dim ffi_objc As LongPtr, ffi_sel As LongPtr, ffi_long As LongPtr ffi_objc = VarPtr(GetFFIObjCType()) ' ObjC对象/指针类型 ffi_sel = VarPtr(GetFFISelType()) ' 选择器类型 ffi_long = VarPtr(GetFFILongType()) ' 整数类型 ' 设置参数类型:[self, _cmd, NSString*, NSInteger, NSError**] argTypes(0) = ffi_objc argTypes(1) = ffi_sel argTypes(2) = ffi_objc argTypes(3) = ffi_long argTypes(4) = ffi_objc ' 指针的指针仍按指针类型处理 ' 初始化cif结构体(使用默认ABI适配ObjC) If ffi_prep_cif(VarPtr(cif), 0, 5, ffi_objc, VarPtr(argTypes(0))) <> 0 Then MsgBox "FFI初始化失败" Exit Sub End If ' 3. 准备参数值 strPattern = "^\d+$" args(0) = lKlass ' 类方法的self为类本身 args(1) = lSel args(2) = ObjCString(strPattern) ' 转换VBA字符串为NSString args(3) = 0 ' NSRegularExpressionOptions.None args(4) = VarPtr(errorPtr) ' 4. 调用方法指针 ffi_call VarPtr(cif), lMethod, VarPtr(returnVal), VarPtr(args(0)) ' 5. 处理返回值与错误 If errorPtr <> 0 Then ' 此处可添加错误处理逻辑 Else ' 使用returnVal指向的NSRegularExpression对象 End If End Sub ' 辅助函数:返回对应平台的ffi_type实例 Private Function GetFFIObjCType() As ffi_type With GetFFIObjCType .size = LenB(CLngPtr(0)) .alignment = .size .type = 1 ' FFI_TYPE_POINTER End With End Function Private Function GetFFISelType() As ffi_type GetFFISelType = GetFFIObjCType ' 选择器本质为指针类型 End Function Private Function GetFFILongType() As ffi_type With GetFFILongType .size = LenB(CLng(0)) .alignment = .size .type = 2 ' FFI_TYPE_LONG End With End Function ' 辅助函数:VBA字符串转NSString(示例框架,需用ffi_call实现实际调用) Private Function ObjCString(ByVal s As String) As LongPtr ' 可复用上述ffi调用逻辑,调用NSString的stringWithUTF8String:方法 ' 此处省略具体实现,需自行补充 End Function
关键说明
- ObjC调用约定:ObjC方法的前两个固定参数是
self(实例或类)和_cmd(选择器),调用时必须传入这两个参数。 - libffi的ABI选择:Mac平台下使用默认ABI(参数传0)即可兼容ObjC方法调用。
- 类型匹配:必须确保
ffi_type的size和alignment与Mac平台的实际类型一致(比如64位Mac上LongPtr为8字节)。
内容的提问来源于stack exchange,提问作者Sancarn
相关产品推荐
相关产品推荐

