Возврат строки в 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") - это "expandedfoo". Таким образом, это небольшой шаг в правильном направлении, поскольку вы можете добавить к возвращаемой строке. Но это также шаг в неверном направлении, поскольку возвращенная строка-это не то, что я действительно вернул...



*pVarResult = CComVariant("test string");


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

582   2  

2 ответов:

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

Так как MultiByteToWideChar() также записывает нуль-Терминатор, вы получаете два в конце строки. Для BSTRs встроенные нуль-символы возможны, поскольку они имеют префикс длины, поэтому реализация 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);
Как уже упоминалось в комментарии, строка должна быть освобождена вызывающим-правила управления памятью диктуют, что исходящие параметры принадлежат вызывающему.

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

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. Поэтому я предполагаю, что дополнительный нуль-Терминатор отправляется в код JavaScript при использовании bstrRet, который его сбрасывает. Вот почему вы ничего не можете добавить к нему. 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 безопасно, но этот код, кажется, работает так далеко в ограниченном тестировании, которое я сделал.

Comments

    Ничего не найдено.