BSTR и VARIANT под... Mac OS X
Под Mac OS X у меня есть Office 2011 и его Excel и VBA, и у меня gcc-5.3.0 в g++.
Я много играл, передавая массивы (числовых встроенных типов) из VBA в dylib (расширение dll в Mac OS X), обновляя их и отправляя обратно в VBA, см., Например:
Передача c/ C++ функции dylib с указателем на VBA на Mac
Теперь я хотел бы сделать то же самое со строками, и сначала с одной строкой, а не массивом. Я хочу получить строку из VBA, изменить ее в C++ и отправить обратно, обновленный, в VBA.
Код на C++
сторона это:
#include <stdlib.h>
#include <ctype.h> //for toupper
extern "C"
{
void toupperfunc(char *vbstr)
{
size_t i = 0U;
char c;
do {
c = vbstr[i];
vbstr[i]=toupper(c);
++i;
} while(vbstr[i]!=0);
}
}
в файле thedylib.dylib
, скомпилирован следующим образом (Office 2011 для Mac OS X составляет 32 бита):
g++ -m32 -Wall -g -c ./thedylib.cpp
g++ -m32 -dynamiclib ./thedylib.o -o ./thedylib.dylib
тогда как на стороне VBA (в Excel 2011 для Mac OS X) у меня есть этот код:
Declare Sub toupperfunc Lib "/path/to/the/dylib/thedylib.dylib" (ByVal str As String)
Public Sub DoIt()
Dim str As String
str = "Ludwig von Mises"
Call toupperfunc(str)
MsgBox (str)
End Sub
и выполняя его, выдает "LUDWIG VON MISES", как и ожидалось.
Замечание 1. Обратите внимание на ByVal
перед строкой в подписи сабвуфера. Положить ByRef
вместо этого произойдет сбой во время выполнения. Еще более странно: представьте, я добавляю tolowerfunc
в моем dylib (тот же код, что и для toupperfunc
но полагаясь на ctype.h
"s tolower
Функция C++). Это также работает, как и ожидалось, но на этот раз, поставив ByRef
перед строкой в подписи вместо ByVal
больше не провоцирует сбой во время выполнения. Итак, есть ли что-то другое между функциями C++ toupper
а также tolower
? Если да, то? Чем объясняется такое поведение?
Замечание 2. Как следствие того, что то, что я описал выше, работает, мы теперь знаем, что Excel 2011 VBA на Mac OS X не обменивается строками с dylib, используя тот же формат в памяти, который использует COM BSTR. Он использует нулевое окончание char*
Строки вместо.
Принимая во внимание замечание 2 и поскольку "использование VARIANT под OS x" было моей долгосрочной целью, см., Например:
Передача VARIANT из Mac OS X Excel 2011 VBA в C++,
Я наконец подражал VARIANT
структура в VARIANT.h
файл, как вы можете видеть в следующем коде:
#ifndef VARIANT_H
#define VARIANT_H
#include <inttypes.h> // needed for gcc analogues of __int64 and unsigned __int64
typedef unsigned short VARTYPE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
// typedef __int64 LONGLONG;
typedef int64_t LONGLONG;
// typedef unsigned __int64 ULONGLONG;
typedef uint64_t ULONGLONG;
typedef long LONG;
typedef unsigned char BYTE;
typedef short SHORT;
typedef float FLOAT;
typedef double DOUBLE;
/* 0 == FALSE, -1 == TRUE */
typedef short VARIANT_BOOL;
/* For backward compatibility */
typedef bool _VARIANT_BOOL;
typedef LONG SCODE;
typedef unsigned long ULONG;
typedef unsigned short USHORT;
typedef unsigned long ULONG;
typedef char CHAR;
typedef unsigned char byte;
typedef int INT;
typedef unsigned int UINT;
typedef unsigned int * PUINT;
typedef union tagCY
{
struct _tagCY
{
ULONG Lo;
LONG Hi;
} DUMMYSTRUCTNAME;
LONGLONG int64;
} CY;
typedef double DATE;
/*#ifndef _MAC*/
//typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
typedef char WCHAR;
/*#else
// some Macintosh compilers don't define wchar_t in a convenient location, or define it as a char
typedef unsigned short WCHAR; // wc, 16-bit UNICODE character
#endif*/
typedef WCHAR OLECHAR;
typedef OLECHAR * BSTR;
typedef BSTR * LPBSTR;
typedef void * PVOID;
/*// #define POINTER_64 __ptr64
#define POINTER_64 unsigned long long*/
//typedef void *POINTER_64 PVOID64;
typedef struct tagSAFEARRAYBOUND
{
ULONG cElements;
LONG lLbound;
} SAFEARRAYBOUND;
typedef struct tagSAFEARRAYBOUND * LPSAFEARRAYBOUND;
typedef struct tagSAFEARRAY
{
USHORT cDims;
USHORT fFeatures;
ULONG cbElements;
ULONG cLocks;
PVOID pvData;
SAFEARRAYBOUND rgsabound[1];
} SAFEARRAY;
typedef SAFEARRAY * LPSAFEARRAY;
typedef struct tagDEC
{
USHORT wReserved;
union
{
struct
{
BYTE scale;
BYTE sign;
} DUMMYSTRUCTNAME;
USHORT signscale;
} DUMMYUNIONNAME1;
ULONG Hi32;
union
{
struct
{
ULONG Lo32;
ULONG Mid32;
} DUMMYSTRUCTNAME;
ULONGLONG Lo64;
} DUMMYUNIONNAME2;
} DECIMAL;
/*#define __tagVARIANT
#define __VARIANT_NAME_1
#define __VARIANT_NAME_2
#define __VARIANT_NAME_3
#define __tagBRECORD
#define __VARIANT_NAME_4*/
typedef /* [wire_marshal] */ struct tagVARIANT VARIANT;
struct tagVARIANT
{
union
{
struct __tagVARIANT
{
VARTYPE vt;
WORD wReserved1;
WORD wReserved2;
WORD wReserved3;
union
{
// non ptr stuff
LONGLONG llVal;
LONG lVal;
BYTE bVal;
SHORT iVal;
FLOAT fltVal;
DOUBLE dblVal;
VARIANT_BOOL boolVal;
// _VARIANT_BOOL bool;
SCODE scode;
CY cyVal;
DATE date;
BSTR bstrVal;
// ptr stuff
/*IUnknown*/ void *punkVal;
/*IDispatch*/ void *pdispVal;
SAFEARRAY * parray;
BYTE * pbVal;
SHORT * piVal;
LONG * plVal;
LONGLONG * pllVal;
FLOAT * pfltVal;
DOUBLE * pdblVal;
VARIANT_BOOL * pboolVal;
_VARIANT_BOOL * pbool;
SCODE * pscode;
CY * pcyVal;
DATE * pdate;
BSTR * pbstrVal;
/*IUnknown*/ void ** ppunkVal;
/*IDispatch*/ void ** ppdispVal;
SAFEARRAY ** pparray;
VARIANT * pvarVal;
PVOID byref;
CHAR cVal;
USHORT uiVal;
ULONG ulVal;
ULONGLONG ullVal;
INT intVal;
UINT uintVal;
DECIMAL * pdecVal;
CHAR * pcVal;
USHORT * puiVal;
ULONG * pulVal;
ULONGLONG * pullVal;
INT * pintVal;
UINT * puintVal;
struct __tagBRECORD
{
PVOID pvRecord;
/*IRecordInfo*/ void * pRecInfo;
} VARIANT_NAME_4;
} VARIANT_NAME_3;
} VARIANT_NAME_2;
DECIMAL decVal;
} VARIANT_NAME_1;
};
typedef VARIANT * LPVARIANT;
typedef VARIANT VARIANTARG;
typedef VARIANT * LPVARIANTARG;
#endif
Я придерживаюсь того, что сделано на стороне окон для всего, кроме BSTR
для которого я установил:
typedef char WCHAR;
typedef WCHAR OLECHAR;
typedef OLECHAR * BSTR;
(Я также вырезал чистый материал COM, а именно части кода, касающиеся IUnknown
а также IDispatch
.)
Определив этот "свет" VARIANT
структура, я хочу играть в ту же игру, что и для массивов double
и для string
то есть обмен VARIANT
находится между VBA и динамической библиотекой C++. Итак, я разработал этот код C++:
#include "/path/to/VARIANT.h"
#include <ctype.h>
VARTYPE getvt(const VARIANT & var_in)
{
return var_in.VARIANT_NAME_1.VARIANT_NAME_2.vt;
}
extern "C"
{
void updatevar(VARIANT * var_in_out, bool converttoupper)
{
VARTYPE vt = getvt(*var_in_out);
switch (vt)
{
case 3:
{
long l = (*var_in_out).VARIANT_NAME_1.VARIANT_NAME_2.VARIANT_NAME_3.lVal;
l *= 2L;
(*var_in_out).VARIANT_NAME_1.VARIANT_NAME_2.VARIANT_NAME_3.lVal = l;
}
break;
case 8024:
{
}
break;
case 8:
{
BSTR wc = (*var_in_out).VARIANT_NAME_1.VARIANT_NAME_2.VARIANT_NAME_3.bstrVal ;
int i = 0;
do
{
char c = wc[i];
wc[i]= converttoupper ? static_cast<char>(toupper(c)) : static_cast<char>(tolower(c));
++i;
} while(wc[i]!=0);
(*var_in_out).VARIANT_NAME_1.VARIANT_NAME_2.VARIANT_NAME_3.bstrVal = &wc[0];
}
break;
default:
{
return;
}
}
}
}
Путин thevarianttest.cpp
составлен следующим образом:
g++ -m32 -Wall -g -c ./thevarianttest.cpp
g++ -m32 -dynamiclib ./thevarianttest.o -o ./thevarianttest.dylib
и используется на стороне VBA (в Excel 2011 для Mac OS X) следующим образом:
Declare Sub updatevar Lib "/path/to/the/dylib/thevarianttest.dylib" (ByRef x As Variant, ByVal istoupper As Boolean)
Public Sub doit()
Dim x As Variant
Dim l As Long
l = -666
x = l
Call updatevar(x, True)
MsgBox (x)
Dim s as String
s = "Ludwig von Mises"
x = s
Call updatevar(x, False)
MsgBox (x)
s = "Ludwig von Mises"
x = s
Call updatevar(x, True)
MsgBox (x) 'FAILURE...
End Sub
Выполнение этого кода VBA теперь последовательно выдает: -1332 Людвиг фон Мизес Людвиг фон Мизес
первые два в порядке, но для последнего ожидалось "LUDWIG VON MISES"... И последнее в конечном итоге опирается на C++ toupper
функция, та же функция из предыдущего замечания 1, которая не позволяла мне ByRef
использовать в VBA в первом примере...
Так, что происходит? Почему это работает в первом примере, а не во втором?
Замечание 3. Обратите внимание на ByRef
напротив VARIANT
подпись саб. Положить ByVal
вместо этого провоцирует сбой во время выполнения... (Это противоположно тому, что происходило со строками и без вариантов в первом коде VBA.)
1 ответ
Начиная с первой попытки @Olorin, я создал библиотеку C++, которая позволяет обмениваться строками и массивами между VBA и C++, используя вариантный тип данных в качестве оболочки. Я также добавил дополнительные функции, такие как безопасность типов для массивов, семантика перемещения и т. д. Вот репо, если кому-то все еще интересно: