在C中处理MacOSX上的BSTR

在C中处理MacOSX上的BSTR,c,macos,vba,bstr,C,Macos,Vba,Bstr,我已经用C编写了一些代码,用于在MacOSX动态库中从VBA调用C代码时转换从VBA传递的字符串。我得到了一些很好的提示,因为我只关心ASCII字符串,所以我编写了以下函数来将BSTR转换为简单的char*: #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include "myheader.h" size_t vbstrlen(BSTR *vbstr) { size_t len

我已经用C编写了一些代码,用于在MacOSX动态库中从VBA调用C代码时转换从VBA传递的字符串。我得到了一些很好的提示,因为我只关心ASCII字符串,所以我编写了以下函数来将
BSTR
转换为简单的
char*

#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;
}
。我使用了
uint16\u t
,因为MacOSX C编译器中有4字节(不是2字节)的wchar\u t。我在调用
vbstochar
以查看
convarg
的内容后添加了一个断点,从Excel调用时它似乎可以工作

这是可行的,但有一点我不明白,为什么我必须将
vbstrlen
函数中的
len
乘以2。我是C新手,所以我必须读一点指针——我想既然我的BSTR包含2个字节的字符,我应该得到正确的字符串长度,而不必乘以2?如果有人能给我解释一下,或者发布一个教程的链接,那就太好了


此外,我的带有字符串参数的函数在VBA中调用时可以工作,但只能在第一次调用之后工作。因此,当我第一次从动态库调用带有
BSTR*
参数的函数时(在我启动应用程序之后,在本例中是Excel),
BSTR*
指针只指向某个(随机?)地址,而不是字符串。当我第二次从VBA调用该函数时,一切正常-知道为什么会这样吗

VB字符串很可能是一个UTF-16字符串,每个字符使用2个字节(BMP、基本多语言平面或U+0000..U+FFFF以外的字符除外,它们被编码为代理项对)。因此,对于“ASCII”数据,将交替使用ASCII字符和零字节。“乘以2”是因为UTF-16使用两个字节来存储每个计数的字符

当我们看到:

typedef uint16_t OLECHAR;
typedef OLECHAR * BSTR;

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); 
} 
#包括
#包括
#包括
#包括“myheader.h”
尺寸(BSTR*vbstr)
{ 
如果(vbstr)
返回*((uint32_t*)vbstr)-1;
返回0;
} 
无效vbstochr(BSTR*vbstr,字符**out)
{ 
尺寸长度=vbstrlen(vbstr);
char str[len+1]={0};
对于(尺寸i=0;i
此代码无法编译。Post real code,warts和all.@HansPassant
vbstrlen
位,带有修剪非重要的
#include
,确实编译,但是,在动态库中编译并从excel 2011 VBA使用时,对任何VBA字符串保持返回0。(使用
typedef wchar\u t*BSTR;
)我在VBA中使用的动态库中测试了vbstrlen函数(在mac上使用excel 2011),它不断为“toto”字符串返回0。有什么想法吗?根据和,MacOSX上的Excel 2011 VBA似乎没有使用COM BSTR使用的相同内存格式与动态库交换字符串。它使用以null结尾的
char*
字符串。另外,一个棘手的部分是动态库无法分配输出字符串并将其返回给VBA,因为VBA无法正确释放它,因此您的VBA代码必须分配字符串并将其传递给动态库进行填充。关于棘手部分的一个问题是:在windows或windows上也是如此,VB/VBA和COM都使用
BSTR
字符串,并且
BSTR
是使用系统提供的分配器分配的,因此一个模块/进程可以分配内存(
SysAllocString()
等),这些内存可以使用另一个模块/进程中的同一分配器释放(
SysFreeString()
等)。这是COM编程的基础。是的,我知道,对不起,非常糟糕的措辞,我是说:<代码> SysLoSoScx< /Co> >和 SysFryStult是C++的函数,对吗?因此,我可以
SysAllocString
通过dll将字符串推送到vba,但我不会
SysFreeString
这样vba将负责windows上的内存释放。(你说在mac os x上是不可能的。)因为我不是专家,在vba的windows下,幕后是怎么做的,使它能够处理这个内存释放的?
#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); 
}