Как эффективно скопировать BSTR в wchar_t[]?
У меня есть объект BSTR, который я хотел бы преобразовать для копирования в объект wchar__t. Хитрость в том, что длина объекта BSTR может составлять от нескольких килобайт до нескольких сотен килобайт. Есть ли эффективный способ копирования данных? Я знаю, что могу просто объявить массив wchar_t и всегда выделять максимально возможные данные, которые ему когда-либо понадобятся. Однако это будет означать выделение сотен килобайт данных для чего-то, что потенциально может потребовать всего несколько килобайт. Какие-либо предложения?
5 ответов
Во-первых, вам, возможно, вообще не нужно ничего делать, если все, что вам нужно сделать, это прочитать содержимое. Тип 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() в примере
BSTR-объекты содержат префикс длины, поэтому определение длины обходится дешево. Определите длину, выделите новый массив, достаточно большой для хранения результата, обработайте его и не забудьте освободить его, когда закончите.
Там никогда не нужно никаких преобразований. BSTR
указатель указывает на первый символ строки и заканчивается нулем. Длина сохраняется до первого символа в памяти. BSTR
s всегда 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
Строки могут и часто содержат встроенные нули. Нуль не означает конец строки.
Используйте ATL и CStringT, тогда вы можете просто использовать оператор присваивания. Или вы можете использовать макросы USES_CONVERSION, они используют выделение кучи, так что вы будете уверены, что не потеряете память.