Возвращение строки в JavaScript из функции C ++

У меня есть класс (JSObject), который реализует интерфейс IDispatch. Класс работает с JavaScript, запущенным в моем размещенном веб-браузере (IWebBrowser2).

Узнайте больше о том, как это работает:Вызов функции C ++ из JavaScript-скрипта, запущенного в веб-браузере

Я могу позвонить в JSObject из своего кода JavaScript и получить возвращенные целые / длинные числа. Но что-то идет не так, когда функция возвращает строку (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(returnedString + "foo") это "расширенный foo". Так что это небольшой шаг в правильном направлении, поскольку вы можете добавить к возвращаемой строке. Но это также шаг в неправильном направлении, поскольку возвращаемая строка - это не то, что я действительно вернул ...

*pVarResult = CComVariant("test string");

Этот код дает те же результаты, что и код в предыдущем листинге (с использованием CComBSTR).

 Tobbe20 сент. 2010 г., 23:59
@ Георг: Отлично. Так что, по крайней мере, у меня нет утечки памяти.
 Georg Fritzsche20 сент. 2010 г., 23:22
Я не вижу его (используется для ATL), но для справки: Out-параметры принадлежат вызывающей стороне, таким образом вызывающая сторона освобождает строку. Увидетьправила управления памятью здесь.

Ответы на вопрос(2)

Отладка кода ниже несколько интересных вещей становится очевидным.

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 32len2 только 30SysAllocString работает с кодом 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 безопасно, но этот код, кажется, работает так далеко в ограниченном тестировании, которое я сделал.

Решение Вопроса

ПервыйMultiByteToWideChar() call возвращает количество символов, необходимое для хранения строки, включая нулевой терминатор. затемSysAllocStringLen() выделяет буфер дляlenW+1 символов (на один больше, чем нужно) и уже завершает его нулем.

Как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);

Как уже упоминалось в комментарии, вызывающая сторона должна освободить строку -правила управления памятью диктовать, что out-параметры принадлежат вызывающей стороне.

 Georg Fritzsche22 сент. 2010 г., 00:28
@Tobbe: Правильно, с кодом вышеSysAllocStringLen() выделяет буфер дляlenW символы, и вы перезаписали бы последний, нулевой терминатор.
 Tobbe21 сент. 2010 г., 13:43
Итак, просто чтобы убедиться, что я понял это. В последнем вызове MultiByteToWideChar мы могли использовать как lenW, так и lenW-1, верно? С lenW-1 мы не копируем \ 0 в str и используем тот, который SysAllocStringLen уже предоставил нам. С помощью всего лишь lenW мы перезаписываем \ 0 SysAllocStringLen, созданный с помощью str. Правильный?

Ваш ответ на вопрос