C++ 我们应该将COM中的BSTR类型视为值还是参考?

C++ 我们应该将COM中的BSTR类型视为值还是参考?,c++,com,atl,cstring,bstr,C++,Com,Atl,Cstring,Bstr,从ATL Internal一书中,我知道BSTR不同于OLECHAR*,BSTR有CComBSTR和CString 根据MSDN,我知道主叫方/被叫方的内存管理职责 从MSDN获取这条线路 HRESULT CMyWebBrowser::put_StatusText(BSTR BSTR) 我仍然不知道如何在我的实现中正确处理bstr。因为我仍然有一个关于BSTR的基本问题——我们应该将BSTR视为一个值(比如int)还是一个引用(比如int*),至少在COM接口边界上是这样 在我的实现中,我希望尽

从ATL Internal一书中,我知道BSTR不同于OLECHAR*,BSTR有CComBSTR和CString

根据MSDN,我知道主叫方/被叫方的内存管理职责

从MSDN获取这条线路

HRESULT CMyWebBrowser::put_StatusText(BSTR BSTR)

我仍然不知道如何在我的实现中正确处理
bstr
。因为我仍然有一个关于BSTR的基本问题——我们应该将
BSTR
视为一个值(比如int)还是一个引用(比如int*),至少在COM接口边界上是这样

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

附加有一些代码剪贴,我觉得它是错误的,因为它是不遵守。ATL内部说SetSysString将“释放传入的原始BSTR”,如果我使用它,它将违反BSTR参数约定,就像CComBSTR.Attach一样

总之,我想在实现中使用CString来处理原始的BSTR,但不知道正确的方法……我在项目中编写了一些just-work代码,但我总是感到紧张,因为我不知道自己是否正确

让我谈谈编码语言

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 ???
}

CComBSTR
只是一个粗略的
BSTR
。因此,您可以随意使用
CComBSTR
而不是原始
BSTR
来帮助编写异常安全的代码,这使得泄漏资源(即原始BSTR)变得更加困难,等等

如果
BSTR
是一个输入参数,它就像一个
const wchar\u t*
(带有长度前缀,并且可能包含一些
NUL
s
L'\0'
字符)。如果
BSTR
中没有嵌入
NUL
s,您只需将其传递给
CString
构造函数,该构造函数将对其进行深度复制,并且您可以在本地使用
CString
。对该
CString
的修改在原始
BSTR
上不可见。您也可以使用std::wstring(请注意,
std::wstring
也可以处理嵌入式
NUL
s)

相反,如果
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类中),并使用
std::wstring
CString
执行本地工作


作为一个“反弹读数”,考虑.< /P> < P>在输入上有<代码> BSTR < /代码>,你不负责释放它。转换到

CString
很容易:

CString值(bstr)

或者,如果您希望在MBCS build上保留Unicode字符:

CStringW sValue(bstr)

如果您需要在具有
[out]
参数时转换回,您可以这样做(简单版本):

完整版本为:

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;
}

我的大问题是我们应该复制还是参考bstr?小问题是如何使用CString引用bstr?如何使用嵌入的NULL复制bstr?使用copy或ref是什么意思?如果是按值传递的输入参数,则“按指针”传递。如果嵌入了
NUL
s,则可以将其复制到
std::wstring
中,而不是
CString
中。也许您从错误的角度观察问题。。。设想一个
BSTR
const wchar\u t*
一样,分配了一个特殊的COM分配器,并带有长度前缀(因此它可以嵌入
NUL
s)。
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;
}