Search code examples
javascriptc++windowscomiwebbrowser2

Returning string to JavaScript from C++ function


I have a class (JSObject) that implements the IDispatch interface. The class is exposed to JavaScript running in my hosted web browser control (IWebBrowser2).

See more here about how this works: Calling C++ function from JavaScript script running in a web browser control

I can call in to JSObject from my JavaScript code, and I can receive returned integers/longs. But something goes wrong when the function returns a string (BSTR).

This is a part of the IDispatch::Invoke() code:

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);?

With the above code you can alert() the returned string, but you can not add to it. alert(returnedString + "foo"); will only show "Returned string". The "foo" part is not added to the string. There seems to be something wrong with the end of the string somehow. Any ideas anyone?

Also, am I leaking memory here since I'm not calling SysFreeString()?

EDIT:

I temporarily included atlbase.h so I could use CComBSTR. The above code now looks like this:

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

Stepping through that code definitely shows that pVarResult is "test string" all the way until the function returns. But when I alert() the returned string in my JavaScript code I get "expanded". alert(returnedString + "foo") is "expandedfoo". So it is a small step in the right direction as you can add to the returned string. But it's also a step in the wrong direction as the returned string isn't what I really returned...

*pVarResult = CComVariant("test string");

That code gives the same results as the code in the previous listing (using CComBSTR).


Solution

  • The first MultiByteToWideChar() call returns the amount of characters needed to store the string, including the null-terminator. Then SysAllocStringLen() allocates a buffer for lenW+1 characters (one more than needed) and already null-terminates it.

    As the MultiByteToWideChar() also writes a null-terminator, you end up with two at the end of the string. For BSTRs embedded null-characters are possible as they are length prefixed, so the JScript implementation probably concatenates without removing the additional one... thus you end up with a string with an embedded null-character in the middle which will only be printed partially.

    Long story short, fix the lengths:

    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);
    

    As mentioned in the comment, the string is to be freed by the caller - the memory management rules dictate that out-parameters are owned by the caller.