80

While working with COM in C++ the strings are usually of BSTR data type. Someone can use BSTR wrapper like CComBSTR or MS's CString. But because I can't use ATL or MFC in MinGW compiler, is there standard code snippet to convert BSTR to std::string (or std::wstring) and vice versa?

Are there also some non-MS wrappers for BSTR similar to CComBSTR?

Update

Thanks to everyone who helped me out in any way! Just because no one has addressed the issue on conversion between BSTR and std::string, I would like to provide here some clues on how to do it.

Below are the functions I use to convert BSTR to std::string and std::string to BSTR respectively:

std::string ConvertBSTRToMBS(BSTR bstr)
{
    int wslen = ::SysStringLen(bstr);
    return ConvertWCSToMBS((wchar_t*)bstr, wslen);
}

std::string ConvertWCSToMBS(const wchar_t* pstr, long wslen)
{
    int len = ::WideCharToMultiByte(CP_ACP, 0, pstr, wslen, NULL, 0, NULL, NULL);

    std::string dblstr(len, '\0');
    len = ::WideCharToMultiByte(CP_ACP, 0 /* no flags */,
                                pstr, wslen /* not necessary NULL-terminated */,
                                &dblstr[0], len,
                                NULL, NULL /* no default char */);

    return dblstr;
}

BSTR ConvertMBSToBSTR(const std::string& str)
{
    int wslen = ::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
                                      str.data(), str.length(),
                                      NULL, 0);

    BSTR wsdata = ::SysAllocStringLen(NULL, wslen);
    ::MultiByteToWideChar(CP_ACP, 0 /* no flags */,
                          str.data(), str.length(),
                          wsdata, wslen);
    return wsdata;
}
7
  • 2
    Is there a reason you're using std::string instead of std::wstring on Windows? Commented Jun 8, 2011 at 20:10
  • Yes, std::wstring will go well too! Question updated, thanks for your comment on this! Commented Jun 8, 2011 at 20:21
  • Why is there a wstring on windows? Commented Apr 13, 2014 at 22:02
  • 1
    @notbad.jpeg : Because in VC++ wchar_t is two bytes and Windows' native encoding is USC-2 (or UTF-16LE depending on who you ask). So, wstring is the natural way to hold a string without converting to/from the native encoding on Windows. Commented Jul 2, 2014 at 20:39
  • 4
    Windows' native encoding switched from UCS-2 to UTF-16LE in Windows 2000. There is no "depending on who you ask" about it. Commented Sep 10, 2015 at 1:53

4 Answers 4

108

BSTR to std::wstring:

// given BSTR bs
assert(bs != nullptr);
std::wstring ws(bs, SysStringLen(bs));

 
std::wstring to BSTR:

// given std::wstring ws
assert(!ws.empty());
BSTR bs = SysAllocStringLen(ws.data(), ws.size());

Doc refs:

  1. std::basic_string<typename CharT>::basic_string(const CharT*, size_type)
  2. std::basic_string<>::empty() const
  3. std::basic_string<>::data() const
  4. std::basic_string<>::size() const
  5. SysStringLen()
  6. SysAllocStringLen()
Sign up to request clarification or add additional context in comments.

@ildjam I'm sorry if this is a rookie question, but I started doing "go to definition" on BSTR (you'll need to run VS as admin) and BSTR seems to be no more than a wchar_t*. On the other hand, I also found Microsoft's documentation which, as you say, says that this constructor can handle embedded null characters. How can this constructor find the length of a BSTR if all it contains is a pointer to wchar_t?
@HerrKaputt : Because BSTRs are allocated on a special heap that retains the length of the allocation, and allows that length to be queried given a BSTR.
NULL is a valid state for BSTR, it is equivalent to an empty string. So the code should perhaps be std::wstring(bs ? bs : L"");
This is not about personal preference: NULL is a valid BSTR, and your conversion code needs to be prepared for it. This is part of the documented contract for BSTRs. Otherwise you'll have to add a disclaimer, saying: "Converts most BSTRs to std::wstring!"
There is no confusion about semantics. A NULL BSTR is semantically identical to an empty BSTR. That maps easily to a std::wstring. Constructing an empty std::wstring is the single correct and obvious conversion for a NULL BSTR.
13

You could also do this

#include <comdef.h>

BSTR bs = SysAllocString("Hello");
std::wstring myString = _bstr_t(bs, false); // will take over ownership, so no need to free

or std::string if you prefer

EDIT: if your original string contains multiple embedded \0 this approach will not work.

This answer is not correct and will give you incorrect results if your string contains NULL characters.
In that case it is a problem with _bstr_t although I have never had any problems with that.
It's only a problem of _bstr_t in so far that it's a bit missleading. It just implicitly casts to wchar_t*. It never claims that that points to a null-terminated string. It's when you pass that value to the std::wstring constructor that you create the problem. It's just as wrong as std::wstring otherstring = something(); std::wstring mystring = otherstring.c_str().
The example I gave is not wrong, however as you point out if the BSTR contains multiple \0 it will not work. No surprise there, the worst that can happen is a truncated string.
12

There is a c++ class called _bstr_t. It has useful methods and a collection of overloaded operators.

For example, you can easily assign from a const wchar_t * or a const char * just doing _bstr_t bstr = L"My string"; Then you can convert it back doing const wchar_t * s = bstr.operator const wchar_t *();. You can even convert it back to a regular char const char * c = bstr.operator char *(); You can then just use the const wchar_t * or the const char * to initialize a new std::wstring oe std::string.

Good answer! std::string str = _bstr_t(theBSTR); Simples!
4

Simply pass the BSTR directly to the wstring constructor, it is compatible with a wchar_t*:

BSTR btest = SysAllocString(L"Test");
assert(btest != NULL);
std::wstring wtest(btest);
assert(0 == wcscmp(wtest.c_str(), btest));

Converting BSTR to std::string requires a conversion to char* first. That's lossy since BSTR stores a utf-16 encoded Unicode string. Unless you want to encode in utf-8. You'll find helper methods to do this, as well as manipulate the resulting string, in the ICU library.

That truncates any strings containing null characters, even though BSTR and std::wstring can both store them correctly.
Also fails if btest represents null
Ugh, never avoid an opportunity to have an unexpected null crash your program.

Your Answer

Draft saved
Draft discarded

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.