使用_bstr_t在函数中传递BSTR*类型的参数

发布于 2024-10-05 20:48:42 字数 556 浏览 2 评论 0原文

执行此操作的正确方法是什么:

_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );

或:

_bstr_t description;
errorInfo->GetDescription( description.GetAddress() );

其中 IError:GetDescription 定义为:

HRESULT GetDescription (BSTR *pbstrDescription);

我知道我可以轻松做到这一点:

BSTR description= SysAllocString (L"Whateva"));
errorInfo->GetDescription (&description);
SysFreeString (description);

谢谢

What is the correct way of doing this:

_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );

or:

_bstr_t description;
errorInfo->GetDescription( description.GetAddress() );

Where IError:GetDescription is defined as:

HRESULT GetDescription (BSTR *pbstrDescription);

I know I could easily do this:

BSTR description= SysAllocString (L"Whateva"));
errorInfo->GetDescription (&description);
SysFreeString (description);

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

心在旅行 2024-10-12 20:48:42

BSTR 是引用计数的,我严重怀疑如果您使用 GetAddress(),它是否能正常工作。遗憾的是,无法使用源代码来仔细检查这一点。我一直都是这样做的:

BSTR temp = 0;
HRESULT hr = p->GetDescription(&temp);
if (SUCCEEDED(hr)) {
    _bstr_t wrap(temp, FALSE);
    // etc..
}

The BSTR is reference counted, I seriously doubt that will work right if you use GetAddress(). Sadly the source code isn't available to double-check that. I've always done it like this:

BSTR temp = 0;
HRESULT hr = p->GetDescription(&temp);
if (SUCCEEDED(hr)) {
    _bstr_t wrap(temp, FALSE);
    // etc..
}
爱的故事 2024-10-12 20:48:42

跟进 @Hans 的答案 - 构造 _bstr_t 的适当方法取决于 GetDescription 是否返回您拥有的 BSTR,或者一个引用您不必释放的内存。

这里的目标是最大限度地减少副本数量,同时避免对返回数据进行任何手动调用 SysFreeString。我将修改所示的代码以澄清这一点:

BSTR temp = 0;
HRESULT hr = p->GetDescription(&temp);
if (SUCCEEDED(hr)) {
    _bstr_t wrap(temp, false);    // do not copy returned BSTR, which
                                  // will be freed when wrap goes out of scope.
                                  // Use true if you want a copy.
    // etc..
}

To follow up on @Hans's answer - the appropriate way to construct the _bstr_t depends on whether GetDescription returns you a BSTR that you own, or one that references memory you don't have to free.

The goal here is to minimize the number of copies, but also avoid any manual calls to SysFreeString on the returned data. I would modify the code as shown to clarify this:

BSTR temp = 0;
HRESULT hr = p->GetDescription(&temp);
if (SUCCEEDED(hr)) {
    _bstr_t wrap(temp, false);    // do not copy returned BSTR, which
                                  // will be freed when wrap goes out of scope.
                                  // Use true if you want a copy.
    // etc..
}
甜点 2024-10-12 20:48:42

较晚的答案可能不适用于早期(或更高)版本的 Visual Studio;然而,
VS 12.0 具有内联 _bstr_t 实现,并且显然在调用 GetBSTR() 时会创建一个 Data_t 实例,其 m_RefCount 为 1 处女_bstr_t。因此,第一个示例中的 _bstr_t 生命周期看起来没问题:

_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );

但是如果 _bstr_t 脏了,现有的内部 m_wstr 指针将被覆盖,泄漏它引用的前一个内存。

通过使用以下operator&,可以使用脏_bstr_t,因为它首先是通过Assign(nullptr)清除的。重载还提供了使用地址运算符代替 GetBSTR() 的便利;

BSTR *operator&(_bstr_t &b) {
    b.Assign(nullptr);
    return &b.GetBSTR();
}

因此,您的第一个示例可能如下所示:

_bstr_t description(L"naughty");
errorInfo->GetDescription(&description);

此评估基于 VS 12.0 中的 comutil.h

A late answer that may not apply to earlier (or later) versions of Visual Studio; however,
VS 12.0 has the _bstr_t implementation inline, and evidently an internal Data_t instance is created with a m_RefCount of 1 when calling GetBSTR() on a virgin _bstr_t. So the _bstr_t lifecycle in your first example looks to be okay:

_bstr_t description;
errorInfo->GetDescription( &description.GetBSTR() );

But if _bstr_t is dirty, the existing internal m_wstr pointer will be overwritten, leaking the previous memory it referenced.

By using the following operator&, a dirty _bstr_t can be used given that it's first cleared via Assign(nullptr). The overload also provides the convenience of utilizing the address operator instead of GetBSTR();

BSTR *operator&(_bstr_t &b) {
    b.Assign(nullptr);
    return &b.GetBSTR();
}

So, your first example could instead look like the following:

_bstr_t description(L"naughty");
errorInfo->GetDescription(&description);

This evaluation was based on comutil.h from VS 12.0.

看春风乍起 2024-10-12 20:48:42

_bstr_t(及其 ATL 兄弟 CComBSTR)是 BSTR 的资源所有者。从代码中观察,“GetAddress”似乎是专门为使用 BSTR 输出参数的用例而设计的,其中客户端获得所有权并期望释放 BSTR。 MSDN 指出:“释放任何现有字符串并返回新分配的字符串的地址。”。

_bstr_t bstrTemp;
HRESULT hr = p->GetDescription(bstrTemp.GetAddress());

请注意,如果 _bstr_t 已经拥有 BSTR,则使用“GetAddress()”并不等同于使用“&GetBSTR()”。 “GetBSTR”仅返回商店位置,而不释放现有位置。

注意:文档中没有说明“GetAddress”的具体用例;这是我通过查看源代码和使用 ATL 对应部分 CComBSTR 的经验得出的结论。

由于用户“caoanan”对此解决方案提出质疑,我将 Microsoft 的源代码粘贴到此处:

inline BSTR* _bstr_t::GetAddress()
{
    Attach(0);
    return &m_Data->GetWString();
}

inline wchar_t*& _bstr_t::Data_t::GetWString() throw()
{
    return m_wstr;
}

inline void _bstr_t::Attach(BSTR s)
{
    _Free();

    m_Data = new Data_t(s, FALSE);
    if (m_Data == NULL) {
        _com_issue_error(E_OUTOFMEMORY);
    }
}

inline _bstr_t::Data_t::Data_t(BSTR bstr, bool fCopy)
    : m_str(NULL), m_RefCount(1)
{
    if (fCopy && bstr != NULL) {
        m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(bstr),
                                         ::SysStringByteLen(bstr));

        if (m_wstr == NULL) {
            _com_issue_error(E_OUTOFMEMORY);
        }
    }
    else {
        m_wstr = bstr;
    }
}

_bstr_t (and its ATL sibling CComBSTR) are resource owners of BSTR. Spying from the code it seems that 'GetAddress' is specifically designed for the use case of working with BSTR output parameters where client takes ownership and expected to free the BSTR. MSDN states: 'Frees any existing string and returns the address of a newly allocated string.'.

_bstr_t bstrTemp;
HRESULT hr = p->GetDescription(bstrTemp.GetAddress());

Note that using 'GetAddress()' is not equivalent to using '&GetBSTR()' in case the _bstr_t already owns a BSTR. 'GetBSTR'' only returns the store location without freeing an existing one.

Caveat: this specific use case of 'GetAddress' is not stated in the documentation; it is my deduction from looking at the source code and experience with its ATL counter part CComBSTR.

Since user 'caoanan' questioned this solution, I paste here the source code Microsoft:

inline BSTR* _bstr_t::GetAddress()
{
    Attach(0);
    return &m_Data->GetWString();
}

inline wchar_t*& _bstr_t::Data_t::GetWString() throw()
{
    return m_wstr;
}

inline void _bstr_t::Attach(BSTR s)
{
    _Free();

    m_Data = new Data_t(s, FALSE);
    if (m_Data == NULL) {
        _com_issue_error(E_OUTOFMEMORY);
    }
}

inline _bstr_t::Data_t::Data_t(BSTR bstr, bool fCopy)
    : m_str(NULL), m_RefCount(1)
{
    if (fCopy && bstr != NULL) {
        m_wstr = ::SysAllocStringByteLen(reinterpret_cast<char*>(bstr),
                                         ::SysStringByteLen(bstr));

        if (m_wstr == NULL) {
            _com_issue_error(E_OUTOFMEMORY);
        }
    }
    else {
        m_wstr = bstr;
    }
}
终弃我 2024-10-12 20:48:42
    int GetDataStr(_bstr_t & str) override {
    BSTR data = str.Detach();
    int res = m_connection->GetDataStr( &data );
    str.Attach(data);
    return res;
}
    int GetDataStr(_bstr_t & str) override {
    BSTR data = str.Detach();
    int res = m_connection->GetDataStr( &data );
    str.Attach(data);
    return res;
}
扛起拖把扫天下 2024-10-12 20:48:42

感谢 bvj 和 Gast128 的回答,很明显,您可以通过 GetAddress() 将空的 bstr_t 传递给具有签名 GetDescription(BSTR* BS),这将创建一个新的 BSTR,但您绝不能以这种方式将包含值的 bstr_t 传递给函数旨在使用包装的BSTR,因为GetAddress()将释放包装的BSTR。我给出一个完整的例子。

#include <iostream>
#include <comdef.h>

void CreateBSTR(BSTR* bstr)
{
  *bstr = SysAllocString(L"Read the manual!");
}

void UseBSTR(BSTR* bstr)
{
  std::wcout << L"Content: " << *bstr << std::endl;
}

int main()
{
  bstr_t bs_t;
  CreateBSTR(bs_t.GetAddress());
  std::wcout << L"Description: " << bs_t << std::endl;

  BSTR BS = bs_t.Detach();
  UseBSTR(&BS);
  bs_t.Attach(BS);
  //Don't: UseBSTR(bs_t.GetAddress());
}

Thanks to the answers by bvj and gast128, it is clear that you can pass an empty bstr_t via GetAddress() to a function with the signature GetDescription(BSTR* BS), which will create a new BSTR, but you must never pass a bstr_t containing a value in this way to a function intended to use the wrapped BSTR, since GetAddress() will free the wrapped BSTR. I present a complete example.

#include <iostream>
#include <comdef.h>

void CreateBSTR(BSTR* bstr)
{
  *bstr = SysAllocString(L"Read the manual!");
}

void UseBSTR(BSTR* bstr)
{
  std::wcout << L"Content: " << *bstr << std::endl;
}

int main()
{
  bstr_t bs_t;
  CreateBSTR(bs_t.GetAddress());
  std::wcout << L"Description: " << bs_t << std::endl;

  BSTR BS = bs_t.Detach();
  UseBSTR(&BS);
  bs_t.Attach(BS);
  //Don't: UseBSTR(bs_t.GetAddress());
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文