Search code examples
winapicomwin32comrunning-object-table

How does Running Object Table implement weak references?


When you register a COM object in the Running Object Table with a zero flag (requesting a weak ref), the ROT increments the ref count by 1. The act of getting an object from the ROT increases the ref count by one more. Once that one is freed, the object stays alive with a ref count of at least one. Its registration in the ROT isn't magically revoked upon retrieval, either.

How is that weak? How is that different from strong registration?

Strong registration follows the same pattern - both registering and retrieval increments the ref count by one.

The interface pointer that the ROT returns to in-apartment clients is not a proxy; the ROT has no way of knowing that I've freed my retrieved interface pointer.


Solution

  • really removal from ROT behavior dependent not only from ROTFLAGS_REGISTRATIONKEEPSALIVE flag but also are (and how) your object implemented IExternalConnection

    ( special note for @IInspectable only - yes all this is undocumented, unsupported, can changed - so please not read more ).

    when we register object in ROT com always query him for IExternalConnection interface. if object not implemented it - used default implementation.

    in case ROTFLAGS_REGISTRATIONKEEPSALIVE already at registration time IExternalConnection::AddConnection called. so we already have 1 external connection. without ROTFLAGS_REGISTRATIONKEEPSALIVE - this method not called.

    every time when somebody call IRunningObjectTable::GetObject (! from another apartment) the CRemoteUnknown::RemAddRef called in our process. this method call IExternalConnection::AddConnection only if we register without ROTFLAGS_REGISTRATIONKEEPSALIVE flag .

    every time when we final Release object (!on proxy, obtained from previous GetObject call ) - CRemoteUnknown::RemReleaseWorker called in our local process. and it internally call IExternalConnection::ReleaseConnection only in case no ROTFLAGS_REGISTRATIONKEEPSALIVE on object. default implementation of IExternalConnection called CStdMarshal::Disconnect -> InternalIrotRevoke when external reference (not tottal object reference) reach 0 and fLastReleaseCloses == TRUE - as result our object is revoked from ROT. but if we implemet IExternalConnection by self we can call or not call CoDisconnectObject so we can be revoked or not from ROT.

    and finally when we direct or indirect call IRunningObjectTable::Revoke com call IExternalConnection::ReleaseConnection if we register with ROTFLAGS_REGISTRATIONKEEPSALIVE .

    so conclusion:

    if we register with ROTFLAGS_REGISTRATIONKEEPSALIVE - IExternalConnection::AddConnection will be called only once at registration time (really can be called say n+1 time and n time - ReleaseConnection - but all this inside IRunningObjectTable::Register call.). when somebody get our object from ROT - we will be not notified about this. and finally IExternalConnection::ReleaseConnection will be called also only once when we call IRunningObjectTable::Revoke.

    from the other hand, if we not use ROTFLAGS_REGISTRATIONKEEPSALIVE flag - IExternalConnection methods will be not called on Register and Revoke. but it will be multiple time called on IRunningObjectTable::GetObject and final Release (on object proxy). if we not implemented IExternalConnection yourself or call CoDisconnectObject when external refs reach 0 and fLastReleaseCloses - we will be removed from ROT. but we free not call CoDisconnectObject (in this case behavior will be like we use ROTFLAGS_REGISTRATIONKEEPSALIVE) or say call it under some condition.

    advantage - we can track every our object use in case no ROTFLAGS_REGISTRATIONKEEPSALIVE flag and decide by self are need disconnect when external refs reach 0 or not.

    and the last - if we call IRunningObjectTable::GetObject from same apartment where we call IRunningObjectTable::Register - we got not proxy, but direct object pointer. in this case of course will be no calls to IExternalConnection methods