V745. A 'wchar_t *' type string is incorrectly converted to 'BSTR' type string.
The analyzer has detected that a string of the wchar_t *
type is handled as a string of the BSTR type, which is strange, and the code is very likely to be incorrect. To figure out why such string handling is dangerous, let's first recall what the BSTR type is.
We will quote the article from MSDN. It is important to understand the danger behind errors of this type, and the V745 diagnostic rule indicates serious errors in most cases.
typedef wchar_t OLECHAR;
typedef OLECHAR * BSTR;
A BSTR (Basic string or binary string) is a string data type that is used by COM, Automation, and Interop functions. Use the BSTR data type in all interfaces that will be accessed from script.
- A length prefix. A four-byte integer that contains the number of bytes in the following data string. It appears immediately before the first character of the data string. This value does not include the terminal null character.
- A data string. A string of Unicode characters. May contain multiple nested null characters.
- A terminator. Two null characters.
A BSTR is a pointer that points to the first character of the data string, not to the length prefix.
BSTRs are allocated using COM memory allocation functions, so they can be returned from methods without the need to control memory allocation.
The following code is incorrect:
BSTR MyBstr = L"I am a happy BSTR";
This code builds (compiles and links) correctly, but it will not function properly because the string does not have a length prefix. If you use a debugger to examine the memory location of this variable, you will not see a four-byte length prefix preceding the data string.
Instead, use the following code:
BSTR MyBstr = SysAllocString(L"I am a happy BSTR");
A debugger that examines the memory location of this variable now reveals a length prefix containing 34. This is the expected value for a 17-byte single-character string that is converted to a wide-character string through the inclusion of the "L" string modifier. The debugger also shows a two-byte terminal null character (0x0000
) that appears after the data string.
If you pass a simple Unicode string as an argument to the COM function that is expecting a BSTR, the COM function will fail.
Note. The analyzer cannot accurately determine whether the error exists in the code. If an incorrect BSTR string is passed somewhere outside the code, it will cause a failure. However, if a BSTR string is cast back to wchar_t *
, it is fine.
Here is an example of such code:
wchar_t *wstr = Foo();
BSTR tmp = wstr;
wchar_t *wstr2 = tmp;
There's no real error here, but this code still "smells" and has to be fixed. Doing so will cause less confusion for future maintainers and will prevent the analyzer from issuing a warning. The correct data types should be used:
wchar_t *wstr = Foo();
wchar_t *tmp = wstr;
wchar_t *wstr2 = tmp;
We also recommend reading the sources mentioned at the end of the article. They will help understand BSTR
strings and how to cast them to other string types.
Here's another example:
wchar_t *wcharStr = L"123";
wchar_t *foo = L"12345";
int n = SysReAllocString(&wcharStr, foo);
This is the description of the SysReAllocString
function:
INT SysReAllocString(BSTR *pbstr, const OLECHAR *psz);
It allocates a new BSTR and copies the given string into it. Then, it frees the BSTR to which pbstr
points and places a pointer to the new BSTR at that address.
The function expects a pointer to a variable containing the address of a string in BSTR format as the first argument. Instead, it receives a pointer to an ordinary string. Since the wchar_t **
type is actually the same thing as BSTR *
, the code compiles correctly. In reality, however, it doesn't make sense and causes a runtime error.
The fixed version of the code:
BSTR wcharStr = SysAllocString(L"123");
wchar_t *foo = L"12345";
int n = SysReAllocString(&wcharStr, foo);
A special case is when the auto
keyword is used. The analyzer issues a warning for the following harmless code:
auto bstr = ::SysAllocStringByteLen(foo, 3);
ATL::CComBSTR value;
value.Attach(bstr); // Warning: V745
This is a false positive, but the analyzer is technically correct when issuing the warning. The bstr
variable is of the wchar_t *
type. When deducing the type of the auto
variable, the C++ compiler does not consider that the function returns a value of the BSTR
type. When deducing auto
, the BSTR
type is simply a synonym of whar_t *
. This means that the code above is equivalent to this:
wchar_t *bstr = ::SysAllocStringByteLen(foo, 3);
ATL::CComBSTR value;
value.Attach(bstr);
This is why the PVS-Studio analyzer issues the warning: it is not recommended to store a pointer to a BSTR
string in a standard wchar_t *
pointer. To eliminate the warning, explicitly specify the type instead of using auto
here:
BSTR *bstr = ::SysAllocStringByteLen(foo, 3);
ATL::CComBSTR value;
value.Attach(bstr);
This is an interesting case when the auto
operator does not help, but instead loses type information and makes things worse.
Another way to eliminate the warning is to use one of the false-positive suppression mechanisms described in the documentation.
References:
- MSDN. BSTR.
- StackOverfow. Static code analysis for detecting passing a wchar_t* to BSTR.
- StackOverfow. BSTR to std::string (std::wstring) and vice versa.
- Robert Pittenger. Guide to BSTR and CString Conversions.
This diagnostic is classified as:
You can look at examples of errors detected by the V745 diagnostic. |