Skip to content

Close tabs on the next dispatcher turn - #3412

Open
FrayxRulez wants to merge 2 commits into
developfrom
tab-close-touch
Open

Close tabs on the next dispatcher turn#3412
FrayxRulez wants to merge 2 commits into
developfrom
tab-close-touch

Conversation

@FrayxRulez

@FrayxRulez FrayxRulez commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

ArgumentException: The parameter is incorrect. (0x80070057), reported by crash telemetry on
12.10.2.0, 12.10.1.0, 12.9.1.0 and 12.8.1.0 — the same call site under both the .NET Native and
the CsWinRT runtimes, so it is not new and not runtime-specific.

Where

   4  winrt::throw_hresult
   5  winrt::impl::consume_...IControlOverrides<...>::OnPointerExited     [ICF-folded shim]
   6  TabViewListView::OnItemsChanged                 dev/TabView/TabViewListView.cpp:48
   7  winrt::impl::produce<TabViewListView,IItemsControlOverrides>::OnItemsChanged
   8  DirectUI::ItemsControlGenerated::OnItemsChangedProtected
   9  ctl::event_handler_base<...>::Invoke
  10  DirectUI::CEventSourceBase<...VectorChangedEventHandler...>::Raise
  11  DirectUI::ItemCollection::OnCollectionChanged
  12  DirectUI::ItemCollection::RaiseVectorChanged
  13  DirectUI::ItemCollection::RemoveAt
  14  ABI...IVectorMethods`1.RemoveAt
  16  ABI...IListMethods`1.Remove
  17  Telegram.Views.Host.TabbedWindow.OnTabCloseRequested

and, below the event:

  25  TabView::RequestCloseTab                        dev/TabView/TabView.cpp:1014
  26  TabViewItem::RequestClose                       dev/TabView/TabViewItem.cpp:349
  ..  DirectUI::ButtonBase::OnClick / OnPointerReleased
  50  CInputServices::RaiseDelayedPointerUpEvent
  51  CInputServices::ProcessGestureInput
  52  CInputServices::ProcessTouchInteractionCallback
  53  TIEAdapter::InteractionEngineCallback

Cause

The index we pass is not the problem: Remove resolved the item and got as far as
ItemCollection::RemoveAt, and the E_INVALIDARG comes back out of XAML's own
items-changed notification — RaiseVectorChangedTabViewListView::OnItemsChanged
__super::OnItemsChanged, the base ItemsControl implementation.

What every symbolicated sample has in common is how the event was delivered:
CInputServices::ProcessTouchInteractionCallbackProcessGestureInput
RaiseDelayedPointerUpEvent. This is the touch path. XAML holds the pointer-up back
during a touch interaction to disambiguate tap from pan, then raises it from inside the
gesture engine's own callback, re-entering the dispatcher through a nested SendMessageW.
TabCloseRequested therefore reaches us while XAML is still walking the tree that the tab
we are about to remove is part of, and the removal fails there. No sample arrives on the
ordinary mouse ProcessPointerInput path, which is consistent with this only being
reproducible by touch.

Fix

Post the close to the next dispatcher turn instead of mutating TabItems inside the input
callback. The count check moves with it, so it is evaluated when the removal actually happens
rather than one turn earlier.

Honest limits

The failing call returned an HRESULT; its own frames are unwound by the time C++/WinRT
throws, so the precise precondition that ItemsControl rejects is not recoverable from the
reports. What is established is the site, that the failure is the framework's reaction to the
removal rather than a bad argument from us, and that all four symbolicated samples arrive
through the touch gesture engine. The change is sequencing, not a guard: if the mechanism is
something else, the crash will keep reporting from the dispatcher turn instead of disappearing.

Not built or run — parsed with Roslyn (CSharpSyntaxTree.ParseText, no syntax
diagnostics), which is a syntax check only, not a type check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant