WinRT C#:无法将未处理的异常保存到存储

WinRT C#:无法将未处理的异常保存到存储,c#,winrt-xaml,C#,Winrt Xaml,我正在研究WinRT。如果抛出未处理的异常,我想将消息文本写入存储器。 我在App.xaml.cs中添加了一个事件处理程序,请参见代码 异常被捕获,但写入文件的最后一行再次崩溃->异常 为什么??有什么想法吗 public App() { this.InitializeComponent(); this.Suspending += OnSuspending; this.UnhandledException += App_UnhandledException; }

我正在研究WinRT。如果抛出未处理的异常,我想将消息文本写入存储器。 我在App.xaml.cs中添加了一个事件处理程序,请参见代码

异常被捕获,但写入文件的最后一行再次崩溃->异常

为什么??有什么想法吗

 public App()
 {
    this.InitializeComponent();
    this.Suspending += OnSuspending;
    this.UnhandledException += App_UnhandledException;
 }

 async void App_UnhandledException(object sender, UnhandledExceptionEventArgs e)
 {
    StorageFolder folder = Windows.Storage.ApplicationData.Current.LocalFolder; 
    StorageFile file= await folder.CreateFileAsync("crash.log",CreationCollisionOption.OpenIfExists);

    await FileIO.AppendTextAsync(file, e.Message);  // <----- crash again -----
 }
public应用程序()
{
this.InitializeComponent();
这个.Suspending+=OnSuspending;
this.UnhandledException+=App_UnhandledException;
}
异步void App_UnhandledException(对象发送方,UnhandledExceptionEventArgs e)
{
StorageFolder folder=Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile file=wait folder.CreateFileAsync(“crash.log”,CreationCollisionOption.OpenIfExists);

await FileIO.AppendTextAsync(file,e.Message);//在这种情况下,
await
可以粗略地翻译为“在另一个线程上执行此任务,并在等待它完成时继续您正在执行的操作”。考虑到你的应用程序正在崩溃,你可能不希望它继续这样做,直到你完成记录问题。我建议在这种情况下同步运行你的文件IO。

我一直在想同样的事情,并在搜索的很早就发现了这一点。我已经找到了一种方法,希望这能证明它的用处对其他人也是如此

问题是,
wait
正在返回对UI线程的控制,应用程序正在崩溃。你需要延迟,但没有真正的方法获得延迟

我的解决方案是使用设置存储。我假设大多数想要这样做的人都想做一些LittleWatson风格的事情,因此为了方便起见,这里有一些代码修改自:

namespace YourApp
{
    using Windows.Storage;
    using Windows.UI.Popups;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;

    public class LittleWatson
    {
        private const string settingname = "LittleWatsonDetails";
        private const string email = "mailto:?to=you@example.com&subject=YourApp auto-generated problem report&body=";
        private const string extra = "extra", message = "message", stacktrace = "stacktrace";

        internal static void ReportException(Exception ex, string extraData)
        {
            ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
            var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;

            exceptionValues[extra] = extraData;
            exceptionValues[message] = ex.Message;
            exceptionValues[stacktrace] = ex.StackTrace;
        }

        internal async static Task CheckForPreviousException()
        {
            var container = ApplicationData.Current.LocalSettings.Containers;
            try
            {
                var exceptionValues = container[settingname].Values;
                string extraData = exceptionValues[extra] as string;
                string messageData = exceptionValues[message] as string;
                string stacktraceData = exceptionValues[stacktrace] as string;

                var sb = new StringBuilder();
                sb.AppendLine(extraData);
                sb.AppendLine(messageData);
                sb.AppendLine(stacktraceData);

                string contents = sb.ToString();

                SafeDeleteLog();

                if (stacktraceData != null && stacktraceData.Length > 0)
                {
                    var dialog = new MessageDialog("A problem occured the last time you ran this application. Would you like to report it so that we can fix the error?", "Error Report")
                    {
                        CancelCommandIndex = 1,
                        DefaultCommandIndex = 0
                    };

                    dialog.Commands.Add(new UICommand("Send", async delegate
                    {
                        var mailToSend = email.ToString();
                        mailToSend += contents;
                        var mailto = new Uri(mailToSend);
                        await Windows.System.Launcher.LaunchUriAsync(mailto);
                    }));
                    dialog.Commands.Add(new UICommand("Cancel"));

                    await dialog.ShowAsync();
                }
            }
            catch (KeyNotFoundException)
            {
                // KeyNotFoundException will fire if we've not ever had crash data. No worries!
            }
        }

        private static void SafeDeleteLog()
        {
            ApplicationData.Current.LocalSettings.CreateContainer(settingname, Windows.Storage.ApplicationDataCreateDisposition.Always);
            var exceptionValues = ApplicationData.Current.LocalSettings.Containers[settingname].Values;

            exceptionValues[extra] = string.Empty;
            exceptionValues[message] = string.Empty;
            exceptionValues[stacktrace] = string.Empty;
        }
    }
}
要实现它,您需要执行与上面链接所述相同的操作,但要确保数据在这里,以防url下降:

App.xaml.cs
构造函数(在调用
this.InitializeComponent()
之前):

显然,如果你已经有了一个未处理的异常方法,你可以在那里抛出对LittleWatson的调用

如果您使用的是Windows 8.1,也可以添加NavigationFailed调用。这需要在实际页面中(通常是MainPage.xaml.cs或第一次打开的任何页面):

xx.xaml.cs
构造函数(任何给定页面):

最后,您需要询问用户是否希望在应用程序重新打开时发送电子邮件。在应用程序的默认页面的构造函数中(默认:app.xaml.cs页面初始化):


或者,如果您已经使用了OnLoad方法,则将调用添加到该方法。

对于原始问题来说,这可能有点太晚了,但是

正如@Hans Passant所建议的,避免
wait
(即同步运行
FileIO.AppendTextAsync()
),这也是@Jon所支持的,我会选择这个,而不是LittleWatson相对太重的代码。因为应用程序无论如何都处于某种错误处理状态(这应该很少发生)我不会把同步产生的任何阻塞(由于删除wait)作为主要的缺点

将同步选项放在一边,下面的
wait
实现对我很有效:

等待文件io.AppendTextAsync(文件,e.Message);
更改为:

Task task = LogErrorMessage(file, e.Message)
task.Wait(2000); // adjust the ms value as appropriate


很难诊断AppendTextAsync()呼叫失败的方式非常糟糕。我想你最好避免在这段代码中使用wait。你认为这里可能需要延迟吗?你知道这是否适用于Windows 8和8.1应用程序吗?我无法确认,因为我没有Windows 8.0机器,也没有VS 2012。不过,我打开了一个旧的8.0应用程序,LittleWatson代码本身也可以正常工作e、 但是,8.0似乎没有“NavigationFailed”事件,因此您只能在App.xaml.cs中调用LittleWatson的未处理异常事件-根帧位将不起作用。
rootFrame.NavigationFailed += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");
this.Loaded += async (s, e) => await LittleWatson.CheckForPreviousException();
Task task = LogErrorMessage(file, e.Message)
task.Wait(2000); // adjust the ms value as appropriate
private async Task LogErrorMessage(StorageFile file, string errorMessage)
{
    await FileIO.AppendTextAsync(file, errorMessage); // this shouldn't crash in App_UnhandledException as it did before
}