Возврат строки в 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).
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