Globals.cs
author Thomas
Mon, 05 Sep 2016 15:37:27 +0200
changeset 1239 5077ab4e477e
parent 1235 8a0ec5c5088e
child 1245 abec57ca48d4
permissions -rw-r--r--
Moved gpa path finding methods to Globals
     1 using Microsoft.Win32;
     2 using pEp.UI;
     3 using System;
     4 using System.Collections.Generic;
     5 using System.IO;
     6 using System.Reflection;
     7 using System.Runtime.InteropServices;
     8 using System.Text;
     9 using System.Threading;
    10 using System.Windows;
    11 using System.Windows.Forms;
    12 using Office = Microsoft.Office.Core;
    13 
    14 namespace pEp
    15 {
    16     /// <summary>
    17     /// Class to store all global/shared data and methods.
    18     /// This class is a shared partial class with the internally generated code.
    19     /// </summary>
    20     internal sealed partial class Globals
    21     {
    22         public const string CUSTOM_SENT_FOLDER_NAME    = "Sent";
    23         public const string LOG_FILE_NAME              = "log.txt";
    24         public const int    LOG_LINES_TO_REPLACE       = 50;                              // The number of lines to replace when the log file gets cleaned up
    25         public const int    LOG_MAX_LINES              = 1000;                            // The maximum lines a log file can reach before the first lines get replaced (FIFO)
    26         public const string PEP_COPYRIGHT              = "Copyright © 2014-2016 p≡p Security SA, Luxembourg";
    27         public const string PEP_DATA_FILE_NAME         = "pEp";                           // The file name of the pEp data store. This does NOT include extension (.pst)
    28         public const string PEP_DISPLAY_NAME           = "p≡p for Outlook";               // Display name to the user
    29         public const string PEP_DISPLAY_NAME_READER    = "p≡p Reader for Outlook";        // Display name to the user
    30 #if DEBUG
    31         public const string PEP_NAME_ADDIN_DESC        = "pEp";                           // Description of the add-in within Outlook itself (shared with reader)
    32 #else
    33         public const string PEP_NAME_ADDIN_DESC        = "pEp for Outlook";               // Description of the add-in within Outlook itself (shared with reader)
    34 #endif
    35         public const string PEP_NAME_INSTALL           = "pEp for Outlook";               // Name for Windows installer, must sync with installation code (shared with reader)
    36         public const string PEP_WEBSITE_LINK           = "https://prettyeasyprivacy.com/";
    37         public const string PEP_WEBSITE_UPGRADE_LINK   = "https://pep.security/buy/";
    38 
    39 #if READER_RELEASE_MODE
    40         public const ReleaseMode RELEASE_MODE = ReleaseMode.Reader;
    41 #else
    42         public const ReleaseMode RELEASE_MODE = ReleaseMode.Standard;
    43 #endif
    44 
    45         // Engine translations
    46         public const int PHRASE_ID_TRUSTWORDS_LANGUAGE = 1000;
    47 
    48         /// <summary>
    49         /// Enumeration to define the supported release modes.
    50         /// </summary>
    51         public enum ReleaseMode
    52         {
    53             Standard,
    54             Reader
    55         }
    56 
    57         /// <summary>
    58         /// Enumeration to define the return status of methods.
    59         /// </summary>
    60         public enum ReturnStatus
    61         {
    62             /// <summary>
    63             /// An unspecified failure occured.
    64             /// </summary>
    65             Failure,
    66 
    67             /// <summary>
    68             /// A failure occured because there is no internet or Exchange connection.
    69             /// </summary>
    70             FailureNoConnection,
    71 
    72             /// <summary>
    73             /// Successfully completed with no unexpected failures.
    74             /// </summary>
    75             Success
    76         }
    77 
    78         private static bool               eventsAreConnected = false;
    79         private static int                logLineCount       = 0;
    80         private static StreamWriter       logWriter          = null;
    81         private static object             mutexLogFile       = new object();
    82         private static ResourceDictionary _ResourceDict      = null;
    83 
    84         /**************************************************************
    85          * 
    86          * Property Accessors
    87          * 
    88          *************************************************************/
    89 
    90         /// <summary>
    91         /// Gets the shared XAML resource dictionary.
    92         /// </summary>
    93         public static ResourceDictionary ResourceDict
    94         {
    95             get
    96             {
    97                 if (_ResourceDict == null)
    98                 {
    99                     // Load resource dictionary
   100                     _ResourceDict = new ResourceDictionary();
   101                     _ResourceDict.Source = new Uri("pack://application:,,,/pEp;component/Resources/Dictionary.xaml", UriKind.RelativeOrAbsolute);
   102 
   103                     return (_ResourceDict);
   104                 }
   105                 else
   106                 {
   107                     return (_ResourceDict);
   108                 }
   109             }
   110         }
   111 
   112         /**************************************************************
   113          * 
   114          * Methods
   115          * 
   116          *************************************************************/
   117 
   118         /// <summary>
   119         /// Builds a crash report and displays it to the user before sending. 
   120         /// </summary>
   121         /// <param name="exception">The exception to build the report with.</param>
   122         /// <param name="summaryMessage">The message summarizing where the error occured or what it is.</param>
   123         /// <param name="allowRestart">True to allow the add-in to attempt to restart itself.</param>
   124         /// <param name="isEngineCrash">True if the engine generated the error. This will not attempt to get
   125         /// an engine log as the engine itself is not working (avoids infinite loop).</param>
   126         public static void StopAndSendCrashReport(Exception exception = null,
   127                                                   string summaryMessage = null,
   128                                                   bool allowRestart = true,
   129                                                   bool isEngineCrash = false)
   130         {
   131             bool continueSend = true;
   132             string engineLog = "";
   133             string log = "";
   134             FormCrashReport form;
   135             FormControlCrashReport.State stateIn = null;
   136             DialogResult result;
   137             Office.COMAddIns comAddIns = null;
   138             Office.COMAddIn addIn = null;
   139             PEPMessage newMessage;
   140             PEPAttachment attachment = null;
   141 
   142             // Show the report before sending
   143             if (continueSend)
   144             {
   145                 stateIn = new FormControlCrashReport.State();
   146                 stateIn.AddressTo = Globals.ThisAddIn.Settings.CrashReportSendAddress;
   147                 stateIn.AddressFrom = null; // A message can be sent without a "from" address
   148 
   149                 // Get log
   150                 try
   151                 {
   152                     log = string.Join(Environment.NewLine, Globals.ReadLogFile());
   153                 }
   154                 catch
   155                 {
   156                     log = "";
   157                 }
   158 
   159                 // Get engine log
   160                 if (isEngineCrash == false)
   161                 {
   162                     try
   163                     {
   164                         engineLog = ThisAddIn.PEPEngine.get_crashdump_log();
   165                     }
   166                     catch (COMException ex)
   167                     {
   168                         engineLog = ex.ToString();
   169                     }
   170                     catch (Exception ex)
   171                     {
   172                         engineLog = ex.ToString();
   173                     }
   174                 }
   175 
   176                 // Double check for null log files
   177                 if (log == null) { log = ""; }
   178                 if (engineLog == null) { engineLog = ""; }
   179 
   180                 // Build report header
   181                 stateIn.ReportText = string.Join(Environment.NewLine, new string[]
   182                     {
   183                         "Summary---------------------------------------------------",
   184                         "",
   185                         "Date: " + System.DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss K"),
   186                         Globals.GetSystemInfo(),
   187                         "",
   188                         (string.IsNullOrWhiteSpace(summaryMessage) ? "no message" : summaryMessage),
   189                         "",
   190                         "Exception Details-----------------------------------------",
   191                         "",
   192                         (exception == null ? "no exception" : exception.ToString()),
   193                         "",
   194                         "p≡p for Outlook Log---------------------------------------",
   195                         "",
   196                         log,
   197                         "",
   198                         "p≡p Engine Log--------------------------------------------",
   199                         "",
   200                         engineLog,
   201                         "",
   202                         "END OF REPORT"
   203                     });
   204 
   205                 if (Globals.ThisAddIn.Settings.IsCrashReportVisible)
   206                 {
   207                     form = new FormCrashReport();
   208                     form.StartPosition = FormStartPosition.CenterParent;
   209                     form.FormControl.DisplayState = stateIn;
   210                     result = form.ShowDialog();
   211 
   212                     if (result != DialogResult.OK)
   213                     {
   214                         continueSend = false;
   215                     }
   216                 }
   217             }
   218 
   219             // Build and send message
   220             if (continueSend)
   221             {
   222                 newMessage = new PEPMessage();
   223                 newMessage.ShortMsg = "Crash Report";
   224                 newMessage.To.Add(new PEPIdentity(stateIn.AddressTo));
   225                 newMessage.LongMsg = stateIn.ReportText;
   226                 newMessage.Direction = pEpCOMServerAdapterLib._pEp_msg_direction.pEp_dir_outgoing;
   227 
   228                 // Add logs as attachments
   229                 if (string.IsNullOrEmpty(log) == false)
   230                 {
   231                     attachment = new PEPAttachment();
   232                     attachment.Data = Encoding.UTF8.GetBytes(log);
   233                     attachment.FileName = "pEp Log.txt";
   234                     newMessage.Attachments.Add(attachment);
   235                 }
   236 
   237                 if (string.IsNullOrEmpty(engineLog) == false)
   238                 {
   239                     attachment = new PEPAttachment();
   240                     attachment.Data = Encoding.UTF8.GetBytes(engineLog);
   241                     attachment.FileName = "pEp Engine Log.txt";
   242                     newMessage.Attachments.Add(attachment);
   243                 }
   244 
   245                 // Try to send the message but ignore any errors
   246                 try
   247                 {
   248                     Globals.ThisAddIn.SendWithoutProcessing(newMessage, true);
   249                 }
   250                 catch { }
   251             }
   252 
   253             // Stop logging
   254             Globals.CloseLogFile();
   255 
   256             // Locate the Outlook add-in
   257             comAddIns = Globals.ThisAddIn.Application.COMAddIns;
   258             foreach (Office.COMAddIn addin in comAddIns)
   259             {
   260                 if (string.Equals(addin.Description, Globals.PEP_NAME_ADDIN_DESC, StringComparison.OrdinalIgnoreCase))
   261                 {
   262                     addIn = addin;
   263                     break;
   264                 }
   265             }
   266 
   267             // Shutdown add-in
   268             if (addIn != null)
   269             {
   270                 addIn.Connect = false;
   271             }
   272 
   273             return;
   274         }
   275 
   276         /// <summary>
   277         /// Connects or disconnects all global error handling events.
   278         /// </summary>
   279         /// <param name="connect">True to connect events, false to disconnect.</param>
   280         public static void ConnectEvents(bool connect)
   281         {
   282             // Connect events only if not already connected
   283             if ((connect == true) &&
   284                 (eventsAreConnected == false))
   285             {
   286                 System.Windows.Forms.Application.ThreadException += Application_UnhandledException;
   287                 AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
   288 
   289                 eventsAreConnected = true;
   290             }
   291             // Always attempt to disconnect
   292             else if (connect == false)
   293             {
   294                 System.Windows.Forms.Application.ThreadException -= Application_UnhandledException;
   295                 AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
   296 
   297                 eventsAreConnected = false;
   298             }
   299 
   300             return;
   301         }
   302 
   303         /// <summary>
   304         /// Adds the given text to the pEp for Outlook log.
   305         /// </summary>
   306         /// <param name="text">The text to add to the log.</param>
   307         public static void Log(string text)
   308         {
   309             Globals.WriteToLogFile(text);
   310             return;
   311         }
   312 
   313         /// <summary>
   314         /// Adds the given verbose text to the pEp for Outlook log only
   315         /// when verbose logging is enabled.
   316         /// </summary>
   317         /// <param name="text">The verbose text to add to the lob.</param>
   318         public static void LogVerbose(string text)
   319         {
   320             if ((Globals.ThisAddIn != null) &&
   321                 (Globals.ThisAddIn.Settings.IsVerboseLoggingEnabled))
   322             {
   323                 Globals.WriteToLogFile(System.DateTime.Now.ToString("HH:mm:ss.fff") + " | " + text);
   324             }
   325             return;
   326         }
   327 
   328         /// <summary>
   329         /// Gets the current system information string.
   330         /// </summary>
   331         /// <returns>The system information as a string.</returns>
   332         public static string GetSystemInfo()
   333         {
   334             string result = "";
   335             List<KVPair<string, string>> systemInfo;
   336             List<string> list = new List<string>();
   337 
   338             try
   339             {
   340                 systemInfo = Globals.GetSystemInfoList();
   341 
   342                 for (int i = 0; i < systemInfo.Count; i++)
   343                 {
   344                     list.Add(systemInfo[i].Key + " " + systemInfo[i].Value);
   345                 }
   346 
   347                 result = string.Join(Environment.NewLine, list);
   348             }
   349             catch { }
   350 
   351             return (result);
   352         }
   353 
   354         /// <summary>
   355         /// Gets the current system information as a list of Key-Value pairs.
   356         /// </summary>
   357         /// <returns>The system information as a list.</returns>
   358         public static List<KVPair<string, string>> GetSystemInfoList()
   359         {
   360             string serial;
   361             string engineVersion;
   362             List<KVPair<string, string>> systemInfo = new List<KVPair<string, string>>();
   363             Office.LanguageSettings lang = null;
   364 
   365             try
   366             {
   367                 lang = Globals.ThisAddIn.Application.LanguageSettings;
   368 
   369                 // Get the engine version
   370                 try
   371                 {
   372                     engineVersion = ThisAddIn.PEPEngine.get_engine_version();
   373                 }
   374                 catch
   375                 {
   376                     engineVersion = null;
   377                 }
   378 
   379                 // Use only the first 10-digits which is the serial itself
   380                 serial = Properties.Settings.Default.Serial;
   381                 serial = (serial != null ? serial : "");
   382                 serial = (serial.Length > 10) ? serial.Substring(0, 10) : serial;
   383 
   384                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_Serial, (Globals.RELEASE_MODE == Globals.ReleaseMode.Reader ? Properties.Resources.SystemInfo_Reader : serial)));
   385                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_AssemblyVersion, Assembly.GetExecutingAssembly().GetName().Version.ToString()));
   386                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_OutlookVersion, Globals.ThisAddIn.Application.Version));
   387                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_OSVersion, Environment.OSVersion.VersionString));
   388                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_PEPEngineVersion, (engineVersion != null ? engineVersion : "-")));
   389                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_64BitOS, Environment.Is64BitOperatingSystem.ToString()));
   390                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_64BitProcess, Environment.Is64BitProcess.ToString()));
   391                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_InstallLanguage, ((Office.MsoLanguageID)lang.LanguageID[Office.MsoAppLanguageID.msoLanguageIDInstall]).ToString()));
   392                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_UILanguage, ((Office.MsoLanguageID)lang.LanguageID[Office.MsoAppLanguageID.msoLanguageIDUI]).ToString()));
   393             }
   394             catch (Exception ex)
   395             {
   396                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_Exception, ex.ToString()));
   397             }
   398             finally
   399             {
   400                 if (lang != null)
   401                 {
   402                     Marshal.ReleaseComObject(lang);
   403                     lang = null;
   404                 }
   405             }
   406 
   407             return (systemInfo);
   408         }
   409 
   410         /// <summary>
   411         /// Writes the given text to the log file.
   412         /// </summary>
   413         /// <param name="text">The text to write to the log file.</param>
   414         private static void WriteToLogFile(string text)
   415         {
   416             try
   417             {
   418                 if (text != null)
   419                 {
   420                     lock (mutexLogFile)
   421                     {
   422                         if (Globals.logWriter == null)
   423                         {
   424                             // If file exists, set line counter to file line count
   425                             if (File.Exists(Globals.GetLogFilePath()))
   426                             {
   427                                 try
   428                                 {
   429                                     var lines = File.ReadAllLines(Globals.GetLogFilePath());
   430                                     logLineCount = lines.Length;
   431                                 }
   432                                 catch { }
   433                             }
   434 
   435                             // Open the file
   436                             Globals.logWriter = File.AppendText(Globals.GetLogFilePath());
   437                         }
   438 
   439                         Globals.logWriter.WriteLine(text);
   440 
   441                         Globals.logLineCount++;
   442 
   443                         // If the maximum line count was reached, clean up the log file by removing the first lines.
   444                         if (Globals.logLineCount >= Globals.LOG_MAX_LINES)
   445                         {
   446                             Globals.SanitizeLog();
   447                         }
   448                     }
   449                 }
   450             }
   451             catch { }
   452 
   453             return;
   454         }
   455 
   456         /// <summary>
   457         /// FIFO function to remove the first lines of the log file when a defined maximum 
   458         /// of lines has been reached. 
   459         /// </summary>
   460         private static void SanitizeLog()
   461         {
   462             try
   463             {
   464                 lock (mutexLogFile)
   465                 {
   466                     Globals.CloseLogFile();
   467 
   468                     var lines = File.ReadAllLines(Globals.GetLogFilePath());
   469 
   470                     Globals.ClearLogFile();
   471 
   472                     int startIndex = (lines.Length - (Globals.LOG_MAX_LINES - Globals.LOG_LINES_TO_REPLACE));
   473                     if (startIndex < 0)
   474                     {
   475                         startIndex = 0;
   476                     }
   477 
   478                     using (StreamWriter streamWriter = File.AppendText(Globals.GetLogFilePath()))
   479                     {
   480                         for (int i = startIndex; i < lines.Length; i++)
   481                         {
   482                             if (string.IsNullOrEmpty(lines[i]) == false)
   483                             {
   484                                 streamWriter.WriteLine(lines[i]);
   485                             }
   486                         }
   487                     }
   488 
   489                     Globals.logLineCount = (Globals.LOG_MAX_LINES - Globals.LOG_LINES_TO_REPLACE);
   490                 }
   491             }
   492             catch { }
   493 
   494             return;
   495         }
   496 
   497         /// <summary>
   498         /// Reads all lines of text from the log file.
   499         /// This will never return null.
   500         /// </summary>
   501         /// <returns>The lines of text in the log file.</returns>
   502         public static string ReadLogFile()
   503         {
   504             string log = "";
   505             string path;
   506             StreamReader logReader;
   507 
   508             try
   509             {
   510                 Globals.CloseLogFile();
   511 
   512                 lock (mutexLogFile)
   513                 {
   514                     path = Globals.GetLogFilePath();
   515                     if (File.Exists(path))
   516                     {
   517                         logReader = new StreamReader(path);
   518                         log = logReader.ReadToEnd();
   519 
   520                         logReader.Close();
   521                         logReader.Dispose();
   522                     }
   523                 }
   524 
   525                 // Never allow null
   526                 if (log == null)
   527                 {
   528                     log = "";
   529                 }
   530             }
   531             catch { }
   532 
   533             return (log);
   534         }
   535 
   536         /// <summary>
   537         /// Closes and releases any open log file writer.
   538         /// </summary>
   539         public static void CloseLogFile()
   540         {
   541             try
   542             {
   543                 lock (mutexLogFile)
   544                 {
   545                     if (Globals.logWriter != null)
   546                     {
   547                         Globals.logWriter.Flush();
   548                         Globals.logWriter.Close();
   549                         Globals.logWriter.Dispose();
   550                         Globals.logWriter = null;
   551                     }
   552                 }
   553             }
   554             catch { }
   555 
   556             return;
   557         }
   558 
   559         /// <summary>
   560         /// Clears all logged text by deleting the file.
   561         /// </summary>
   562         public static void ClearLogFile()
   563         {
   564             string path;
   565 
   566             try
   567             {
   568                 Globals.CloseLogFile();
   569 
   570                 lock (mutexLogFile)
   571                 {
   572                     path = Globals.GetLogFilePath();
   573                     if (File.Exists(path))
   574                     {
   575                         File.Delete(path);
   576                     }
   577                 }
   578             }
   579             catch { }
   580 
   581             return;
   582         }
   583 
   584         /// <summary>
   585         /// Gets the path of the log file.
   586         /// </summary>
   587         /// <returns>The path of the log file.</returns>
   588         private static string GetLogFilePath()
   589         {
   590             return (Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "pEp", LOG_FILE_NAME));
   591         }
   592 
   593         /// <summary>
   594         /// Gets the path of the GPA .exe using the install directory from the registry.
   595         /// WARNING: this method can return a null path.
   596         /// </summary>
   597         /// <returns>The path of the GPA .exe, otherwise null.</returns>
   598         public static string GetGPAPath()
   599         {
   600             // We put a general catch block around here, just to be safe - disabling
   601             // the GPA button is the better alternative to crashing if something goes
   602             // wrong here. 
   603             try
   604             {
   605                 foreach (var gpgPath in TryToGuessGpgPathes())
   606                 {
   607                     var gpaPath = System.IO.Path.Combine(gpgPath, "gpa.exe");
   608                     if (File.Exists(gpaPath))
   609                         return gpaPath;
   610                 }
   611             }
   612             catch (Exception ex)
   613             {
   614                 // As I do not expext any exceptions to actually appear at this level,
   615                 // we log them (even in non-verbose level).
   616                 Globals.Log("GetGpaPath: " + ex.Message);
   617             }
   618 
   619             return null;
   620         }
   621 
   622         /// <summary>
   623         /// Helper method which tries to find possible GnuPG installation pathes, with
   624         /// decreasing confidence level.
   625         /// </summary>
   626         /// <returns></returns>
   627         private static IEnumerable<string> TryToGuessGpgPathes()
   628         {
   629             // We just try all possible registry views to find GPA. As we just start it as an externa process,
   630             // we don´t care whether its 32 bit or 64 bit.
   631 
   632             // We try the 32 bit view first, as that's what we currently deploy.
   633             var gpgPath = TryToReadGpgPathFromRegistry(RegistryView.Registry32);
   634 
   635             if (!string.IsNullOrEmpty(gpgPath))
   636                 yield return gpgPath;
   637 
   638             gpgPath = TryToReadGpgPathFromRegistry(RegistryView.Registry64);
   639 
   640             if (!string.IsNullOrEmpty(gpgPath))
   641                 yield return gpgPath;
   642 
   643             // Use backup hardcoded directory locations as fallbacks. However, we log this issue
   644             // as it is not expected (broken installation?)
   645             Globals.Log("TryToGuessGpgPathes: Not found in registry, trying backup hard-coded directory pathes.");
   646 
   647             yield return System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86),
   648                 "GNU", "GnuPG");
   649 
   650             yield return System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
   651                 "GNU", "GnuPG");
   652         }
   653 
   654         private static string TryToReadGpgPathFromRegistry(RegistryView view)
   655         {
   656             try
   657             {
   658                 using (var rootKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, view))
   659                 using (RegistryKey rk = rootKey.OpenSubKey("SOFTWARE\\GNU\\GnuPG"))
   660                 {
   661                     var gpgPath = (string)rk.GetValue("Install Directory");
   662                     return gpgPath;
   663                 }
   664             }
   665             catch (Exception ex)
   666             {
   667                 // Exceptions here are expected (the key does not exist in the view we currently 
   668                 // tried), thus we only long them in verbose mode.
   669                 Globals.LogVerbose("TryToReadGpgPathFromRegistry: " + ex.Message);
   670                 return null;
   671             }
   672         }
   673 
   674         /**************************************************************
   675          * 
   676          * Event Handling
   677          * 
   678          *************************************************************/
   679 
   680         /// <summary>
   681         /// Event handler for when an unhandled error occurs within the current application.
   682         /// </summary>
   683         private static void Application_UnhandledException(object sender, ThreadExceptionEventArgs e)
   684         {
   685             Globals.StopAndSendCrashReport(e.Exception);
   686             return;
   687         }
   688 
   689         /// <summary>
   690         /// Event handler for when an unhandled error occurs within the current domain.
   691         /// </summary>
   692         private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
   693         {
   694             Globals.StopAndSendCrashReport();
   695             return;
   696         }
   697 
   698     }
   699 }