2010-09-20 32 views
2

我有一个实现IDispatch接口的类(JSObject)。该类暴露于在托管的Web浏览器控件(IWebBrowser2)中运行的JavaScript。从C++函数返回字符串为JavaScript

这里了解如何工作参见:Calling C++ function from JavaScript script running in a web browser control

我可以从我的JavaScript代码调用到JSObject,并且可以接收返回整数/多头。但是当函数返回一个字符串(BSTR)时会出错。

这是IDispatch::Invoke()代码的一部分:

int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", -1, 
    NULL, 0); 
BSTR bstrRet = SysAllocStringLen(0, lenW); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", -1, bstrRet, 
    lenW); 

pVarResult->vt = VT_BSTR; 
pVarResult->bstrVal = bstrRet; 

// Who calls SysFreeString(bstrRet);? 

与上面的代码,你可以alert()返回的字符串,但不能添加到它。 alert(returnedString + "foo");只会显示“返回的字符串”。 “foo”部分未添加到字符串中。不知怎的,字符串的结尾似乎有些问题。任何想法的人?我是不是在打SysFreeString()

编辑:

我暂时列入atlbase.h所以我可以使用CComBSTR。上面的代码现在看起来是这样的:

pVarResult->vt = VT_BSTR; 
pVarResult->bstrVal = CComBSTR("test string"); 

通过代码步进明确表明pVarResult是“测试字符串”一路,直到函数返回。但是,当我在JavaScript代码中alert()返回的字符串时,我得到“扩展”。 alert(returnedString + "foo")是“expandedfoo”。所以这是向正确方向迈出的一小步,因为您可以添加到返回的字符串中。但它也是在错误的方向作为返回串的步骤是不是我真的回来......

*pVarResult = CComVariant("test string"); 

该代码给出了相同的结果在前面的列表中(使用的CComBSTR)的代码。

+1

我没有看到它(用于ATL),但仅供参考:Out参数由调用者拥有,因此调用者可以释放该字符串。请参阅[内存管理规则](http://msdn.microsoft.com/en-us/library/ms810016.aspx)。 – 2010-09-20 21:22:50

+0

@Georg:太好了。所以至少我没有泄漏记忆。 – Tobbe 2010-09-20 21:59:20

回答

1

第一个MultiByteToWideChar()调用返回存储字符串所需的字符数量,包括空终止符。然后SysAllocStringLen()lenW+1个字符分配一个缓冲区(比需要的多一个)并且已经以null结尾。

由于MultiByteToWideChar()还写入了一个空终止符,所以最后在字符串的末尾会出现两个。对于BSTR,嵌入的空字符是可能的,因为它们的长度是前缀,所以JScript实现可能会连接而不删除附加的字符......因此,最终会得到一个字符串,其中间只有一个嵌入的空字符部分。

长话短说,固定长度:

lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, NULL, 0); 
bstrRet = SysAllocStringLen(0, lenW-1); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str, -1, bstrRet, lenW-1); 

正如在注释中,字符串是被调用者释放 - 在memory management rules决定了输出参数被调用者所有。

+0

所以,只是为了确保我得到这个。在最后的MultiByteToWideChar调用中,我们可以只使用lenW和lenW-1,对吧?对于lenW-1,我们不会复制str中的\ 0,并使用已经为我们提供的一个SysAllocStringLen。用lenW我们覆盖了用str创建的\ 0 SysAllocStringLen。正确? – Tobbe 2010-09-21 11:43:31

+0

@Tobbe:正确,用上面的代码'SysAllocStringLen()'为'lenW'字符分配一个缓冲区,并且你会覆盖最后一个空终止符。 – 2010-09-21 22:28:27

0

在一些有趣的事情下面调试代码变得明显。

int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "testing testing", -1, 
    NULL, 0); 
BSTR bstrRet = SysAllocStringLen(0, lenW); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "testing testing", -1, bstrRet, 
    lenW); 

BSTR bstrRet2 = SysAllocString(L"testing testing"); 

int len1 = SysStringByteLen(bstrRet); 
int len2 = SysStringByteLen(bstrRet2); 

len1为32 len2只是30 SysAllocString作品与JavaScript代码我有,另一种方法则没有。

看看分配bstrRet的内存,我可以看到它以0x00 0x00 0x00 0x00结尾,而bstrRet2只有0x00 0x00。所以我的猜测是,当使用bstrRet将其抛出时,会向JavaScript代码发送额外的空终止符。这就是为什么你不能附加任何东西。 bstrRet2没有额外的空终止符。

知道,在这个问题的原代码,可向工作,如果它的修改是这样的:

int lenW = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", -1, 
    NULL, 0) - 1; 
BSTR bstrRet = SysAllocStringLen(0, lenW); 
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, "Returned string", lenW*2, bstrRet, 
    lenW); 

pVarResult->vt = VT_BSTR; 
pVarResult->bstrVal = bstrRet; 

我不知道如果这样做lenW*2是安全的,但该代码似乎工作这么远在我所做的有限测试。