У меня есть объект BSTR, который я хотел бы преобразовать для копирования в объект wchar__t. Сложность заключается в том, что длина объекта BSTR может составлять от нескольких килобайт до нескольких сотен килобайт. Есть ли эффективный способ копирования данных? Я знаю, что могу просто объявить массив wchar_t и всегда выделять максимально возможные данные, которые ему когда-либо понадобятся. Однако это означало бы выделение сотен килобайт данных для чего-то, для чего потенциально может потребоваться всего несколько килобайт. Какие-либо предложения?





Объекты BSTR содержат префикс длины, поэтому определение длины обходится недорого. Определите длину, выделите новый массив, достаточно большой, чтобы вместить результат, обработайте его и не забудьте освободить его, когда закончите.
Используйте ATL и CStringT, тогда вы можете просто использовать оператор присваивания. Или вы можете использовать макросы USES_CONVERSION, они используют выделение кучи, поэтому вы можете быть уверены, что у вас не будет утечки памяти.
Нет необходимости в преобразовании. Указатель BSTR указывает на первый символ строки и заканчивается нулем. Длина сохраняется перед первым символом в памяти. BSTR всегда использует Unicode (UTF-16 / UCS-2). На каком-то этапе существовало нечто, называемое «ANSI BSTR» - в устаревших API есть некоторые ссылки, но вы можете игнорировать их в текущей разработке.
Это означает, что вы можете безопасно передать BSTR любой функции, ожидающей wchar_t.
В Visual Studio 2008 вы можете получить ошибку компилятора, потому что BSTR определен как указатель на unsigned short, а wchar_t - это собственный тип. Вы можете использовать или отключить соответствие wchar_t с /Zc:wchar_t.
Я считаю, что эта операция всегда безопасна, но не всегда может дать ожидаемый результат. BSTR может содержать нулевые символы в своем теле (отсюда и префикс длины), тогда как функция, ожидающая wchar_t *, будет интерпретировать первый нулевой символ как конец строки.
Вы не можете «безопасно передать BSTR любой функции, ожидающей wchar_t *». Сравните SysStringLen (NULL) и wcslen (NULL).
Просто чтобы расширить комментарий Константина - BSTR может действительно быть NULL, что определенный эквивалентно пустой строке (""). Напротив, большинство функций, ожидающих wchar_t * явно не будет, обрабатывают NULL так же, как указатель на пустую строку ...
Следует иметь в виду, что строки BSTR могут содержать и часто содержат встроенные нули. Нулевое значение не означает конец строки.
Во-первых, возможно, вам вообще ничего не придется делать, если все, что вам нужно сделать, это прочитать содержимое. Тип BSTR уже является указателем на массив wchar_t с завершающим нулем. Фактически, если вы проверите заголовки, вы обнаружите, что BSTR по существу определяется как:
typedef BSTR wchar_t*;
Таким образом, компилятор не может их различить, даже если они имеют разную семантику.
Есть два важных нюанса.
Предполагается, что BSTR неизменяемы. Вы никогда не должны изменять содержимое BSTR после его инициализации. Если вы «измените его», вам придется создать новый, назначить новый указатель и освободить старый (если он у вас есть).
[ОБНОВИТЬ: это неправда; Извините! Вы можете изменить BSTR на месте; У меня очень редко была потребность.]
BSTR могут содержать встроенные нулевые символы, тогда как традиционные строки C / C++ - нет.
Если у вас есть достаточный контроль над источником BSTR и вы можете гарантировать, что BSTR не имеет встроенных NULL, вы можете читать из BSTR, как если бы это был wchar_t, и использовать обычные строковые методы (wcscpy и т. д.) Для получить доступ к нему. В противном случае ваша жизнь станет тяжелее. Вам всегда придется манипулировать своими данными как с другими BSTR, так и с динамически выделяемым массивом wchar_t. Большинство функций, связанных со строками, не будут работать правильно.
Предположим, вы контролируете свои данные или не беспокоитесь о NULL. Предположим также, что вам действительно нужно сделать копию, и вы не можете просто прочитать существующий BSTR напрямую. В этом случае вы можете сделать что-то вроде этого:
UINT length = SysStringLen(myBstr); // Ask COM for the size of the BSTR
wchar_t *myString = new wchar_t[lenght+1]; // Note: SysStringLen doesn't
// include the space needed for the NULL
wcscpy(myString, myBstr); // Or your favorite safer string function
// ...
delete myString; // Done
Если вы используете оболочки классов для своего BSTR, оболочка должна иметь возможность вызывать для вас SysStringLen (). Например:
CComBString use .Length();
_bstr_t use .length();
ОБНОВИТЬ: Хорошая статья на эту тему написана кем-то гораздо более осведомленным, чем я:
"Полное руководство Эрика [Липперта] по семантике BSTR"
ОБНОВИТЬ: в примере заменена strcpy () на wcscpy ()
AFAIK, BSTR должны быть неизменными нет. Вот почему они не объявлены как const *.
Хммм ... Я не могу найти никаких ссылок, подтверждающих мою позицию. О чем я только думал? Я исправлю это.
не следует ли вам использовать wcscpy вместо strcpy?
@ arolson101 (на wcscpy): вы, конечно, правы. Спасибо, что заметили мою ошибку.
wchar_t не может быть точно такого же размера, как короткий.