从 c dll 返回变体到 macOS 上的 Excel VBA

问题描述 投票:0回答:1

在 Paulo Bueno 的项目中 https://github.com/buenop/MinXL 他从 C++ dll 返回一个变体,例如

Declare PtrSafe Function IncrementArrayBy _
Lib "/Library/Application Support/Microsoft/YourLibrary.dylib" _
(ByRef v as Variant, ByVal d as Double) As Variant 

我想做同样的事情,但在 C 中。这是我到目前为止的简化代码:

typedef uint16_t VARTYPE;  
typedef uint16_t WORD; 
typedef double DOUBLE;

typedef struct {
    VARTYPE vt;
    WORD    wReserved1;
    WORD    wReserved2;
    WORD    wReserved3;
    union {
        DOUBLE       dblVal;
    } data;
} VARIANT2;

enum VARENUM {
    VT_DOUBLE       = 0x0005,
};

VARIANT2 * new_VT_DOUBLE_PTR(double x) {
    VARIANT2 *answer = (VARIANT2 *) malloc(sizeof(VARIANT2));
    answer->vt = VT_DOUBLE;
    answer->data.dblVal = x;
    return answer;
}

VARIANT2 new_VT_DOUBLE(double x) {
    VARIANT2 answer;
    answer.vt = VT_DOUBLE;
    answer.data.dblVal = x;
    return answer;
}
Private Declare PtrSafe Function jinni_new_VT_DOUBLE _
Lib "/Library/Application Support/Microsoft/jinni.dylib" Alias "new_VT_DOUBLE" _
(ByVal x As Double) As Variant
Private Declare PtrSafe Function jinni_new_VT_DOUBLE_PTR _
Lib "/Library/Application Support/Microsoft/jinni.dylib" Alias "new_VT_DOUBLE_PTR" _
(ByVal x As Double) As Variant

Function newDoublePtr(x As Double) As Variant
    newDoublePtr = jinni_new_VT_DOUBLE_PTR(x)
End Function

Function newDouble(x As Double) As Variant
    newDouble = jinni_new_VT_DOUBLE(x)
End Function

目前这两个函数都向 VBA 返回一个 Empty。这是有道理的,就好像我查看前几个答案中的 16 个字节(即 vt 为 0),例如使用此代码。

Private Declare PtrSafe Sub memcpy _
Lib "/usr/lib/libSystem.dylib" _
(ByVal dest As LongPtr, ByVal src As LongPtr, ByVal count As Long)

Function peekMem(ptr As LongPtr, size As Long) As Byte()
    ReDim answer(1 To size) As Byte
    memcpy VarPtr(answer(1)), ptr, size
    peekMem = answer
End Function

Sub testVar()
    Dim fred As Variant, joe As Variant, p As LongPtr

    fred = 1#
    joe = peekMem(VarPtr(fred), 16)

    p = jinni_new_VT_DOUBLE_PTR2(5#)
    joe = peekMem(VarPtr(fred), 16)   ' this has the correct values is it

    fred = jinni_new_VT_DOUBLE_PTR(5#)
    joe = peekMem(VarPtr(fred), 16)   ' but this doesn't

    fred = jinni_new_VT_DOUBLE(5#)
    joe = peekMem(VarPtr(fred), 16)   ' and neither does this

End Sub

任何想法如何让它工作? vba 是否需要 VARIANT **?

我真的不想将数据复制到预分配的变体中,因为我喜欢在堆栈上返回变体的简单性。


Paulo 确认他返回堆栈上的变体,将问题简化为,为什么下面显示为 16 个零?

VARIANT2 doubleInVariant(double x, VARIANT2 * out) {
    VARIANT2 answer;
    answer.vt = VT_R8;
    answer.data.dblVal = x;
    memcpy(out, &answer, sizeof(VARIANT2));
    return answer;
}

Private Declare PtrSafe Function jinni_doubleInVariant _
Lib "/Library/Application Support/Microsoft/jinni.dylib" Alias "doubleInVariant" _
(ByVal x As Double, ByRef cpy As Variant) As Variant


Paulo 指出变体是 24 字节而不是 16 字节。因此,将变体结构更改为:

typedef struct {
    VARTYPE vt;
    WORD    wReserved1;
    WORD    wReserved2;
    WORD    wReserved3;
    union {
        DOUBLE       dblVal;
    } data;
     WORD padding[4];                // due to __VARIANT_NAME_4 in the tagVARIANT struct
} VARIANT2;

现在一切正常 - 请参阅 https://github.com/coppertop-bones/jinni 获取 src 代码和示例电子表格。

c excel vba macos variant
1个回答
0
投票

用额外的填充定义 VARIANT 可以使结构从 16 字节变为 24 字节。

typedef struct {
    VARTYPE vt;
    WORD    wReserved1;
    WORD    wReserved2;
    WORD    wReserved3;
    union {
        DOUBLE       dblVal;
    } data;
     WORD padding[4];                // due to __VARIANT_NAME_4 in the tagVARIANT struct
} VARIANT2;
© www.soinside.com 2019 - 2024. All rights reserved.