In Paulo Bueno's project https://github.com/buenop/MinXL he returns a Variant from a C++ dll, e.g.
Declare PtrSafe Function IncrementArrayBy _
Lib "/Library/Application Support/Microsoft/YourLibrary.dylib" _
(ByRef v as Variant, ByVal d as Double) As Variant
I would like to do the same, but in C. Here's is my reduced code so far:
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
At the moment both these functions are returning an Empty to VBA. This makes sense as if I look at the 16 bytes from answer the first few (i.e. the vt are 0), e.g. using this code.
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
Any ideas how to get this working? Is vba expecting a VARIANT **?
I don't really want to copy the data into a preallocated variant as I like the simplicity of returning a Variant on the stack.
Paulo confirmed he returns the variant on the stack, reducing the problem to, why does the following show up as 16 zeros?
VARIANT2 doubleInVariant(double x, VARIANT2 * out) {
VARIANT2 answer;
answer.vt = VT_R8;
answer.data.dblVal = x;
memcpy(out, &answer, sizeof(VARIANT2));
return answer;
}
and
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 pointed out that a variant is 24 bytes not 16 bytes. So changing the variant struct to:
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;
Everything now works - see https://github.com/coppertop-bones/jinni for the src code and an example spreadsheet.
Defining VARIANT with the extra padding that takes the struct from 16 bytes to 24 bytes works.
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;
I'm feeling a bit of a school boy but the results of a quick google do seem to suggest that 16 bytes is a common misconception. For anyone one encountering this I hope this helps.