winforms icon indicating copy to clipboard operation
winforms copied to clipboard

`Owner` property should be cleaned to avoid memory leaks

Open vladimir-krestov opened this issue 3 years ago • 3 comments

Is your feature request related to a problem? Please describe

Not all objects are cleaned after disposing of Control accessible objects because of keeping some value in Owner. Needs to suggest the opportunity to null Owner in accessible objects to avoid memory leaks.

Describe the solution you'd like and alternatives you've considered

The suggestion is based on #6886 comment (see description)

Will this feature affect UI controls?

No

vladimir-krestov avatar Apr 24 '22 04:04 vladimir-krestov

Another possible option to avoid changes to the nullability contract is to do something like the following. It will require a risk assessment as well though.

private Control? _owner;

public Control Owner => _owner ?? throw new ObjectDisposedException(...);

internal void ClearOwnerControl()
{
    _owner = null;
}

RussKie avatar Apr 25 '22 23:04 RussKie

I see 3 ways:

  1. Use Igor's suggestion. We will avoid changing and adding a lot of null-checks for all control AOs. But it will lead for many unexpected unstable exceptions, because UIA likes to call our methods after disposing a control and disconnecting the object (eg. FragmentNavigate). So anyway we must to add checks for all methods of all inherited objects to avoid the exceptions or we can make a workaround - create a new private property (eg. "IsOwnerDisposed") inside AccessibleObject.cs and check it for every UIA API (eg. UiaCore.IRawElementProviderFragment.Navigate). Thereby we will implement a part of the approach "Don't create an accessible object if its control is not created". Something like that: image

  2. Make Owner nullable. Add a lot of null-checks for all inherited accessible objects for all methods and properties. It will change nullable statement for public API (eg. Owner can be null now). This is huge work. But it allows to make it safe.

  3. Use one supporting static property/method that returns one Control instance. Put this one global object as Owner for any disposed accessible object. Thereby we will avoid nullable changes and exceptions. This global object will return some incorrect abstract values, but we shouldn't care about it for disposed objects. Main aim - avoid exceptions.

@RussKie, @Tanya-Solyanik, what do you like?

vladimir-krestov avatar Jun 14 '22 12:06 vladimir-krestov

I like the idea of tracking when the owner is disposed, and for Control-derived owners this shouldn't be too difficult to implement (see Disposed event). Building on top of that, we could also have a shared static dummy control that we'd return when the real owner is disposed, but wouldn't we be hiding error scenarios that we'd discover (by the virtue of a failure) otherwise?

As for the adding checks - we'd have to add them one way or another.

RussKie avatar Jun 15 '22 02:06 RussKie

Sorry I missed this when I was doing #9164. While changing the nullability is a breaking change it:

  1. Isn't a binary breaking change
  2. Has a low probability of impact to external users
  3. Allows writing more resilient code for CCWs that are being held by native code (without try..catching everywhere)

JeremyKuhne avatar May 24 '23 17:05 JeremyKuhne

@Tanya-Solyanik should this be closed now?

elachlan avatar Jul 17 '23 10:07 elachlan

Yeah, we can close it now that #9224 was resolved.

dmitrii-drobotov avatar Jul 17 '23 10:07 dmitrii-drobotov