我们应该将COM中的BSTR类型视为值还是引用?

时间:2013-03-15 20:04:17

标签: c++ com atl cstring bstr

从书籍 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 ???
}

2 个答案:

答案 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::wstringCString进行本地工作。

作为“读书”,请考虑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;
}