microsoft / CsWinRT

C# language projection for the Windows Runtime
MIT License
553 stars 105 forks source link

Getting LaunchActivatedEventArgs crashes in release mode #1342

Open lhak opened 2 years ago

lhak commented 2 years ago

Describe the bug

See the attached repro project. When getting the activation arguments in Main (App.xaml.cs), the application will crash with a System.NullReferenceException. Please note that this only happens in Release (x64) mode with PublishReadyToRun enabled (the default value). ActivationArgsRepro.zip

The following change seems to somehow workaround this issue

AppActivationArguments activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs();
Windows.ApplicationModel.Activation.LaunchActivatedEventArgs launchArgs = activatedArgs.Data as Windows.ApplicationModel.Activation.LaunchActivatedEventArgs;
launchArgs.Kind.ToString(); // Add this line to avoid a crash
if (launchArgs.Arguments.StartsWith("argument"))
{

}

Steps to reproduce the bug

  1. Run the attached repro project in Release mode (x64)

Expected behavior

No response

Screenshots

No response

NuGet package version

1.1.4

Packaging type

Packaged (MSIX)

Windows version

Windows 11 version 21H2 (22000)

IDE

Visual Studio 2022

Additional context

No response

dhoehna commented 2 years ago

This is reproable. @lhak thank you for the repro project.

After doing some light debugging the error is coming from Windows.ApplicationModel.Activation.LaunchActivatedEventArgs launchArgs = activatedArgs.Data as Windows.ApplicationModel.Activation.LaunchActivatedEventArgs; This throws Null Reference Extension.

What is odd is that activatedArgs.Data isn't null. Found with a if(activatedArgs.Data == null) {log null} else {log not null}

Next, I added activatedEventArgs.Kind.ToString(); Fixed the issue. I found it odd though, that adding a line, that does nothing, makes this work.

I tried something else. Instead of activatedEventArgs.Kind.ToString() I did Thread.Sleep(1000). App runs fine.

Looks like an optimization, somewhere, is causing a race condition where args.Data isn't null, yet, casting it causes a null reference exception. @MikeHillberg Does a race condition with WinUI3 sound familiar?

dhoehna commented 2 years ago

Changing as to a cast throws an error, even with THread.Sleep.

var activatedArgsAsSomethingLese = (Windows.ApplicationModel.Activation.LaunchActivatedEventArgs)activatedEventArgs.Data;

Still runs fine in debug.

dhoehna commented 2 years ago

Getting closer.

I printed the .ToString of 1activatedEventArgs.Data` Here are the results Debug: Windows.ApplicationModel.Activation.LaunchActivatedEventArgs Release (No sleeping): WinRT.IInspectable Release (Sleeping): WinRT.IInspectable

dhoehna commented 2 years ago

Combining AppActivationArguments activatedArgs = AppInstance.GetCurrent().GetActivatedEventArgs(); and Windows.ApplicationModel.Activation.LaunchActivatedEventArgs launchArgs = activatedArgs.Data as Windows.ApplicationModel.Activation.LaunchActivatedEventArgs;

Into MyData = AppInstance.GetCurrent().GetActivatedEventArgs().Data as Windows.ApplicationModel.Activation.LaunchActivatedEventArgs;

does not crash in release.

dhoehna commented 2 years ago

@lhak Combining the two calls into one works for mitigating the issue.

I'm still looking into why this is happening.

manodasanW commented 1 year ago

The issue looks to be the Windows SDK projection assembly where Windows.ApplicationModel.Activation.LaunchActivatedEventArgs lives has not been already loaded when the Data property is called. This is because Data is marshaled as an object across the WinRT ABI and there has been no other references to other types from that assembly yet. This means when we try to create a concrete type of LaunchActivatedEventArgs, we fail to as we don't see it in the loaded projection assemblies. So we instead create it as an IInspectable. Later when the as operator is used to cast it, that fails because dynamic casting from IInspectable is only supported with interfaces and not classes.

Moving this to the CsWinRT repo for tracking. One way to address it is to special case the Windows SDK projection to look up types there even if it is not loaded. But that probably won't scale to other projection binaries where this technically can happen too.