CComBSTR
is just a RAII wrapper around raw BSTR
. So feel free to use CComBSTR
instead of raw BSTR
to help writing code that is exception-safe, that makes it harder to leak resources (i.e. the raw BSTR), etc.
If the BSTR
is an input parameter, it's just like a const wchar_t*
(with length prefixed, and potentially some NUL
s L'\0'
characters inside). If the BSTR
doesn't have NUL
s embedded inside, you can simply pass it to a CString
constructor, that will make a deep-copy of it, and you can locally work with your CString
. Modifications to that CString
won't be visible on the original BSTR
. You can use std::wstring as well (and note that std::wstring
can handle embedded NUL
s as well).
void DoSomething(BSTR bstrInput)
{
std::wstring myString(bstrInput);
// ... work with std::wstring (or CString...) inside here
}
Instead, if the BSTR
is an output parameter, then it is passed using another level of indirection, i.e. BSTR*
. In this case, you can use CComBSTR::Detach()
inside your method to release the BSTR
safely wrapped into the CComBSTR
, and transfer its ownership to the caller:
HRESULT DoSomething( BSTR* pbstrOut )
{
// Check parameter pointer
if (pbstrOut == nullptr)
return E_POINTER;
// Guard code with try-catch, since exceptions can't cross COM module boundaries.
try
{
std::wstring someString;
// ... work with std::wstring (or CString...) inside here
// Build a BSTR from the ordinary string
CComBSTR bstr(someString.c_str());
// Return to caller ("move semantics", i.e. transfer ownership
// from current CComBSTR to the caller)
*pbstrOut = bstr.Detach();
// All right
return S_OK;
}
catch(const std::exception& e)
{
// Log exception message...
return E_FAIL;
}
catch(const CAtlException& e)
{
return e; // implicit cast to HRESULT
}
}
Basically, the idea is to use BSTR
(wrapped in a RAII class like CComBSTR
) only at the boundary, and do the local work using std::wstring
or CString
.
As a "bouns reading", consider Eric Lippert's guide to BSTR semantics.