在mac os x下我有办公室2011及其excel和VBA,我有gcc-5.3.0的g ++。
我为将数组(数值内置类型)从VBA传递到dylib(mac os x上的dll的扩展名)并更新它们并将它们发送回VBA做了很多,参见例如:
passing c/c++ dylib function taking pointer to VBA on 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 for mac os x是32位):
g++ -m32 -Wall -g -c ./thedylib.cpp
g++ -m32 -dynamiclib ./thedylib.o -o ./thedylib.dylib
而在VBA方面(在excel 2011 for 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
会在运行时产生崩溃。更奇怪的是:想象一下,我在我的dylib中添加tolowerfunc
(代码与toupperfunc
相同,但依赖于ctype.h
的{{1}} C ++函数)。它也可以按预期工作,但这一次,在签名中将tolower
放在字符串前面而不是ByRef
不会引起运行时崩溃。那么,C ++函数ByVal
和toupper
之间是否有不同之处?如果是这样,什么? 有什么可以解释这种行为?
备注2。作为上述内容可行的事实的推论,我们现在知道mac os x上的Excel 2011 VBA不会使用相同的内容与dylib交换字符串。 COM BSTR使用的内存格式。它使用以null结尾的tolower
字符串。
考虑到备注2和“在os x下使用VARIANT”是我的长期目标,请参阅:
Passing a VARIANT from mac OS X Excel 2011 VBA to c++,
我终于在char*
文件中模仿了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
之外,我坚持在Windows窗口所做的一切:
BSTR
(我还删除了纯粹的COM内容,即有关typedef char WCHAR;
typedef WCHAR OLECHAR;
typedef OLECHAR * BSTR;
和IUnknown
的部分代码。)
定义了这个“轻型”IDispatch
结构后,我想玩VARIANT
和double
的数组游戏,即交换{{ 1}}介于VBA和C ++动态库之间。所以我设计了这个C ++代码:
string
输入VARIANT
,编译如下:
#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;
}
}
}
}
并在VBA端(在excel 2011中用于mac os x)中使用如下:
thevarianttest.cpp
现在连续弹出执行此VBA代码: -1332 路德维希冯米塞斯 Ludwig von Mises
前两个是好的,但最后一个,“LUDWIG VON MISES”是预期的...最后一个最终依赖于C ++的g++ -m32 -Wall -g -c ./thevarianttest.cpp
g++ -m32 -dynamiclib ./thevarianttest.o -o ./thevarianttest.dylib
函数,与之前的注释1相同的函数不是允许我在第一个例子中使用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中使用...
那是怎么回事?为什么它在第一个例子中工作而在第二个例子中不再工作?
备注3. 请注意toupper
子签名前面的ByRef
。放置ByRef
会引发运行时崩溃...(这与第一个VBA代码中的字符串和没有变体的情况相反。)