我们应该将 COM 中的 BSTR 类型视为值还是引用?
Posted
技术标签:
【中文标题】我们应该将 COM 中的 BSTR 类型视为值还是引用?【英文标题】:Shall we treat BSTR type in COM as value or reference? 【发布时间】:2013-03-04 16:27:52 【问题描述】:从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 ???
【问题讨论】:
【参考方案1】:CComBSTR
只是 raw BSTR
周围的 RAII wrapper。因此,请随意使用 CComBSTR
而不是原始的 BSTR
来帮助编写异常安全的代码,从而更难泄漏资源(即原始 BSTR)等。
如果BSTR
是一个输入 参数,它就像一个const wchar_t*
(以长度为前缀,并且可能包含一些NUL
s L'\0'
字符)。如果BSTR
没有嵌入NUL
s,您可以简单地将它传递给CString
构造函数,这将对其进行深层复制,您可以在本地使用您的CString
。对 CString
的修改将不会在原始 BSTR
上可见。您也可以使用 std::wstring (注意std::wstring
也可以处理嵌入的NUL
s)。
void DoSomething(BSTR bstrInput)
std::wstring myString(bstrInput);
// ... work with std::wstring (or CString...) inside here
相反,如果BSTR
是一个输出 参数,那么它会使用另一层间接传递,即BSTR*
。在这种情况下,您可以在方法中使用CComBSTR::Detach()
来释放安全地包装到CComBSTR
中的BSTR
,并将其所有权转移给调用者:
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
(包装在类似CComBSTR
的RAII 类中)仅在边界处,并使用@ 进行本地工作987654348@或CString
。
作为“赏读”,请考虑Eric Lippert's guide to BSTR semantics。
【讨论】:
我最大的问题是我们应该复制还是引用 bstr ?小问题是如何用 CString 引用 bstr ?如何复制带有嵌入 NULL 的 bstr? 复制或参考是什么意思?如果是输入参数,则按值传递BSTR
,否则通过“按指针”传递BSTR*
。如果您嵌入了NUL
s,您可以将其复制到std::wstring
,而不是CString
。
也许你从错误的角度观察问题...想想BSTR
就像const wchar_t*
分配了一个特殊的 COM 分配器和长度前缀(所以它可以有嵌入NUL
s)。【参考方案2】:
输入有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;
【讨论】:
以上是关于我们应该将 COM 中的 BSTR 类型视为值还是引用?的主要内容,如果未能解决你的问题,请参考以下文章