J
Jan Drozd
Guest
Hi to all members of MS community,
I am developing a video player application in C#/UWP for MS Store. Unfortunately, we face very strange buggy behavior of this app. It simply crashes in some occasions. Without any trace. Without OnUnhandledException or OnUnobservedException events being triggered. Let me summarize what we know and tried below:
Application properties
The Heisenbug - application crashes without any exception or crash trace when:
What we've tried:
First, I spent dozens of hours trying to isolate the bug and did cca 100 changes within code, but without success. A lot of things were tried, below the list of most important I think:
Conclusion
I assume we have very unique usecase (two views, secondary view for playback, custom playback controls, DRM, etc.) thus noone before us ran into these problems. I assume its some kind of bug in UWP, specifically with memory adresses. Any feedback, any tips to resolve this are greatly appreciated.
Code examples
Our application has more than 20 000 lines of code, thus I can't share it all. However, below our exception handling code, and also code opening the secondary view.
private async void OnUnobservedException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
using (SentrySdk.Init("https://2522d4b44b954769b0063392734b8b0d@o222939.ingest.sentry.io/5458120"))
{
if (e.Exception != null && e.Exception.Message != null &&
e.Exception.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.Message);
}
else if (e.Exception.InnerException != null && e.Exception.InnerException.Message
!= null && e.Exception.InnerException.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.InnerException.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.InnerException.Message);
}
else
{
SentrySdk.CaptureMessage("Unobserved exception without
stack trace happened");
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, "Unobserved
exception without stack trace happened");
}
}
}
catch
{
Environment.FailFast("An error occured whilst reporting an unboserved error.",
e.Exception);
}
}
private async void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
{
try
{
using (SentrySdk.Init("https://2522d4b44b954769b0063392734b8b0d@o222939.ingest.sentry.io/5458120"))
{
e.Handled = true;
if (e.Exception != null && e.Exception.Message != null && e.Exception.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.Message);
}
else if (e.Exception.InnerException != null && e.Exception.InnerException.Message != null && e.Exception.InnerException.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.InnerException.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.InnerException.Message);
}
else
{
SentrySdk.CaptureMessage("Exception without stack trace happened");
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, "Exception without stack trace happened");
}
}
}
catch
{
Environment.FailFast("An error occured whilst reporting an error.", e.Exception);
}
}
if (PlaybackProperties.CustomWindowViewID != 0)
{
try
{
await PlaybackProperties.ExternalCoreApplicationView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
if (!playnow)
PlaybackProperties.PlaybackPage.DisplayPlaylist = true;
await PlaybackProperties.PlaybackPage.StartCustomVideo(file, playnow);
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(PlaybackProperties.CustomWindowViewID);
AttachPlaybackHandlers();
});
}
catch { }
}
else
{
CoreApplicationView newView = CoreApplication.CreateNewView();
Frame frame = null;
PlaybackProperties.ExternalCoreApplicationView = newView;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
frame = new Frame();
frame.Navigate(typeof(PlaybackPage), null);
Window.Current.Content = frame;
// You have to activate the window in order to show it later.
Window.Current.Activate();
ApplicationView.GetForCurrentView().Consolidated += LibraryPage_Consolidated;
PlaybackProperties.CustomWindowViewID = ApplicationView.GetForCurrentView().Id;
PlaybackProperties.PlaybackPage = (PlaybackPage)frame.Content;
if (!playnow)
PlaybackProperties.PlaybackPage.DisplayPlaylist = true;
await PlaybackProperties.PlaybackPage.StartCustomVideo(file, playnow);
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(PlaybackProperties.CustomWindowViewID);
AttachPlaybackHandlers();
}
Continue reading...
I am developing a video player application in C#/UWP for MS Store. Unfortunately, we face very strange buggy behavior of this app. It simply crashes in some occasions. Without any trace. Without OnUnhandledException or OnUnobservedException events being triggered. Let me summarize what we know and tried below:
Application properties
- It's a video player application with movie library
- We do use UWP's MediaPlayerElement and also we do utilize PlayReady to protect the content
- We do open library at start and when playback is requested, we open secondary application view to run playback within - this is important feature to us - user can have fullscreen projection and control playback from his laptop without interfering with the movie playback
- We do use custom playback controls we designed for the app instead of using MS TransportControls
The Heisenbug - application crashes without any exception or crash trace when:
- We do have opened playback window. It's enough to have media loaded and paused.
- Crash occurs probably due to foreground/background app lifecycle - when app is on foreground, playback works for hours without issue.
- When we minimize/switch to background and return back to foreground multiple times, issue may occur (in most occasions it won't but if you continuosly work this way with app, crash happens within cca 15 minutes of testing this way)
- Issue never occurs when using debugger. It happens only when we are in Release configuration
What we've tried:
First, I spent dozens of hours trying to isolate the bug and did cca 100 changes within code, but without success. A lot of things were tried, below the list of most important I think:
- I made sure that our secondary view is always using the dispatcher for this new view
- I monitor events like MemoryManager.AppMemoryUsageLimitChanging and MemoryManager.AppMemoryUsageIncreased to see if we have reached the memory limit. These events are not ever triggered for our app. It consumes up to 300 MB of memory.
- I douse GC.Collect when switching to background to reduce memory usage on background
- I stop doing any UI changes when on background
- Everywhere (almost) I use multiple try/catch blocks. Especially when Dispatcher.RunAsync is used, i have try/catch around whole method and also try/catch within the method executed by dispatcher - no exception is triggered or written to log (despite we log each exception here)
- I have added extensive logging - I do log every method/function within app. From the logs I can see that there's not our code being executed during the crash.
- I do use App.OnUnhandledException and TaskScheduler.UnobservedTaskException to catch and log any exception caused by software. I do also have try/catch statement within those methods and use Environment.FailFast should the exception handling code fail
- I do check Event Viewer for each of the crashes (I reproduced them multiple times) and there's no trace of the crash within event log (Application, System). We had some crashes caused by PlayReady code in the past,but these always written something to event log. This time, there's simply nothing. No crash log created, no record within sentry.io, no record in event viewer.
- I've tested just our movie library (primary view) to minimize/maximize dozens of times (maybe hundreds) and app never crashed.
- I prevented execution a lot of our code during the stage when app is in the background, didn't help
- We made sure with colleagues we are able to have playback in the foreground without crashes for hours (tested on multiple devices)
- We are able to reproduce crash across all versions of Windows 10 we tested. I do use 20H2 version, tested also builds 1909 and other older builds.
- I followed [UWP] Get the crash dump file for my app but no crash dumps are ever created for such situation
- Crash happens for both DRM protected and unprotected media files (MKV, MP4, PIFF)
Conclusion
I assume we have very unique usecase (two views, secondary view for playback, custom playback controls, DRM, etc.) thus noone before us ran into these problems. I assume its some kind of bug in UWP, specifically with memory adresses. Any feedback, any tips to resolve this are greatly appreciated.
Code examples
Our application has more than 20 000 lines of code, thus I can't share it all. However, below our exception handling code, and also code opening the secondary view.
private async void OnUnobservedException(object sender, UnobservedTaskExceptionEventArgs e)
{
try
{
using (SentrySdk.Init("https://2522d4b44b954769b0063392734b8b0d@o222939.ingest.sentry.io/5458120"))
{
if (e.Exception != null && e.Exception.Message != null &&
e.Exception.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.Message);
}
else if (e.Exception.InnerException != null && e.Exception.InnerException.Message
!= null && e.Exception.InnerException.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.InnerException.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.InnerException.Message);
}
else
{
SentrySdk.CaptureMessage("Unobserved exception without
stack trace happened");
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, "Unobserved
exception without stack trace happened");
}
}
}
catch
{
Environment.FailFast("An error occured whilst reporting an unboserved error.",
e.Exception);
}
}
private async void OnUnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
{
try
{
using (SentrySdk.Init("https://2522d4b44b954769b0063392734b8b0d@o222939.ingest.sentry.io/5458120"))
{
e.Handled = true;
if (e.Exception != null && e.Exception.Message != null && e.Exception.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.Message);
}
else if (e.Exception.InnerException != null && e.Exception.InnerException.Message != null && e.Exception.InnerException.Message != "")
{
SentrySdk.CaptureMessage(e.Exception.InnerException.Message);
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, e.Exception.InnerException.Message);
}
else
{
SentrySdk.CaptureMessage("Exception without stack trace happened");
StorageFile crashlog = await Storage.RandomCrashLogFile();
await Windows.Storage.FileIO.WriteTextAsync(crashlog, "Exception without stack trace happened");
}
}
}
catch
{
Environment.FailFast("An error occured whilst reporting an error.", e.Exception);
}
}
if (PlaybackProperties.CustomWindowViewID != 0)
{
try
{
await PlaybackProperties.ExternalCoreApplicationView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
if (!playnow)
PlaybackProperties.PlaybackPage.DisplayPlaylist = true;
await PlaybackProperties.PlaybackPage.StartCustomVideo(file, playnow);
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(PlaybackProperties.CustomWindowViewID);
AttachPlaybackHandlers();
});
}
catch { }
}
else
{
CoreApplicationView newView = CoreApplication.CreateNewView();
Frame frame = null;
PlaybackProperties.ExternalCoreApplicationView = newView;
await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
frame = new Frame();
frame.Navigate(typeof(PlaybackPage), null);
Window.Current.Content = frame;
// You have to activate the window in order to show it later.
Window.Current.Activate();
ApplicationView.GetForCurrentView().Consolidated += LibraryPage_Consolidated;
PlaybackProperties.CustomWindowViewID = ApplicationView.GetForCurrentView().Id;
PlaybackProperties.PlaybackPage = (PlaybackPage)frame.Content;
if (!playnow)
PlaybackProperties.PlaybackPage.DisplayPlaylist = true;
await PlaybackProperties.PlaybackPage.StartCustomVideo(file, playnow);
});
bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(PlaybackProperties.CustomWindowViewID);
AttachPlaybackHandlers();
}
Continue reading...