2012-04-10 2 views
1

Я написал код в C для преобразования строк, переданных из VBA, когда код C вызывается из VBA из MacOSX dylib. Я получил несколько хороших советов here, и так как я забочусь только о ASCII строки я написал следующие функции преобразующие BSTR к простому char*:Обработка BSTR на MacOSX в C

#include <stdlib.h> 
#include <stdio.h> 
#include <stdint.h> 
#include "myheader.h" 

size_t vbstrlen(BSTR *vbstr) 
{ 
    size_t len = 0U; 
    while(*(vbstr++)) ++len; 
    len = len*2; 
    return len; 
} 

void vbstochr(BSTR *vbstr, char** out) 
{ 
    int len2 = vbstrlen(vbstr); 
    char str[len+1]; 

    int i; 

    for(i = 0; i < len; i++) 
    { 
     str[i] = (char) (((uint16_t*) vbstr)[i]); 
    } 

    str[i] = '\0'; 

    asprintf(out, str); 
} 

int test(BSTR *arg1) 
{ 
    char* convarg; 
    vbstochr(arg1, &convarg); 

    return 1; 
} 

myheader.h выглядит следующим образом:

typedef uint16_t OLECHAR; 
typedef OLECHAR * BSTR; 

. Я использовал uint16_t из-за 4 байтов (не 2 байта) wchar_t в компиляторе MacOSX C. Я добавил точку останова после вызова vbstochar, чтобы посмотреть содержимое convarg и, похоже, работает при вызове из Excel.

Так что это работает, но я не понимаю, почему я должен умножить свой len в функции vbstrlen на 2. Я новичок в C, поэтому мне пришлось немного почитать указатели - и я думал, так как мой BSTR содержит 2 байтовых символа, я должен получить нужную длину строки без необходимости умножать на два? Было бы здорово, если бы кто-нибудь мог объяснить это мне или опубликовать ссылку на учебник?

Кроме того, мои функции со строковыми аргументами работают при вызове в VBA, но только после первого вызова. Поэтому, когда я вызываю функцию с аргументом BSTR* из dylib в первый раз (после запуска приложения, Excel в этом случае), указатель BSTR* просто указывает на какой-то (случайный?) Адрес, но не на строку. Когда я вызываю функцию из VBA во второй раз, все работает отлично - любые идеи, почему это так?

+0

Этот код не может скомпилировать. Сообщение * реальный * код, бородавки и все. –

+0

@HansPassant бит 'vbstrlen', с обрезкой неживого' # include''s, компилируется, но компилируется в dylib и используется с excel 2011 VBA, возвращает 0 для любой строки VBA. (С 'typedef wchar_t * BSTR;'.) –

ответ

1

BSTR имеет встроенную длину, вам не нужно вручную вычислить длину.

Что касается необходимости умножения длины на 2, это связано с тем, что BSTR использует 2-байтовые символы, но char составляет всего 1 байт. Вы закодировали свою функцию vbstrlen(), чтобы вернуть количество байтов в BSTR, а не количество символов.

Поскольку вы заинтересованы только в ASCII-строк, можно упростить код следующим образом:

#include <stdlib.h> 
#include <stdio.h> 
#include <stdint.h> 
#include "myheader.h" 

size_t vbstrlen(BSTR *vbstr) 
{ 
    if (vbstr) 
     return *(((uint32_t*)vbstr)-1); 
    return 0; 
} 

void vbstochr(BSTR *vbstr, char** out) 
{ 
    size_t len = vbstrlen(vbstr); 
    char str[len+1] = {0}; 

    for(size_t i = 0; i < len; ++i) 
     str[i] = (char) vbstr[i]; 

    asprintf(out, str); 
} 
+0

Я протестировал вашу функцию vbstrlen в dylib, используемом в VBA (с excel 2011 на Mac), он возвращает 0 для строки «toto». Есть идеи ? –

+0

Согласно [этому] (http://www.codeproject.com/Articles/810282/Microsoft-Office-VBA-to-the-Macs) и [это] (http://stackoverflow.com/questions/9833808/ mac-office-2011-vba-and-dylib), кажется, что Excel 2011 VBA на MacOSX * не * обменивает строки с помощью dylib, используя тот же формат в памяти, который использует COM BSTR. Вместо этого он использует строки с символом «char *» с нулевым завершением.Кроме того, сложная часть состоит в том, что dylib не может выделить выходную строку и вернуть ее в VBA, поскольку VBA не освободит ее правильно, поэтому ваш код VBA должен будет выделить строку и передать ее в dylib для заполнения. –

+0

One вопрос о сложной части: это его тоже случай в окнах или? –

0

Вероятность того, что строка VB представляет собой строку UTF-16, которая использует 2 байта на символ (кроме символов вне BMP, Basic Multilingual Plane или U + 0000..U + FFFF, которые кодируются как суррогатные пар). Итак, для ваших данных «ASCII» у вас будут чередующиеся символы ASCII и нулевые байты. «Множество на 2» связано с тем, что UTF-16 использует два байта для хранения каждого счетного символа.

Это почти окончательное, когда мы видим:

typedef uint16_t OLECHAR; 
typedef OLECHAR * BSTR; 
Смежные вопросы