5

从ATL Internals一书中,我知道 BSTR 与 OLECHAR* 不同,BSTR 有 CComBSTR 和 CString。

根据 MSDN Allocating and Releating 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 有一些代码片段,我觉得这是错误的,因为它不遵守为 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 ???
}
4

2 回答 2

12

CComBSTR只是rawRAII 包装器。因此,请随意使用而不是 raw来帮助编写异常安全的代码,这使得泄漏资源(即原始 BSTR)等变得更加困难。 BSTRCComBSTRBSTR

如果 theBSTR是一个输入参数,它就像 a const wchar_t*(以长度为前缀,并且可能包含一些NULsL'\0'字符)。如果sBSTR没有NUL嵌入其中,您可以简单地将其传递给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()在您的方法中使用来释放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),并使用or进行本地工作。std::wstringCString

作为“读物”,请考虑Eric Lippert 的 BSTR 语义指南

于 2013-03-15T20:23:21.177 回答
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;
}
于 2013-03-15T20:24:24.937 回答