简体   繁体   中英

Pass wchar_t instead of BSTR causes E_OUTOFMEMORY error

I have a simple DirectShow library and i also have a client application that call functions from that library. The problem is that some functions accept BSTR as arguments, but i prefer to pass either std::string or char* . It is certain that if i use one of these types i'll get plenty of compiler errors.

I asked the reason and someone said that i can't use non of them, because they don't guarantee ABI unlike BSTR.

Also i tried wchar_t . I passed it to my function instead of BSTR. Surprisingly it didn't generate any compiler error:

std::string username = "john";
wchar_t wcstring[4] = {0};
MultiByteToWideChar(0, 0, username.c_str(), 4, wcstring, 4);

But when i call my function:

HRESULT hr = pFoo->myDummyFunction(wcstring);

And run the program, It returns E_OUTOFMEMORY as HRESULT. I'm curious to know why it happens.

If the method you are calling is declared as taking a BSTR parameter, then you must pass a real BSTR as the argument. There is no other choice.

I'm sorry it's not your preference. I agree there are all kinds of reasons why this is inconvenient. However trying to pass a wchar_t* instead of a BSTR is like trying to pass a std::vector<wchar_t> to a function that expects a std::wstring . Hopefully you will agree that that would be nonsense even if the compiler didn't stop you.

A BSTR is not just a sequence of characters; it's a variable-sized struct with very well defined semantics that happens to include a null-terminated sequence of wchar_t as it's main member.

What confuses you is that the compiler appears to allow them to be mixed. That is because the BSTR was designed so that, in some very limited scenarios, a BSTR object could be used like if it was just a wchar_t* . A BSTR can sometimes be used in place of a wchar_t*, but not the other way around .

That just means that the C++ compiler cannot tell the difference between a BSTR and a wchar_t*. In fact, the Windows headers define BSTR essentially as typedef wchar_t* BSTR so they are the same thing to the compiler, but the compiler doesn't know about the rest of the BSTR struct. That means the compiler cannot tell you when it's legal to use a BSTR and when it's legal to use a wchar_t*: it is your responsibility to pick the right one by yourself.

About BSTR :

Beyond the null-terminated sequence of characters at the core of the BSTR, a BSTR also has a size prefix stored before the first character ; and a BSTR must be allocated in a specific heap controlled by the COM infrastructure in Windows which is why it must only be created by calling SysAllocString() and similar functions.

Using a wchar_t* where you should be using a BSTR will lead to errors, crashes and undefined behavior depending on specifics. We can't know why specifically you got the E_OUTOFMEMORY error, but it's not hard to make a guess: the function probably tries to use the size prefix while trying to make a copy of the BSTR. Because you passed a wchar_t* instead of a BSTR, the prefix has garbage in it, likely an absurdly large number, and the function tries to use that large number to allocate huge amounts of memory for the copy, which fails.


If you want to use std::wstring or a std::string for your own code, go right ahead. I would do the same thing. But create a BSTR before calling that method, pass it as the argument, then free the BSTR and move on.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM