从书籍 ATL Internals ,我知道BSTR与OLECHAR *不同,并且BSTR有CComBSTR和CString。
根据MSDN Allocating and Releasing Memory for a BSTR,我知道调用者/被调用者的内存管理责任。
从MSDN获取此行,
HRESULT CMyWebBrowser::put_StatusText(BSTR bstr)
我仍然不知道如何在我的实现中正确处理bstr
。由于我仍然有BSTR的基本问题 - 我们应该将bstr
视为值(如int)还是作为引用(如int *),至少在COM接口边界上。
我希望在我的实现中尽快将BSTR转换为CString / CComBSTR。对于转换,值或引用语义将完全不同。我已经深入研究CComBSTR.Attach,CComBSTR.AssignBSTR等。但代码无法解决我的疑虑。
MSDN CComBSTR.Attach有一些代码片段,我觉得这是错误的,因为它不服从Allocating and Releasing Memory for a BSTR。 ATL Internals表示SetSysString将'释放传入的原始BSTR',如果我使用它,它将违反BSTR参数约定,就像CComBSTR.Attach一样。
总而言之,我想在实现中使用CString来处理原始BSTR,但是不知道正确的方法......我在我的项目中写了一些正常的工作代码,但我总是感到紧张,因为我不知道我知道我是否正确。
让我谈谈编码语言
HRESULT CMyWebBrowser::put_StatusText(BSTR bstr)
{
// What I do NOT know
CString str1; // 1. copy bstr (with embeded NUL)
CString str2; // 2. ref bstr
// What I know
CComBSTR cbstr1;
cbstr1.AssignBSTR(bstr); // 3. copy bstr
CComBSTR cbstr2;
cbstr2.Attach(bstr); // 4. ref bstr, do not copy
// What I do NOT know
// Should we copy or ref bstr ???
}
答案 0 :(得分:12)
CComBSTR
只是原始 BSTR
周围的RAII wrapper。因此,请随意使用CComBSTR
而不是原始BSTR
来帮助编写异常安全的代码,这样可以更难泄漏资源(即原始BSTR)等。
如果BSTR
是输入参数,则它就像const wchar_t*
一样(长度为前缀,可能还有一些NUL
s L'\0'
里面的人物)。如果BSTR
内嵌了NUL
,您只需将其传递给CString
构造函数,该构造函数将对其进行深层复制,您可以在本地使用你的CString
。原始CString
上将无法显示对BSTR
的修改。您也可以使用std :: wstring(并注意std::wstring
也可以处理嵌入的NUL
。
void DoSomething(BSTR bstrInput)
{
std::wstring myString(bstrInput);
// ... work with std::wstring (or CString...) inside here
}
相反,如果BSTR
是输出参数,则使用其他级别的间接传递,即BSTR*
。在这种情况下,您可以在方法中使用CComBSTR::Detach()
释放BSTR
安全地包含在CComBSTR
中,并将其所有权转移给调用者:
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
}
}
基本上,我们的想法是使用BSTR
(包含在RAII类CComBSTR
)仅限的边界 ,然后执行使用std::wstring
或CString
进行本地工作。
作为“读书”,请考虑Eric Lippert's guide to BSTR semantics。
答案 1 :(得分:4)
输入BSTR
时,您不负责发布它。转换为CString
非常简单:
CString sValue(bstr);
或者,如果您希望在MBCS构建上保留Unicode字符:
CStringW sValue(bstr);
如果您需要在有[out]
参数时转换回去,那么(简单版):
VOID Foo(/*[out]*/ BSTR* psValue)
{
CString sValue;
*psValue = CComBSTR(sValue).Detach();
}
完整版是:
STDMETHODIMP Foo(/*[out]*/ BSTR* psValue)
{
_ATLTRY
{
ATLENSURE_THROW(psValue, E_POINTER); // Parameter validation
*psValue = NULL; // We're responsible to initialize this no matter what
CString sValue;
// Doing our stuff to get the string value into variable
*psValue = CComBSTR(sValue).Detach();
}
_ATLCATCH(Exception)
{
return Exception;
}
return S_OK;
}