What is the correct way of doing this:
_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );
or:
_bstr_t description;
errorInfo->GetDescription( description.GetAddress() );
Where IError:GetDescription
is defined as:
HRESULT GetDescription (BSTR *pbstrDescription);
I know I could easily do this:
BSTR description= SysAllocString (L"Whateva"));
errorInfo->GetDescription (&description);
SysFreeString (description);
Thanks
_bstr_t (and its ATL sibling CComBSTR) are resource owners of BSTR. Spying from the code it seems that 'GetAddress' is specifically designed for the use case of working with BSTR output parameters where it is expected that client frees the BSTR.
Using 'GetAddress()' is not equivalent to using '&GetBSTR()' in case the _bstr_t already owns a BSTR. MSDN states: 'Frees any existing string and returns the address of a newly allocated string.'.
_bstr_t bstrTemp;
HRESULT hr = p->GetDescription(bstrTemp.GetAddress());
Caveat: this specific use case of 'GetAddress' is not stated in the documentation; it is my deduction from looking at the source code and experience with its ATL counter part CComBSTR.
Since user 'caoanan' questioned this solution, I paste here the source code Microsoft:
inline BSTR* _bstr_t::GetAddress()
{
Attach(0);
return &m_Data->GetWString();
}
inline wchar_t*& _bstr_t::Data_t::GetWString() throw()
{
return m_wstr;
}
inline void _bstr_t::Attach(BSTR s)
{
_Free();
m_Data = new Data_t(s, FALSE);
if (m_Data == NULL) {
_com_issue_error(E_OUTOFMEMORY);
}
}
inline _bstr_t::Data_t::Data_t(BSTR bstr, bool fCopy)
: m_str(NULL), m_RefCount(1)
{
if (fCopy && bstr != NULL) {
m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(bstr),
::SysStringByteLen(bstr));
if (m_wstr == NULL) {
_com_issue_error(E_OUTOFMEMORY);
}
}
else {
m_wstr = bstr;
}
}
A late answer that may not apply to earlier (or later) versions of Visual Studio; however,
VS 12.0 has the _bstr_t
implementation inline, and evidently an internal Data_t
instance is created with a m_RefCount
of 1 when calling GetBSTR()
on a virgin _bstr_t
. So the _bstr_t
lifecycle in your first example looks to be okay:
_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );
But if _bstr_t
is dirty, the existing internal m_wstr
pointer will be overwritten, leaking the previous memory it referenced.
By using the following operator&
, a dirty _bstr_t
can be used given that it's first cleared via Assign(nullptr)
. The overload also provides the convenience of utilizing the address operator instead of GetBSTR()
;
BSTR *operator&(_bstr_t &b) {
b.Assign(nullptr);
return &b.GetBSTR();
}
So, your first example could instead look like the following:
_bstr_t description(L"naughty");
errorInfo->GetDescription(&description);
This evaluation was based on comutil.h
from VS 12.0.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With