如何更好地初始化不可创建的 COM 对象的引用计数器?

发布于 2024-08-17 18:44:58 字数 1366 浏览 6 评论 0原文

我有一个 COM 接口,其中有一个返回对象的方法:

interface ICreatorInterface {
    HRESULT CreateObject( IObjectToCreate** );
};

关键是调用 ICreatorInterface::CreateObject() 是检索实现 IObjectToCreate 接口的对象的唯一方法。

在 C++ 中,我可以这样做:

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 0
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();
     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result );
     if( FAILED(hr) ) {
         delete newObject;
     }
     return hr;
 }

或 这种方式

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 1
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();
     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result );
     // if QI() failed reference count is still 1 so this will delete the object
     newObject->Release();
     return hr;
 }

区别在于引用计数器的初始化方式以及在 QueryInterface() 失败时如何实现对象删除。由于我完全控制了 CCreatorInterfaceImplCObjectToCreateImpl,所以我可以选择任何一种方式。

IMO 第一个变体更清晰 - 所有引用计数的东西都在一段代码中。我有监督什么吗?为什么第二种方法会更好?以上哪一个更好,为什么?

I have a COM interface with a method that returns an object:

interface ICreatorInterface {
    HRESULT CreateObject( IObjectToCreate** );
};

The key is that calling ICreatorInterface::CreateObject() is the only way to retrieve an object implementing IObjectToCreate interface.

In C++ I could do it this way:

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 0
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();
     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result );
     if( FAILED(hr) ) {
         delete newObject;
     }
     return hr;
 }

or this way

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 1
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();
     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result );
     // if QI() failed reference count is still 1 so this will delete the object
     newObject->Release();
     return hr;
 }

The difference is how the reference counter is initialized and how the object deletion is implemented in case QueryInterface() fails. Since I fully control both CCreatorInterfaceImpl and CObjectToCreateImpl I can go either of ways.

IMO the first variant is clearer - all reference-counting stuff is in one piece of code. Have I overseen something? Why could the second approach be better? Which of the above is better and why?

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

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

发布评论

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

评论(3

葬心 2024-08-24 18:44:58

这两种变体都违反了 COM 的一个非常基本的原则

  • 。除了 AddRef 之外,切勿对引用计数为零的 COM 对象调用任何方法。

否则会导致各种错误。简单地说,因为它阻止人们对对象进行完全合法的操作。就像把它们放入智能指针一样。智能指针将调用 AddRef,将计数设置为 1,然后 Release 将计数设置为 0 并导致对象自毁。

是的,我意识到 90% 的 QueryInterface 实现都没有这样做。但我也向您保证,有一些可以做到:)

我认为最简单的方法是在创建对象后立即调用 AddRef。这允许对象尽早表现得像普通 COM 对象一样。

我过去遇到过这个问题,并且编写了一个很好的小帮助器方法(假设该对象是在 ATL 中实现的)。

template <class T>
static 
HRESULT CreateWithRef(T** ppObject)
{
    CComObject<T> *pObject;
    HRESULT hr = CComObject<T>::CreateInstance(&pObject);
    if ( SUCCEEDED(hr) )
    {
        pObject->AddRef();
        *ppObject = pObject;
    }

    return hr; 
}

Both variations violate a very fundamental principle of COM

  • Never call any method, other than AddRef, on a COM object that has a ref count of zero.

To do otherwise leads to all sorts of errors. Simply put because it prevents people from doing completely legal operations on the object. Like putting them into a smart pointer. The smart pointer would call AddRef, put the count to 1, and later Release putting the count to 0 and causing the object to self destruct.

Yes I realize that 90% of the implementations of QueryInterface don't do this. But I also guarantee you that there are some out there that do :)

I think the simplest approach is to call AddRef immediately after creating the object. This allows the object to behave like a normal COM object at the earliest possible moment.

I've run into this problem in the past and I've written a nice little helper method (assuming the object is implemented in ATL).

template <class T>
static 
HRESULT CreateWithRef(T** ppObject)
{
    CComObject<T> *pObject;
    HRESULT hr = CComObject<T>::CreateInstance(&pObject);
    if ( SUCCEEDED(hr) )
    {
        pObject->AddRef();
        *ppObject = pObject;
    }

    return hr; 
}
江南月 2024-08-24 18:44:58

Raymond Chen 在他的博客上写了一篇相关文章:
引用计数为零的对象

Raymond Chen wrote a relevant article on his weblog:
On objects with a reference count of zero

逆光下的微笑 2024-08-24 18:44:58

我总是使用以下代码场景来创建返回的 com 对象,以避免内存问题。当然,这是可行的,因为我的对象在创建时引用计数 = 0。对我来说,这总是比尝试使用删除运算符处理错误条件更清晰。

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 0
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();

     newObject->AddRef();

     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result);

     newObject->Release(); // release my addref, if QI succeeded it AddRef'd, if not the object is destroyed

     return hr; // return result from QI
 }

I always use the following code scenario for creating returned com objects to avoid issues with memory. Of course this works because my objects are reference counted = 0 when created. This always just seems clearer to me than trying to handle the error condition with using the delete operator.

 HRESULT CCreatorInterfaceImpl::CreateObject( IObjectToCreate** result )
 {
     //CObjectToCreateImpl constructor sets reference count to 0
     CObjectToCreateImpl* newObject = new CObjectToCreateImpl();

     newObject->AddRef();

     HRESULT hr = newObject->QueryInterface( __uuidof(IObjectToCreate), (void**)result);

     newObject->Release(); // release my addref, if QI succeeded it AddRef'd, if not the object is destroyed

     return hr; // return result from QI
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文