Возвращение строки в JavaScript из функции C ++ - PullRequest
2 голосов
/ 20 сентября 2010

У меня есть класс (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).

Ответы [ 2 ]

1 голос
/ 21 сентября 2010

Первый вызов MultiByteToWideChar() возвращает количество символов, необходимое для хранения строки, включая нулевой терминатор. Затем 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);

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

0 голосов
/ 21 сентября 2010

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

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

...