Skip to content

UWP: static XAML events subscribed from a secondary CoreApplication view are invoked on the first subscriber's thread #2524

Description

@FrayxRulez

Summary

In a UWP app on modern .NET (<UseUwp>true</UseUwp>), a handler for a static XAML event subscribed from a secondary CoreApplication view is invoked on the first view that subscribed, not on the view that subscribed it.

CompositionTarget.Rendering is the clearest case: both views' handlers are driven by a single registration, so the secondary view's frame callback arrives on the main view's thread. Under .NET Native the handler ran on the subscribing view's thread.

Repro

protected override async void OnLaunched(LaunchActivatedEventArgs e)
{
    _mainThreadId = Environment.CurrentManagedThreadId;
    Window.Current.Content = new Grid();
    Window.Current.Activate();

    CompositionTarget.Rendering += OnMainRendering;

    var newView = CoreApplication.CreateNewView();
    int viewId = 0;

    await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        _secondaryThreadId = Environment.CurrentManagedThreadId;
        Window.Current.Content = new Grid();
        Window.Current.Activate();
        viewId = ApplicationView.GetForCurrentView().Id;

        CompositionTarget.Rendering += OnSecondaryRendering;   // subscribed on this view's thread
    });

    await ApplicationViewSwitcher.TryShowAsStandaloneAsync(viewId);
}

// each handler records Environment.CurrentManagedThreadId and increments a counter

Result

main thread 4 ticks 256
secondary thread 5 ticks 256
Rendering handlers ran on: main 4, secondary 4   <-- both on the main view's thread

Two independent view render loops would not stay in lockstep; the tick counts are exactly equal on every run (256/256, 258/258, 262/262), which is consistent with both delegates being invoked from one registration rather than one per view.

The ABI allows what the projection does not

Fetching the statics directly, on each view's thread:

RoGetActivationFactory("Windows.UI.Xaml.Media.CompositionTarget", IID_ICompositionTargetStatics)
  main      0x25B31BA1878
  secondary 0x25B31BA1878   (same object)

QueryInterface(IAgileObject) -> S_OK    (agile, so a call from either view runs on the calling thread)

So the statics object is a process-wide agile singleton, and add_Rendering invoked from the secondary view's thread would register against that view. The aggregation appears to happen above the ABI, in the per-statics event source cache: the second += does not produce a second add_Rendering, it appends the delegate to the registration the first view already made.

Consistent with that, the generated projection caches the statics in a plain static field (__objRef_global__Windows_UI_Xaml_Media_ICompositionTargetStatics), and Microsoft.Windows.UI.Xaml.dll contains no ThreadStatic/ThreadLocal anywhere.

Impact

Anything frame-driven in a secondary view runs on the wrong thread. Windows.UI.Composition objects tolerate it because they are agile, which masks the problem; XAML objects do not. In our app the visible symptom is that image animations in a secondary window freeze, because the handler ends up calling WriteableBitmap.Invalidate() on a bitmap created on the other view:

System.Runtime.InteropServices.COMException (0x8001010E)   // RPC_E_WRONG_THREAD
   at ABI.Windows.UI.Xaml.Media.Imaging.IWriteableBitmapMethods.Invalidate(IObjectReference _obj)
   at Windows.UI.Xaml.Media.Imaging.WriteableBitmap.Invalidate()

Any per-view static WinRT event is affected, not just this one.

Environment

  • Windows 11 10.0.26200
  • .NET SDK 10.0.302, net10.0-windows10.0.26100.0, UseUwp=true, x64, self-contained, no AOT
  • Microsoft.Windows.SDK.NET.Ref projection 10.0.26100.55
  • Microsoft.Windows.CsWinRT 2.2.0 referenced by the app

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions