BSTR и VARIANT под mac os x

Под mac os x у меня есть офис 2011 и его excel и VBA, и у меня есть g++ от gcc-5.3.0.

Я много играл в передачу массивов (числовых встроенных типов) из VBA в dylib (расширение dll для mac os x), обновлял их и отправлял обратно в VBA, см., например:

передача функции c/c++ dylib, принимающей указатель на VBA на Mac

Теперь я хотел бы сделать то же самое со строками, и сначала только с одной строкой, а не с массивом. Я хочу получить строку из VBA, изменить ее на С++ и отправить обратно, обновленную, в 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, скомпилированном следующим образом (офис 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

и при его выполнении появляется "ЛЮДВИГ ФОН МИЗЕС", как и ожидалось.

Примечание 1. Обратите внимание на ByVal перед строкой в ​​подписи подписчика. Установка вместо этого ByRef привела бы к сбою во время выполнения. Еще более странно: представьте, что я добавляю tolowerfunc в свою dylib (тот же код, что и для toupperfunc, но опираясь на функцию tolower C++ из ctype.h). Это также работает, как и ожидалось, но на этот раз размещение ByRef перед строкой в ​​подписи вместо ByVal больше не вызывает сбой во время выполнения. Итак, есть ли что-то другое между функциями С++ toupper и tolower? Если так, то ? Что объясняет такое поведение?

Примечание 2. Как следствие того факта, что то, что я описал выше, работает, теперь мы знаем, что Excel 2011 VBA на mac os x не обменивается строками с dylib, используя тот же формат в памяти, который COM BSTR использует. Вместо этого он использует строки char* с завершающим нулем.

Принимая во внимание замечание 2 и поскольку моей долгосрочной целью было «использование VARIANT под os x», см., например:

Передача ВАРИАНТА из mac OS X Excel 2011 VBA на С++,

Наконец, я сымитировал структуру 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, для которого я установил:

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 Людвиг фон Мизес Людвиг фон Мизес

первые два в порядке, но для последнего ожидалось "ЛЮДВИГ ФОН МИЗЕС"... И последний, в конечном счете, опирается на функцию toupper C++, ту же функцию из предыдущего примечания 1, которая не позволяла мне ByRef использовать в VBA в первом примере...

Так что же происходит? Почему в первом примере все работало, а во втором нет?

Примечание 3. Обратите внимание на ByRef перед VARIANT подписью подписи. Вместо этого вставка ByVal вызывает сбой во время выполнения... (Это противоположно тому, что происходило со строками и без вариантов в первом коде VBA.)


person Olorin    schedule 11.03.2016    source источник
comment
Я пока не знаю, относится ли мой комментарий к вашей проблеме, потому что анализ требует времени, и я не знаю, интересен ли еще кому-то ответ... Однако вы поняли, что количество символов сохраняется в более высоком адресе и содержимом строки в другом направлении? Если нет, и кто-то все еще заинтересован, то может присмотреться, чтобы помочь. Насколько я помню, указатель указывает непосредственно на строку данных. См. также docs.microsoft.com/en-us/ предыдущие версии/windows/desktop/   -  person Jörg Brüggmann    schedule 18.03.2021
comment
Еще один комментарий, чтобы понять расположение памяти: codeproject.com/Tips/726867/   -  person Jörg Brüggmann    schedule 18.03.2021
comment
@ Jörg'Wuwei'Brüggmann Да, я это понял ... Спасибо за ссылку   -  person Olorin    schedule 19.03.2021
comment
Это была проблема, @Olorin?   -  person Jörg Brüggmann    schedule 19.03.2021
comment
К сожалению, нет ... Я окончательно забыл о серьезной работе на Mac ...   -  person Olorin    schedule 19.03.2021