COM aggregation, "stabilization" AddRef/Release pair, necessary? (Don Box book)

masterjodi 40 Reputation points
2024-04-29T07:02:22.5866667+00:00

To the excellent members of the COM team:

 

I have one question with the “stabilization” technique introduced in Don Box’s book.

 

In functions like FinalConstruct(), the outer object might create inner aggregation objects. These inner objects in turn might need to QueryInterface() outer object for communication. On page 96 of Don’s book, the inner object queries the “truck” interface of the outer in the inner’s Initialize().

 

It is mentioned on page 96 that the Initialize() code might accidently call release on the outer object when the outer object still has ref count of zero, cause an error. To guard this, a pair of AddRef()/Release() is added around the creation of inner object (also on page 96), and is named “stabilization”.

 

The ATL source code followed this in the code for CComCreator, and debugging show that InternalAddRef() and InternalRelease() are called.

 

However, question is on the necessity of stabilization:

 

Line 8263 onwards in atlbase.h (VS2022) shows a canonical implementation of QueryInterface(), in which AddRef() is only called when the query is successful. Otherwise the returns never equal S_OK (at line 8289 hRes is default initialized to something like 0xcccccccc rather than 0(==S_OK)).

 

Hence if QueryInterface() is unsuccessful, AddRef() will not be called, but also the return would not be S_OK, and the m_pTruck->Release() code in Don’s page 96 code would not be called either, so there is no danger of accidently calling Release() on the outer object when its ref count is zero.

 

Could you provide more insights on why the stabilization pairs is required? 

Customer

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,433 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,548 questions
Office Development
Office Development
Office: A suite of Microsoft productivity software that supports common business tasks, including word processing, email, presentations, and data management and analysis.Development: The process of researching, productizing, and refining new or existing technologies.
3,542 questions
{count} votes

Accepted answer
  1. RLWA32 40,856 Reputation points
    2024-04-29T13:58:59.2466667+00:00

    In Inner::Initialize(), which QueryInterface() and saves the interface pointer once and for all, and Release() the extra count.

    If the reference count of the outer object is 0 when FinalConstruct is entered Inner::Initialize() will increment the outer object reference count to 1 and then Release() will decrement the count to 0. Inside the outer object's Release() the reference count of 0 will trigger object deletion (i.e., delete this).

    ATL's implementation of stabilization will increment the outer object reference count from 0 to 1 before the call to Inner::Initialize(). So Inner::Initialize() will then increment the outer object reference count to 2 and then outer object's Release() decrements it back to 1 and so avoids object destruction.

    1 person found this answer helpful.
    0 comments No comments

1 additional answer

Sort by: Most helpful
  1. masterjodi 40 Reputation points
    2024-04-29T13:36:18.4933333+00:00

    Hello,

    "If all of this took place when the outer object's reference count was initially zero then":

    Assume outer.refcount==0,

    in Don Box book p.96 Inner::Initialize(void):

    1. If QueryInterface succeeds, outer.refcount==1, so later it cannot get accidentally destroyed.
    2. If QueryInterface fails, HRESULT hr != S_OK, so m_pTruck->Release() cannot get executed.

    where would "decrementing the reference count would result in the object's destruction" happen?