﻿# V745\. A 'wchar\_t \*' type string is incorrectly converted to 'BSTR' type string\.

Анализатор обнаружил, что со строкой типа wchar\_t \* начинают работать как со строкой типа BSTR\. Это очень подозрительно, и код, скорее всего, содержит ошибку\. Чтобы лучше понять, в чем заключается опасность, сначала вспомним, что такое BSTR\.

Далее мы процитируем [статью с сайта MSDN](https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms221069%28v=vs.85%29.aspx)\. Важно понять, в чём проблема, так как предупреждение V745 часто сигнализирует о серьёзных ошибках\.

```cpp
typedef wchar_t OLECHAR;
typedef OLECHAR * BSTR;
```

BSTR \(basic string или binary string\) — это строковый тип данных, который используется в COM, Automation и Interop\-функциях\. Тип BSTR следует использовать во всех интерфейсах\. Представление BSTR:

1. **Префикс длины**\. Целое число размером 4 байта, которое отображает длину следующей за ним строки в байтах\. Префикс длины указывается непосредственно перед первым символом строки и не учитывает символ\-ограничитель\.
1. **Строка данных**\. Строка символов в кодировке Unicode\. Может содержать множественные вложенные нулевые символы\.
1. **Ограничитель**\. Два нулевых символа\.

Тип BSTR является указателем, который указывает на первый символ строки, а не на префикс длины\.

Память для BSTR\-строк выделяется с помощью функций выделения памяти COM, поэтому они могут возвращаться методами без необходимости контроля над выделением памяти\.

Представленный ниже код является неправильным:

```cpp
BSTR MyBstr = L"I am a happy BSTR";
```

Данный пример собирается \(компилируется и линкуется\) успешно, но не будет работать должным образом, поскольку у строки отсутствует префикс длины\. Если проверить расположение в памяти данной переменной с помощью отладчика, он покажет отсутствие префикса длины размером 4 байта перед началом строки данных\.

Правильный вариант кода должен выглядеть так:

```cpp
BSTR MyBstr = SysAllocString(L"I am a happy BSTR");
```

Теперь отладчик покажет наличие префикса длины, который равен значению 34\. Оно соответствует 17 символам, которые приводится к wide\-character строке с помощью строкового модификатора `L`\. Отладчик также покажет двухбайтовый символ\-ограничитель \(`0x0000`\) в конце строки\.

Если передать простую Unicode\-строку в качестве аргумента функции COM, которая ожидает BSTR\-строку, произойдёт сбой в работе этой функции\.

**Примечание**\. Анализатор не может точно предсказать, есть в коде настоящая ошибка или нет\. Если неправильная BSTR\-строка передаётся куда\-то вовне, то произойдёт сбой\. Если же BSTR\-строка превращается обратно в `wchar_t *`, то всё хорошо\. 

Речь идет о подобном коде:

```cpp
wchar_t *wstr = Foo();
BSTR tmp = wstr;
wchar_t *wstr2 = tmp;
```

Здесь нет настоящей ошибки, но это "код с запахом", который следует поправить\. Так он вызовет меньше недоумения у программиста, сопровождающего код, и анализатор не будет выдавать предупреждение\. Следует использовать правильные типы данных:

```cpp
wchar_t *wstr = Foo();
wchar_t *tmp = wstr;
wchar_t *wstr2 = tmp;
```

Рекомендуем также ознакомиться со ссылками, приведёнными в конце статьи\. Они помогут разобраться с BSTR\-строками и тем, как их можно конвертировать в строки других типов\.

Рассмотрим ещё один пример:

```cpp
wchar_t *wcharStr = L"123";
wchar_t *foo = L"12345";
int n = SysReAllocString(&wcharStr, foo);
```

Описание функции `SysReAllocString`:

```cpp
INT SysReAllocString(BSTR *pbstr, const OLECHAR *psz);
```

Она выделяет новую BSTR и копирует в неё заданную строку, затем освобождает BSTR, на которую указывает `pbstr`, и помещает по этому адресу указатель на новую BSTR\.

В качестве первого аргумента функция ожидает указатель на переменную, содержащую адрес строки в формате BSTR\. Но вместо этого ей передают указатель на обыкновенную строку\. Так как тип `wchar_t **` с точки зрения компилятора — то же самое, что и `BSTR *`, то этот код успешно компилируется\. Но на практике он не имеет смысла и приведёт к ошибке на этапе исполнения\.

Правильный вариант кода:

```cpp
BSTR wcharStr = SysAllocString(L"123");
wchar_t *foo = L"12345";
int n = SysReAllocString(&wcharStr, foo);
```

Дополнительно рассмотрим ситуацию, когда используется ключевое слово [`auto`](https://en.cppreference.com/w/cpp/language/auto)\. Анализатор выдаёт предупреждение на следующий безобидный код:

```cpp
auto bstr = ::SysAllocStringByteLen(foo, 3);
ATL::CComBSTR value;
value.Attach(bstr);  // Warning: V745
```

Это ложное срабатывание, но формально анализатор прав, выдавая предупреждение\. Переменная `bstr` имеет тип `wchar_t *`\. Компилятор языка C\+\+ при выведении типа для auto\-переменной не учитывает, что функция возвращает значение типа `BSTR`\. При выведении `auto`, тип `BSTR` — это просто синоним `whar_t *`\. Получается, что написанный код эквивалентен:

```cpp
wchar_t *bstr = ::SysAllocStringByteLen(foo, 3);
ATL::CComBSTR value;
value.Attach(bstr);
```

Поэтому анализатор PVS\-Studio и выдаёт предупреждение, так как не рекомендуется хранить указатель на `BSTR` строку в обыкновенном `wchar_t *` указателе\. Чтобы устранить предупреждение, следует отказаться в данном месте от `auto` и написать тип явно:

```cpp
BSTR *bstr = ::SysAllocStringByteLen(foo, 3);
ATL::CComBSTR value;
value.Attach(bstr);
```

Это интересный случай, когда оператор `auto` не помогает, а наоборот — теряет информацию о типе и ухудшает ситуацию\. 

Другой вариант устранить предупреждение — использовать один из механизмов подавления ложных срабатываний, описанных в документации\.

**Дополнительные ссылки:**

1. MSDN\. [BSTR](https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms221069%28v=vs.85%29.aspx)\.
1. StackOverfow\. [Static code analysis for detecting passing a wchar\_t\* to BSTR](https://stackoverflow.com/questions/8986115/static-code-analysis-for-detecting-passing-a-wchar-t-to-bstr)\.
1. StackOverfow\. [BSTR to std::string \(std::wstring\) and vice versa](https://stackoverflow.com/questions/6284524/bstr-to-stdstring-stdwstring-and-vice-versa)\.
1. Robert Pittenger\. [Guide to BSTR and CString Conversions](https://www.codeproject.com/articles/4829/guide-to-bstr-and-c-string-conversions)\.