You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 22:17:42