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

Ray*_*ond 5 c++ com atl bstr cstring

从书籍ATL Internals,我知道BSTR与OLECHAR*不同,并且BSTR有CComBSTR和CString.

根据MSTR为BSTR分配和释放内存,我知道调用者/被调用者的内存管理责任.

从MSDN获取此行,

HRESULT CMyWebBrowser::put_StatusText(BSTR bstr)

我仍然不知道如何bstr在我的实现中正确处理.因为我仍然有BSTR的基本问题 - 我们应该将其bstr视为值(如int)或作为引用(如int*),至少在COM接口边界上.

我希望在我的实现中尽快将BSTR转换为CString/CComBSTR.对于转换,值或引用语义将完全不同.我已经深入研究CComBSTR.Attach,CComBSTR.AssignBSTR等.但代码无法解决我的疑虑.

MSDN CComBSTR.Attach有一些代码片段,我觉得这是错误的,因为它不服从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 ???
}
Run Code Online (Sandbox Code Playgroud)

Mr.*_*C64 12

CComBSTR仅仅是一个RAII包装围绕原料 BSTR.所以随意使用CComBSTR而不是raw BSTR来帮助编写异常安全的代码,这会使资源泄漏更加困难(即原始BSTR)等.

如果BSTR是一个输入参数,它就像一个const wchar_t*(长度前缀,可能还有一些NULs L'\0'字符).如果内部BSTR没有NUL嵌入s,你可以简单地将它传递给CString构造函数,这将构建它的深层副本,并且你可以在本地使用你的CString.对CString原件的修改将不会在原件上显示BSTR.您也可以使用std :: wstring(并注意也std::wstring可以处理嵌入式NULs).

void DoSomething(BSTR bstrInput)
{
    std::wstring myString(bstrInput);
    // ... work with std::wstring (or CString...) inside here
}
Run Code Online (Sandbox Code Playgroud)

相反,如果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
    }
}
Run Code Online (Sandbox Code Playgroud)

基本上,这个想法是在边界使用BSTR(包含在类似RAII类中CComBSTR),并使用或执行本地工作.std::wstringCString

作为"读书",请考虑Eric Lippert的BSTR语义指南.