2013-02-21 62 views
4

我正在使用WinRT。如果拋出未處理的異常,我想將消息文本寫入存儲。 我在'App.xaml.cs'中添加了一個事件處理程序,請參閱代碼。WinRT C#:無法將UnhandledException保存到存儲

捕獲到異常,但寫入文件的最後一行再次崩潰 - >'異常'!

爲什麼?任何想法?

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 ----- 
} 

感謝

陽光

+2

我的攝製。很難診斷,AppendTextAsync()調用以非常糟糕的方式失敗。我想你最好避免在這段代碼中使用* await *。 – 2013-02-21 14:23:01

+0

您認爲可能需要延期嗎? – 2013-02-22 06:47:26

回答

0

在這種情況下,await可以大致翻譯爲「做另一個線程這項工作,並繼續你在做什麼,你需要等待它完成。」鑑於你的應用程序正在做的是崩潰,你可能不希望它繼續這樣做,直到你完成日誌記錄問題。我建議在這種情況下同步運行你的文件IO。

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:[email protected]&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; 
     } 
    } 
} 

要實現它,你需要做的一樣上面的鏈接說,但要保證數據的這裏在案件的網址不斷下降:

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"); 

最後,你需要問用戶如果他們想在應用重新打開時發送電子郵件。在您的應用程序的默認頁面的構造函數(默認:頁面App.xaml.cs初始化):

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

或添加呼叫到您的OnLoad方法,如果你已經在使用它。

+1

你知道這是否適用於Windows 8和8.1應用程序? – Earlz 2014-03-27 20:27:52

+0

我無法確認它,因爲我沒有放置Windows 8.0機器,也沒有VS 2012.儘管我已經打開了一箇舊的8.0應用程序,並且LittleWatson代碼本身工作正常。但是,8.0似乎沒有「NavigationFailed」事件,因此您只能在App.xaml.cs中調用LittleWatson的UnhandledException事件 - rootFrame位不起作用。 – 2014-03-28 10:23:09

0

這可能會有點太晚了原來的問題,但...

爲@Hans帕桑特建議,避免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 
}