4

我正在开发 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 -----
 }

谢谢

晴天

4

3 回答 3

4

我一直在想同样的事情,并且在搜索的早期就偶然发现了这一点。我想出了一个方法,希望这对其他人也有用。

问题是await返回对 UI 线程的控制和应用程序的崩溃。您需要延期,但没有真正的方法可以得到延期。

我的解决方案是改用设置存储。我假设大多数想要这样做的人都想做一些 LittleWatson 风格的事情,所以这里有一些从http://blogs.msdn.com/b/andypennell/archive/2010/11/01/error-reporting-on修改的代码-windows-phone-7.aspx为您提供方便:

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()):

this.UnhandledException += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");

显然,如果您已经有一个 UnhandledException 方法,您可以在其中抛出对 LittleWatson 的调用。

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

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

rootFrame.NavigationFailed += (s, e) => LittleWatson.ReportException(e.Exception, "extra message goes here");

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

this.Loaded += async (s, e) => await LittleWatson.CheckForPreviousException();

或者,如果您已经使用了 OnLoad 方法,请添加对它的调用。

于 2014-01-30T22:05:05.343 回答
0

在这种情况下,await可以粗略地翻译为“在另一个线程上完成这项工作,并在等待它完成的同时继续你正在做的事情”。鉴于您的应用程序正在崩溃,您可能不希望它继续这样做,直到您完成记录问题。在这种情况下,我建议同步运行您的文件 IO。

于 2013-07-25T21:21:03.350 回答
0

对于最初的问题来说,这可能来得太晚了,但是......

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

将同步选项放在一边,以下await实现对我有用:

更改await FileIO.AppendTextAsync(file, e.Message);为:

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
}
于 2017-01-28T08:11:09.770 回答