Browse Source

Fix fatal NRE when the Win32 input pane is disposed during window close (#22138)

* Fix fatal NRE when the Win32 input pane is disposed during window close

WindowsInputPane.Dispose set _windowImpl to null before calling
IFrameworkInputPane.Unadvise. Unadvise can deliver a final Showing/Hiding
notification as it unwinds, and the shell may also call back after teardown.
Either path reaches OnStateChanged -> ScreenRectToClient, which dereferences
the now-null _windowImpl.

Because that frame is entered from native code through a COM callback, the
resulting NullReferenceException cannot unwind into managed code: the process
is terminated with 0xC0000005 instead of raising a catchable exception.

Two changes, both minimal:

- Release _windowImpl only after Unadvise has returned, so the field stays
  valid for the whole of the teardown during which it is read.
- Return early from OnStateChanged once _disposed is set, so a notification
  arriving during or after Dispose is ignored rather than acted on.

Either change alone closes the reported race; together they also cover a
callback that arrives after Dispose has completed.

* Rewrite input pane comment to be more concise

---------

Co-authored-by: Max Katz <maxkatz6@outlook.com>
pull/22142/head
Darren Bruning 2 weeks ago
committed by GitHub
parent
commit
c0183eaa88
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 11
      src/Windows/Avalonia.Win32/Input/WindowsInputPane.cs

11
src/Windows/Avalonia.Win32/Input/WindowsInputPane.cs

@ -52,6 +52,11 @@ internal unsafe class WindowsInputPane : InputPaneBase, IDisposable
private void OnStateChanged(bool showing, UnmanagedMethods.RECT? prcInputPaneScreenLocation)
{
// Unadvise can deliver last notification while it unwinds, and the shell can call back after teardown.
// Either would dereference a disposed _windowImpl, crashing the process.
if (_disposed)
return;
var oldState = (OccludedRect, State);
OccludedRect = prcInputPaneScreenLocation.HasValue
? ScreenRectToClient(prcInputPaneScreenLocation.Value)
@ -76,7 +81,6 @@ internal unsafe class WindowsInputPane : InputPaneBase, IDisposable
if (_disposed)
return;
_disposed = true;
_windowImpl = null!;
if (_inputPane is not null)
{
if (_cookie != 0)
@ -87,6 +91,11 @@ internal unsafe class WindowsInputPane : InputPaneBase, IDisposable
_inputPane.Dispose();
_inputPane = null;
}
// Released only once Unadvise has returned, so the field stays valid for the whole of the
// teardown it is read during.
_windowImpl = null!;
// Suppress finalization.
GC.SuppressFinalize(this);
}

Loading…
Cancel
Save