Как эффективно скопировать BSTR в wchar_t []? - PullRequest
5 голосов
/ 16 сентября 2008

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

Ответы [ 5 ]

8 голосов
/ 16 сентября 2008

Во-первых, вам, возможно, вообще ничего не нужно делать, если все, что вам нужно сделать, это прочитать содержимое. Тип BSTR уже является указателем на массив wchar_t с нулевым символом в конце. Фактически, если вы проверите заголовки, вы обнаружите, что BSTR по существу определяется как:

typedef BSTR wchar_t*;

Таким образом, компилятор не может различить их, даже если они имеют разную семантику.

Есть два важных предостережения.

  1. BSTR должны быть неизменными. Вы никогда не должны изменять содержимое BSTR после его инициализации. Если вы «измените его», вам нужно создать новый, назначить новый указатель и освободить старый (если он у вас есть).
    [ ОБНОВЛЕНИЕ : это не так; извиняюсь! Вы можете изменить BSTR на месте; Я очень редко нуждался в этом.]

  2. 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 () в примере

5 голосов
/ 16 сентября 2008

BSTR-объекты содержат префикс длины, поэтому определение длины обходится дешево. Определите длину, выделите новый массив, достаточно большой для хранения результата, обработайте его и не забудьте освободить его, когда закончите.

4 голосов
/ 16 сентября 2008

Нет необходимости в конвертации. Указатель 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.

3 голосов
/ 16 сентября 2008

Следует иметь в виду, что строки BSTR могут и часто содержат встроенные нули. Ноль не означает конец строки.

0 голосов
/ 16 сентября 2008

Используйте ATL и CStringT, тогда вы можете просто использовать оператор присваивания. Или вы можете использовать макросы USES_CONVERSION, они используют выделение кучи, так что вы будете уверены, что не потеряете память.

...