/// <summary> /// Handles the <see cref="Application.DispatcherUnhandledException"/> event.</summary> /// <param name="information"> /// Information about the <see cref="Exception"/> and the current application state, or a /// null reference for no available information.</param> /// <param name="rulesFile"> /// The file path to the current scenario rule script, or a null reference if no such file /// exists.</param> /// <param name="saveScenario"> /// An optional <see cref="Action{String}"/> delegate that is invoked to save the current /// scenario data to an XML file.</param> /// <param name="saveSession"> /// An optional <see cref="Action{String}"/> delegate that is invoked to save the current /// session data to an XML file.</param> /// <remarks><para> /// <b>OnUnhandledException</b> creates a text file describing the error which includes the /// specified <paramref name="information"/>. Additionally, up to two debug XML files /// containing scenario and session data are created by invoking any of the supplied <see /// cref="Action{String}"/> delegates that are not null references. /// </para><para> /// The user is also asked to e-mail all created files, plus the specified <paramref /// name="rulesFile"/> if it exists, to the e-mail address specified by the <see /// cref="ApplicationInfo.Email"/> property via Simple MAPI, or if this fails, to send basic /// error information to the same address using the Explorer "mailto" method. /// </para><para> /// Finally, <b>OnUnhandledException</b> terminates the application using <see /// cref="Environment.Exit"/> and returns an error code of -1.</para></remarks> public static void OnUnhandledException(string information, string rulesFile, Action <String> saveScenario, Action <String> saveSession) { // check for original distribution package bool original = ApplicationInfo.IsSigned; // determine error and debug file names string errorFile = FilePaths.CreateUserPath("FatalError.txt").AbsolutePath; string scenarioFile = FilePaths.GetScenarioFile(ScenarioFileType.Debug).AbsolutePath; string sessionFile = FilePaths.GetSessionFile(SessionFileType.Debug).AbsolutePath; // create an error e-mail? bool wantEmail = false; if (original) { // ask user to create e-mail with attachments string message = String.Format(ApplicationInfo.Culture, Strings.DialogFatalErrorOriginal, errorFile, scenarioFile, sessionFile); wantEmail = ShowFatalError(message, null, true); } else { // just notify user that files are being saved string message = String.Format(ApplicationInfo.Culture, Strings.DialogFatalErrorModified, errorFile, scenarioFile, sessionFile); ShowFatalError(message, null, false); } // create state information string StringBuilder info = new StringBuilder(); info.AppendFormat("{0} ", ApplicationInfo.Signature); info.Append(Environment.NewLine); info.AppendFormat("Kynosarges Signature: {0}", original); info.Append(Environment.NewLine); info.AppendFormat("Public Key Token: {0}", StringUtility.Validate(ApplicationInfo.PublicKeyToken)); info.Append(Environment.NewLine); info.AppendFormat("Home: \"{0}\" ", FilePaths.ApplicationFolder); info.Append(Environment.NewLine); info.Append(Environment.NewLine); info.AppendFormat("{0} ", Environment.OSVersion); info.Append(Environment.NewLine); info.AppendFormat("{0} ", WindowsUtility.GetMemoryStatus()); info.Append(Environment.NewLine); info.Append(Environment.NewLine); // append additional information if specified if (!String.IsNullOrEmpty(information)) { info.AppendFormat("{0} ", information); info.Append(Environment.NewLine); } // create subject for e-mail message string subject = String.Concat(ApplicationInfo.Signature, " ", Strings.LabelError); // collection to hold file attachment data List <MapiAddress> files = new List <MapiAddress>(); try { try { // always create error information file files.Add(new MapiAddress(Path.GetFileName(errorFile), errorFile)); using (StreamWriter writer = new StreamWriter(errorFile, false, Encoding.UTF8)) writer.WriteLine(info.ToString()); // attach rule script file if specified RootedPath rulesPath = FilePaths.CreateCommonPath(rulesFile); if (!rulesPath.IsEmpty && File.Exists(rulesPath.AbsolutePath)) { files.Add(new MapiAddress(rulesPath.FileName, rulesPath.AbsolutePath)); } // create scenario debug file if possible if (saveScenario != null) { saveScenario(scenarioFile); files.Add(new MapiAddress(Path.GetFileName(scenarioFile), scenarioFile)); } // create session debug file if possible if (saveSession != null) { saveSession(sessionFile); files.Add(new MapiAddress(Path.GetFileName(sessionFile), sessionFile)); } } catch (Exception e) { // only proceed if user wanted e-mail if (wantEmail) { // ask user to try simpler e-mail format wantEmail = ShowFatalError(Strings.DialogFatalErrorSaveMail, e, true); throw; // rethrow to try mailto: } // user declined e-mail, just show error ShowFatalError(Strings.DialogFatalErrorSave, e, false); return; // quit, nothing else to do } // quit now if user declined e-mail if (!wantEmail) { return; } try { // address e-mail to application author MapiAddress recipient = new MapiAddress( ApplicationInfo.Company, ApplicationInfo.Email); // try sending e-mail with attached files MapiMail.SendMail(subject, "", new[] { recipient }, files.ToArray()); } catch (Exception e) { // quit if user cancelled MAPI session MapiException me = e as MapiException; if (me != null && me.Code == MapiException.Abort) { return; } // ask user to try simpler e-mail format wantEmail = ShowFatalError(Strings.DialogFatalErrorMail, e, true); throw; // rethrow to try mailto: } } catch { // quit now if user declined e-mail if (!wantEmail) { return; } // construct shell command to create e-mail string mailto = String.Format(CultureInfo.InvariantCulture, "mailto:{0}?subject={1}&body={2}", ApplicationInfo.Email, subject, info); try { // request default e-mail client Process.Start(mailto); } catch (Exception e) { ShowExplorerError(null, Strings.DialogExplorerMailError, e); } } finally { // quit application upon return Environment.Exit(-1); } }