В проекте Пауло Буэно https://github.com/buenop/MinXL он возвращает Variant из dll C++, например.
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
На данный момент обе эти функции возвращают Empty в VBA. Это имеет смысл, как будто я смотрю на 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 ожидает ВАРИАНТ **?
Я действительно не хочу копировать данные в предварительно выделенный вариант, так как мне нравится простота возврата варианта в стек.
Пауло подтвердил, что он возвращает вариант в стек, сводя проблему к тому, почему следующее отображается как 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
Пауло указал, что вариант равен 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 и пример электронной таблицы.
TwinBASIC не работает на macos
Верно, но создать стандартную dll в twinBasic очень просто. Поэтому, если функциональность вашего кода на C можно воспроизвести в twinBasic, это может быть более простым решением.
Определение 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;
Я чувствую себя немного школьником, но результаты быстрого поиска в Google, кажется, предполагают, что 16 байтов - это распространенное заблуждение. Для тех, кто сталкивается с этим, я надеюсь, что это поможет.
Вы не говорите, почему вы используете c. Возможно, вы сможете добиться желаемого, изменив свой код C на twinBasic (twinbasic.com/preview.html).