There are two ways in which one ("outer") object can re-use another ("inner") object internally, and provide all the interfaces of the inner object (in addition to its own).

Containment: the outer object simply contains the inner object – which is unaware that its client is actually another object. The outer object must implement each of the inner object’s interfaces, but the implementation just calls the inner object via its interface.

Aggregation: when a COM object is created, it is passed an IUnknown pointer, which is normally NULL. Alternatively, it can be passed the IUnknown interface of the outer object, in which case it knows it’s the inner object of an aggregate. If an outer object interface is queried for an inner object interface, it just returns the inner object’s interface directly. If an inner object interface is queried for an interface it doesn’t know, it passes the request to the IUnknown interface it was given when it was created. Both objects must conform to a number of aggregation rules to ensure that reference counts work correctly.