Conversation
|
|
||
| In the former, the object is implemented purely in C# and its lifetime is managed by the .NET | ||
| garbage collector. C#/WinRT only comes into play when this C# object is passed across the ABI to a | ||
| WinRT function. When this happens, C#/WinRT creates a CCW for it using the .NET 5 ComWrappers API |
There was a problem hiding this comment.
Link to the ComWrappers API? https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.comwrappers
| know about all the references to it from another reference tracking system like the .NET | ||
| garbage collector. This allows XAML to track scenarios where objects may have circular references | ||
| or only have references to it from objects that are pending clean up. Specifically, when a C# wrapper | ||
| is created for a XAML runtime tracked object (implements `IReferenceTracker`), the XAML runtime |
There was a problem hiding this comment.
| serves the purpose of documenting how C#/WinRT interacts with all 3 systems to correctly manage the | ||
| lifetime of projected WinRT objects. | ||
|
|
||
| ### COM reference tracking |
There was a problem hiding this comment.
It might be easier to augment this with a series of enumerated steps in the process.
Co-authored-by: Aaron Robinson <arobins@microsoft.com>
| which C#/WinRT does behind the scenes when a C# class extends such a projected type. In COM aggregation, | ||
| there is 2 objects in play: the outer object which is the CCW for the C# object and the inner object | ||
| which is the WinRT object being extended. Both these objects are made to look like one object known as the | ||
|
|
There was a problem hiding this comment.
Unexpected line break? Also some line breaks below this when viewing the preview non-markdown version of the doc
|
|
||
| The above describes what typically happens for any natively implemented WinRT object that C#/WinRT | ||
| projects. There are some differences to this when the object is instead a C# implemented object that is | ||
| projected into WinRT via a COM callable wrapper (CCW). This is done via a C# class implementing a set of |
There was a problem hiding this comment.
Maybe factor out these into 2 separate sections? E.g.
C# class implementing WinRT interface
...
C# class extending an unsealed WinRT type
...
|
|
||
| ### COM reference tracking | ||
|
|
||
| Each WinRT object that we project is based on COM and implements a set of interfaces. |
|
|
||
| Each WinRT object that we project is based on COM and implements a set of interfaces. | ||
| As per the COM design, every COM interface implements `IUnknown` which has an `AddRef` and `Release` | ||
| function. C#/WinRT calls the `AddRef` function anytime it gets a new reference to a WinRT object |
There was a problem hiding this comment.
You mention CCWs below. Maybe introduce IObjectReference as the RCW here.
| As per the COM design, every COM interface implements `IUnknown` which has an `AddRef` and `Release` | ||
| function. C#/WinRT calls the `AddRef` function anytime it gets a new reference to a WinRT object | ||
| which C#/WinRT holds onto using an `IObjectReference` instance. It also calls `AddRef` whenever it gives | ||
| out a reference to one of these objects across the ABI as an out parameter. C#/WinRT calls the |
There was a problem hiding this comment.
should introduce this acronym too: Application Binary Interface (ABI)
| object alive and that is managed by the .NET runtime and `ComWrappers` implementation. | ||
|
|
||
|
|
||
| In the latter scenario, extending an unsealed WinRT type is typically done via COM Aggregation |
There was a problem hiding this comment.
"latter" here is referring back a bit - may help to provide some structure to the doc (section titles)
|
|
||
| In the latter scenario, extending an unsealed WinRT type is typically done via COM Aggregation | ||
| which C#/WinRT does behind the scenes when a C# class extends such a projected type. In COM aggregation, | ||
| there is 2 objects in play: the outer object which is the CCW for the C# object and the inner object |
| composed object. To achieve that, the outer object delegates calls for any of the inner object | ||
|
|
||
| interfaces that aren't overridden to the inner object. Any calls for interfaces that are only | ||
| implemented on the outer object or is overridden by the outer object or is for the `IUnknown` interface |
There was a problem hiding this comment.
or are overridden ..., or are for ....
|
|
||
|
|
||
| In the latter scenario, extending an unsealed WinRT type is typically done via COM Aggregation | ||
| which C#/WinRT does behind the scenes when a C# class extends such a projected type. In COM aggregation, |
There was a problem hiding this comment.
I'd include links inline in the body of the text, rather than at the end of the doc, like COM Aggregation here (https://docs.microsoft.com/en-us/windows/win32/com/aggregation)
| would be handled by the outer object itself. The last part means that the lifetime and the COM reference | ||
| counting of this aggregated object is maintained by the outer object and more specifically its `IUnknown` | ||
| implementation on the CCW from ComWrappers. This is where the standard COM reference tracking | ||
| convention described earlier starts to differ. As we know for CCWs, there are 2 things which |
There was a problem hiding this comment.
spell out any number less than 10 (standard technical writing guideline)
| other than facilitating QI calls for them from the native side where `Release` isn't called right after. | ||
| The recommendation for tear off interfaces which do want to support such uses on aggregated objects | ||
| is that they can continue to be constructed on demand upon the first QI for it, but the interface | ||
| should not be cleaned up until the object is cleaned up even if there are no longer any reference |
There was a problem hiding this comment.
can we be more crisp about the recommendation - that a tear-off implementor should cache all instances to prevent premature destruction?
| objects if one of these interfaces need to be QIed for by the composed object as part of the | ||
| projection implementation. This is because a `Release` would happen right after which would trigger | ||
| the cleanup of the interface as its lifetime isn't tied to the outer. Given that tear off interfaces | ||
| are rare and not typically used by C# consumers, C#/WinRT today doesn't address this |
There was a problem hiding this comment.
"not typically used by C# consumers" ? the tear-off implementor has no idea what client code is using it
| or only have references to it from objects that are pending clean up. Specifically, when a C# wrapper | ||
| is created for a XAML runtime tracked object (implements `IReferenceTracker`), the XAML runtime | ||
| needs to be informed of it by a call to `ConnectFromTrackerSource` on `IReferenceTracker`. This is | ||
| done by the ComWrappers implementation when an RCW is created. After that, any references to that |
| needs to be informed of it by a call to `ConnectFromTrackerSource` on `IReferenceTracker`. This is | ||
| done by the ComWrappers implementation when an RCW is created. After that, any references to that | ||
| object that are tracked by the other reference tracking system (.NET garbage collector in this case) | ||
| needs to be informed to XAML by a call to `AddRefFromTrackerSource`. This is done by both C#/WinRT and |
| needs to be informed to XAML by a call to `AddRefFromTrackerSource`. This is done by both C#/WinRT and | ||
| ComWrappers after any `AddRef` call to increment the COM reference count. Similarly, before the | ||
| `Release` call, there would be a `ReleaseFromTrackerSource` to indicate a reference on the object | ||
| was released. When the RCW is destructed, there would similarly be a call to |
Documenting what we learned when working on the fix for the memory management issues in C#/WinRT.
Fixes #810