Globals.cs
author Thomas
Mon, 28 Oct 2019 08:15:41 +0100
branchOUT-619
changeset 2834 4fa3adadc41d
parent 2811 d055904ad03b
child 2826 7041626afb56
child 2898 85c082c01112
child 2955 43d27b4c9666
permissions -rw-r--r--
Adjust image size
     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 PEP_REG_NAME_USER_FOLDER                = "UserFolder";
    24         public const string PEP_COPYRIGHT                           = "Copyright © 2014-2019 p≡p Security SA, Luxembourg";
    25         public const string PEP_DATA_FILE_NAME                      = "pEp";                           // The file name of the pEp data store. This does NOT include extension (.pst)
    26         public const string PEP_DISPLAY_NAME                        = "p≡p for Outlook";               // Display name to the user
    27         public const string PEP_DISPLAY_NAME_READER                 = "p≡p Reader for Outlook";        // Display name to the user
    28         public const string PEP_DRAFTS_FOLDER_NAME                  = "pEp Drafts";                    // Folder name for secure drafts
    29         public const string PEP_FOLDER_NAME                         = "pEp";
    30         public const string PEP_INTERNAL_CATEGORY_NAME              = "pEp Internal";
    31 #if DEBUG
    32         public const string PEP_NAME_ADDIN_DESC                     = "pEp";                           // Description of the add-in within Outlook itself (shared with reader)
    33 #else
    34         public const string PEP_NAME_ADDIN_DESC                     = "pEp for Outlook";               // Description of the add-in within Outlook itself (shared with reader)
    35 #endif
    36         public const string PEP_NAME_INSTALL                        = "pEp for Outlook";               // Name for Windows installer, must sync with installation code (shared with reader)
    37         public const string PEP_PROCESSING_CATEGORY_NAME            = "pEp Processing";
    38         public const string PEP_WEBSITE_LINK                        = "https://pEp.software";
    39         public const string PEP_WEBSITE_UPGRADE_LINK                = "https://pEp.software";
    40         public const string PEP_WEBSITE_READER_LINK                 = "https://pEp.security/reader";
    41 
    42 #if READER_RELEASE_MODE
    43         public const ReleaseMode RELEASE_MODE = ReleaseMode.Reader;
    44 #elif ENTERPRISE
    45         public const ReleaseMode RELEASE_MODE = ReleaseMode.Enterprise;
    46 #else
    47         public const ReleaseMode RELEASE_MODE = ReleaseMode.Standard;
    48 #endif
    49 
    50         // Engine translations
    51         public const int PHRASE_ID_TRUSTWORDS_LANGUAGE = 1000;
    52 
    53         // Timeout for sync messages
    54         public const int TIMEOUT_SYNC_MESSAGE          = 600000; // 10 minutes in milliseconds
    55 
    56         /// <summary>
    57         /// Enumeration to define the supported release modes.
    58         /// </summary>
    59         public enum ReleaseMode
    60         {
    61             Standard,
    62             Reader,
    63             Enterprise
    64         }
    65 
    66         /// <summary>
    67         /// Enumeration to define the return status of methods.
    68         /// </summary>
    69         public enum ReturnStatus
    70         {
    71             /// <summary>
    72             /// An unspecified failure occured.
    73             /// </summary>
    74             Failure,
    75 
    76             /// <summary>
    77             /// A failure occured because there is no internet or Exchange connection.
    78             /// </summary>
    79             FailureNoConnection,
    80 
    81             /// <summary>
    82             /// Successfully completed with no unexpected failures.
    83             /// </summary>
    84             Success
    85         }
    86 
    87         /// <summary>
    88         /// Enumeration to define the Outlook version pEp runs on.
    89         /// For information on version numbers, see: https://en.wikipedia.org/wiki/Microsoft_Outlook#Versions
    90         /// </summary>            
    91         public enum Version
    92         {
    93             Undefined = 0,
    94             Outlook2010 = 14,
    95             Outlook2013 = 15,
    96             Outlook2016 = 16
    97         }
    98 
    99         private static bool                 eventsAreConnected      = false;
   100         private static ResourceDictionary   _ResourceDict           = null;
   101         private static Version              _OutlookVersion         = Version.Undefined;
   102         private static char                 _ListDelimiter          = char.MinValue;
   103         private static string               _PEPUserFolder          = null;
   104 
   105         /**************************************************************
   106          * 
   107          * Property Accessors
   108          * 
   109          *************************************************************/
   110 
   111         /// <summary>
   112         /// Gets the machine's default list delimiter character, as defined in
   113         /// HKEY_CURRENT_USER\Control Panel\International\sList 
   114         /// </summary>
   115         public static char ListDelimiter
   116         {
   117             get
   118             {
   119                 if (Globals._ListDelimiter == char.MinValue)
   120                 {
   121                     try
   122                     {
   123                         using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\International"))
   124                         {
   125                             string registryEntry = key?.GetValue("sList") as string;
   126                             Globals._ListDelimiter = registryEntry?.ToCharArray()[0] ?? ';';
   127                         }
   128                     }
   129                     catch (Exception ex)
   130                     {
   131                         Log.Error("ListDelimiter: Error getting default list delimiter. " + ex.ToString());
   132                     }
   133                 }
   134 
   135                 return Globals._ListDelimiter;
   136             }
   137         }
   138 
   139         /// <summary>
   140         /// Gets the shared XAML resource dictionary.
   141         /// </summary>
   142         public static ResourceDictionary ResourceDict
   143         {
   144             get
   145             {
   146                 if (_ResourceDict == null)
   147                 {
   148                     // Load resource dictionary
   149                     _ResourceDict = new ResourceDictionary
   150                     {
   151                         Source = new Uri("pack://application:,,,/pEp;component/Resources/Dictionary.xaml", UriKind.RelativeOrAbsolute)
   152                     };
   153 
   154                     return (_ResourceDict);
   155                 }
   156                 else
   157                 {
   158                     return (_ResourceDict);
   159                 }
   160             }
   161         }
   162 
   163         /// <summary>
   164         /// Gets the folder where pEp saves and loads user files to/from.
   165         /// </summary>
   166         public static string PEPUserFolder
   167         {
   168             get
   169             {
   170                 if (Globals._PEPUserFolder == null)
   171                 {
   172                     string defaultFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), Globals.PEP_FOLDER_NAME);
   173 
   174                     try
   175                     {
   176                         string perUserDirectory = ThisAddIn.PEPEngine.PerUserDirectory();
   177                         Globals._PEPUserFolder = Environment.ExpandEnvironmentVariables(perUserDirectory);
   178                     }
   179                     catch (Exception ex)
   180                     {
   181                         // Note: At this point, the pEp log might not yet be available
   182                         Log.Error("PEPUserFolder: Error getting user folder from the engine. " + ex.ToString());
   183 
   184                         Globals._PEPUserFolder = defaultFolder;
   185                     }
   186                 }
   187 
   188                 return Globals._PEPUserFolder;
   189             }
   190         }
   191 
   192         /**************************************************************
   193          * 
   194          * Methods
   195          * 
   196          *************************************************************/
   197 
   198         /// <summary>
   199         /// Builds a crash report and displays it to the user before sending. 
   200         /// </summary>
   201         /// <param name="exception">The exception to build the report with.</param>
   202         /// <param name="summaryMessage">The message summarizing where the error occured or what it is.</param>
   203         /// <param name="allowRestart">True to allow the add-in to attempt to restart itself.</param>
   204         /// <param name="isEngineCrash">True if the engine generated the error. This will not attempt to get
   205         /// an engine log as the engine itself is not working (avoids infinite loop).</param>
   206         public static void StopAndSendCrashReport(Exception exception = null,
   207                                                   string summaryMessage = null,
   208                                                   bool allowRestart = true,
   209                                                   bool isEngineCrash = false)
   210         {
   211             bool continueSend = true;
   212             string engineLog = "";
   213             string log = "";
   214             FormCrashReport form;
   215             FormControlCrashReport.State stateIn = null;
   216             DialogResult result;
   217             Office.COMAddIns comAddIns = null;
   218             Office.COMAddIn addIn = null;
   219             PEPMessage newMessage;
   220             PEPAttachment attachment = null;
   221 
   222             // Show the report before sending
   223             if (continueSend)
   224             {
   225                 stateIn = new FormControlCrashReport.State
   226                 {
   227                     AddressTo = Globals.ThisAddIn.Settings?.CrashReportSendAddress ?? PEPSettings.CRASH_REPORT_SEND_ADDRESS_DEFAULT,
   228                     AddressFrom = null // A message can be sent without a "from" address
   229                 };
   230 
   231                 // Get log
   232                 try
   233                 {
   234                     log = string.Join(Environment.NewLine, Log.Read());
   235                 }
   236                 catch
   237                 {
   238                     log = "";
   239                 }
   240 
   241                 // Get engine log
   242                 if (isEngineCrash == false)
   243                 {
   244                     try
   245                     {
   246                         engineLog = ThisAddIn.PEPEngine.GetCrashdumpLog();
   247                     }
   248                     catch (COMException ex)
   249                     {
   250                         engineLog = ex.ToString();
   251                     }
   252                     catch (Exception ex)
   253                     {
   254                         engineLog = ex.ToString();
   255                     }
   256                 }
   257 
   258                 // Double check for null log files
   259                 if (log == null) { log = ""; }
   260                 if (engineLog == null) { engineLog = ""; }
   261 
   262                 // Build report header
   263                 stateIn.ReportText = string.Join(Environment.NewLine, new string[]
   264                     {
   265                         "Summary---------------------------------------------------",
   266                         "",
   267                         "Date: " + System.DateTime.Now.ToString("MM/dd/yyyy HH:mm:ss K"),
   268                         Globals.GetSystemInfo(),
   269                         "",
   270                         (string.IsNullOrWhiteSpace(summaryMessage) ? "no message" : summaryMessage),
   271                         "",
   272                         "Exception Details-----------------------------------------",
   273                         "",
   274                         (exception == null ? "no exception" : exception.ToString()),
   275                         "",
   276                         "p≡p for Outlook Log---------------------------------------",
   277                         "",
   278                         log,
   279                         "",
   280                         "p≡p Engine Log--------------------------------------------",
   281                         "",
   282                         engineLog,
   283                         "",
   284                         "END OF REPORT"
   285                     });
   286 
   287                 if (Globals.ThisAddIn.Settings?.IsCrashReportVisible ?? true)
   288                 {
   289                     form = new FormCrashReport
   290                     {
   291                         StartPosition = FormStartPosition.CenterParent,
   292                         DisplayState = stateIn
   293                     };
   294                     result = form.ShowDialog();
   295 
   296                     if (result != DialogResult.OK)
   297                     {
   298                         continueSend = false;
   299                     }
   300                 }
   301             }
   302 
   303             // Build and send message
   304             if (continueSend)
   305             {
   306                 newMessage = new PEPMessage
   307                 {
   308                     ShortMsg = "Crash Report"
   309                 };
   310                 newMessage.To.Add(new PEPIdentity(stateIn.AddressTo));
   311                 newMessage.LongMsg = stateIn.ReportText;
   312                 newMessage.Direction = pEpCOMServerAdapterLib.pEpMsgDirection.pEpDirOutgoing;
   313 
   314                 // Add logs as attachments
   315                 if (string.IsNullOrEmpty(log) == false)
   316                 {
   317                     attachment = new PEPAttachment
   318                     {
   319                         Data = Encoding.UTF8.GetBytes(log),
   320                         FileName = "pEp Log.txt"
   321                     };
   322                     newMessage.Attachments.Add(attachment);
   323                 }
   324 
   325                 if (string.IsNullOrEmpty(engineLog) == false)
   326                 {
   327                     attachment = new PEPAttachment
   328                     {
   329                         Data = Encoding.UTF8.GetBytes(engineLog),
   330                         FileName = "pEp Engine Log.txt"
   331                     };
   332                     newMessage.Attachments.Add(attachment);
   333                 }
   334 
   335                 // Try to send the message but ignore any errors
   336                 try
   337                 {
   338                     Globals.ThisAddIn.CreateAndSendMessage(newMessage, true, false);
   339                 }
   340                 catch { }
   341             }
   342 
   343             // Stop logging
   344             Log.Close();
   345 
   346             // Locate the Outlook add-in
   347             comAddIns = Globals.ThisAddIn.Application.COMAddIns;
   348             foreach (Office.COMAddIn addin in comAddIns)
   349             {
   350                 if (string.Equals(addin.Description, Globals.PEP_NAME_ADDIN_DESC, StringComparison.OrdinalIgnoreCase))
   351                 {
   352                     addIn = addin;
   353                     break;
   354                 }
   355             }
   356 
   357             // Shutdown add-in
   358             if (addIn != null)
   359             {
   360                 addIn.Connect = false;
   361             }
   362 
   363             return;
   364         }
   365 
   366         /// <summary>
   367         /// Connects or disconnects all global error handling events.
   368         /// </summary>
   369         /// <param name="connect">True to connect events, false to disconnect.</param>
   370         public static void ConnectEvents(bool connect)
   371         {
   372             // Connect events only if not already connected
   373             if ((connect == true) &&
   374                 (eventsAreConnected == false))
   375             {
   376                 System.Windows.Forms.Application.ThreadException += Application_UnhandledException;
   377                 AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
   378 
   379                 eventsAreConnected = true;
   380             }
   381             // Always attempt to disconnect
   382             else if (connect == false)
   383             {
   384                 System.Windows.Forms.Application.ThreadException -= Application_UnhandledException;
   385                 AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
   386 
   387                 eventsAreConnected = false;
   388             }
   389 
   390             return;
   391         }
   392 
   393         /// <summary>
   394         /// Gets the Outlook version pEp runs on.       
   395         /// </summary>
   396         /// <returns>The Outlook version or 'Undefined' if an error occured.</returns>
   397         public static Version OutlookVersion
   398         {
   399             get
   400             {
   401                 if (Globals._OutlookVersion == Version.Undefined)
   402                 {
   403                     var version = Version.Undefined;
   404                     string versionNumber = string.Empty;
   405 
   406                     try
   407                     {
   408                         versionNumber = Globals.ThisAddIn.Application.Version.Substring(0, 2);
   409                     }
   410                     catch (Exception ex)
   411                     {
   412                         versionNumber = string.Empty;
   413                         Log.Error("GetOutlookVersion: Error getting version. " + ex.ToString());
   414                     }
   415 
   416                     if (int.TryParse(versionNumber, out int versionNumberInt))
   417                     {
   418                         try
   419                         {
   420                             version = (Globals.Version)versionNumberInt;
   421                         }
   422                         catch (Exception ex)
   423                         {
   424                             version = Version.Undefined;
   425                             Log.Error("GetOutlookVersion: Error parsing version. " + ex.ToString());
   426                         }
   427                     }
   428 
   429                     Globals._OutlookVersion = version;
   430                 }
   431 
   432                 return Globals._OutlookVersion;
   433             }
   434         }
   435 
   436         /// <summary>
   437         /// Gets the pEp for Outlook version.
   438         /// This will never be null.
   439         /// </summary>
   440         /// <returns>The version string.</returns>
   441         public static string GetPEPVersion()
   442         {
   443             string version;
   444             string updateLevel;
   445 
   446             // Build the version string
   447             updateLevel = Properties.Settings.Default.UpdateLevel;
   448             version = Assembly.GetExecutingAssembly().GetName().Version.ToString();
   449 
   450             if (string.IsNullOrWhiteSpace(updateLevel) == false)
   451             {
   452                 version += " Update " + updateLevel;
   453             }
   454 
   455             return (version);
   456         }
   457 
   458         /// <summary>
   459         /// Gets the current system information string.
   460         /// </summary>
   461         /// <returns>The system information as a string.</returns>
   462         public static string GetSystemInfo()
   463         {
   464             string result = "";
   465             List<KVPair<string, string>> systemInfo;
   466             List<string> list = new List<string>();
   467 
   468             try
   469             {
   470                 systemInfo = Globals.GetSystemInfoList();
   471 
   472                 for (int i = 0; i < systemInfo.Count; i++)
   473                 {
   474                     list.Add(systemInfo[i].Key + " " + systemInfo[i].Value);
   475                 }
   476 
   477                 result = string.Join(Environment.NewLine, list);
   478             }
   479             catch { }
   480 
   481             return (result);
   482         }
   483 
   484         /// <summary>
   485         /// Gets the current system information as a list of Key-Value pairs.
   486         /// </summary>
   487         /// <returns>The system information as a list.</returns>
   488         public static List<KVPair<string, string>> GetSystemInfoList()
   489         {
   490             string serial;
   491             string version = Globals.GetPEPVersion();
   492             string engineVersion;
   493             List<KVPair<string, string>> systemInfo = new List<KVPair<string, string>>();
   494             Office.LanguageSettings lang = null;
   495 
   496             try
   497             {
   498                 lang = Globals.ThisAddIn.Application.LanguageSettings;
   499 
   500                 // Get the engine version
   501                 try
   502                 {
   503                     engineVersion = ThisAddIn.PEPEngine.GetEngineVersion();
   504                 }
   505                 catch
   506                 {
   507                     engineVersion = null;
   508                 }
   509 
   510                 // Use only the first 10-digits which is the serial itself
   511                 serial = Properties.Settings.Default.Serial;
   512                 serial = (serial ?? "");
   513                 serial = (serial.Length > 10) ? serial.Substring(0, 10) : serial;
   514 
   515                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_Serial, (Globals.RELEASE_MODE == Globals.ReleaseMode.Reader ? Properties.Resources.SystemInfo_Reader : serial)));
   516                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_PEPForOutlookVersion, (version ?? "-")));
   517                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_PEPEngineVersion, (engineVersion ?? "-")));
   518                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_OutlookVersion, Globals.ThisAddIn.Application.Version));
   519                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_OSVersion, Environment.OSVersion.VersionString));
   520                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_64BitOS, Environment.Is64BitOperatingSystem.ToString()));
   521                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_64BitProcess, Environment.Is64BitProcess.ToString()));
   522                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_InstallLanguage, ((Office.MsoLanguageID)lang.LanguageID[Office.MsoAppLanguageID.msoLanguageIDInstall]).ToString()));
   523                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_UILanguage, ((Office.MsoLanguageID)lang.LanguageID[Office.MsoAppLanguageID.msoLanguageIDUI]).ToString()));
   524             }
   525             catch (Exception ex)
   526             {
   527                 systemInfo.Add(new KVPair<string, string>(Properties.Resources.SystemInfo_Exception, ex.ToString()));
   528             }
   529             finally
   530             {
   531                 if (lang != null)
   532                 {
   533                     // Marshal.ReleaseComObject(lang);
   534                     lang = null;
   535                 }
   536             }
   537 
   538             return (systemInfo);
   539         }
   540 
   541         /// <summary>
   542         /// Gets the path of the gpg.exe using the install directory from the registry.
   543         /// WARNING: this method can return a null path.
   544         /// </summary>
   545         /// <returns>The path of the gpg.exe, otherwise null.</returns>
   546         public static string GetGPGPath()
   547         {
   548             try
   549             {
   550                 foreach (var gpg4WinPath in TryToGuessGpg4WinPaths())
   551                 {
   552                     var gpgPath = Path.Combine(gpg4WinPath, "..", "GnuPG", "bin", "gpg.exe");
   553                     if (File.Exists(gpgPath))
   554                     {
   555                         return gpgPath;
   556                     }
   557                 }
   558             }
   559             catch (Exception ex)
   560             {
   561                 /* As we do not expext any exceptions to actually appear at this level,
   562                  * we log them (even in non-verbose mode).
   563                  */
   564                 Log.Error("GetGpaPath: " + ex.Message);
   565             }
   566 
   567             return null;
   568         }
   569 
   570         /// <summary>
   571         /// Gets the path of the GPA .exe using the install directory from the registry.
   572         /// WARNING: this method can return a null path.
   573         /// </summary>
   574         /// <returns>The path of the GPA .exe, otherwise null.</returns>
   575         public static string GetGPAPath()
   576         {
   577             /* We put a general catch block around here, just to be safe - disabling
   578              * the GPA button is the better alternative to crashing if something goes
   579              * wrong here. 
   580              */
   581             try
   582             {
   583                 foreach (var gpgPath in TryToGuessGpg4WinPaths())
   584                 {
   585                     var gpaPath = Path.Combine(gpgPath, "bin", "gpa.exe");
   586                     if (File.Exists(gpaPath))
   587                     {
   588                         return gpaPath;
   589                     }
   590                 }
   591             }
   592             catch (Exception ex)
   593             {
   594                 /* As we do not expext any exceptions to actually appear at this level,
   595                  * we log them (even in non-verbose mode).
   596                  */
   597                 Log.Error("GetGpaPath: " + ex.Message);
   598             }
   599 
   600             return null;
   601         }
   602 
   603         /// <summary>
   604         /// Helper method which tries to find possible Gpg4win installation pathes, with
   605         /// decreasing confidence level.
   606         /// </summary>
   607         /// <returns>A collection of possible Gpg4win paths.</returns>
   608         private static IEnumerable<string> TryToGuessGpg4WinPaths()
   609         {
   610             /* We just try all possible registry views to find GPA. As we just start it as an external process,
   611              * we don't care whether it's 32 bit or 64 bit and whether it's in the local machine or current 
   612              * user scope.
   613              */
   614             string gpgPath = null;
   615 
   616             // First, try HKLM and 32 bit
   617             gpgPath = TryToReadGpg4winPathFromRegistry(RegistryHive.LocalMachine, RegistryView.Registry32);
   618 
   619             if (string.IsNullOrEmpty(gpgPath) == false)
   620             {
   621                 yield return gpgPath;
   622             }
   623 
   624             // Second, try HKCU and 32 bit
   625             gpgPath = TryToReadGpg4winPathFromRegistry(RegistryHive.CurrentUser, RegistryView.Registry32);
   626 
   627             if (string.IsNullOrEmpty(gpgPath) == false)
   628             {
   629                 yield return gpgPath;
   630             }
   631 
   632             // Third, try HKLM and 64 bit
   633             gpgPath = TryToReadGpg4winPathFromRegistry(RegistryHive.LocalMachine, RegistryView.Registry64);
   634 
   635             if (string.IsNullOrEmpty(gpgPath) == false)
   636             {
   637                 yield return gpgPath;
   638             }
   639 
   640             // Fourth, try HKCU and 64 bit
   641             gpgPath = TryToReadGpg4winPathFromRegistry(RegistryHive.CurrentUser, RegistryView.Registry64);
   642 
   643             if (string.IsNullOrEmpty(gpgPath) == false)
   644             {
   645                 yield return gpgPath;
   646             }
   647 
   648             /* Use backup hardcoded directory locations as fallbacks. However, we log this issue
   649              * as it is not expected (broken installation?)
   650              */
   651             Log.Warning("TryToGuessGpgPathes: Not found in registry, trying backup hard-coded directory pathes.");
   652 
   653             yield return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Gpg4win");
   654 
   655             yield return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "Gpg4win");
   656 
   657             yield return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Gpg4win");
   658 
   659             yield return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Programs", "Gpg4win");
   660         }
   661 
   662         /// <summary>
   663         /// Helper method that looks up the install directory of the Gpg4win path in the registry.
   664         /// </summary>
   665         /// <param name="hKey">The registry hive to search (Local Machine or Current User).</param>
   666         /// <param name="view">The registry view to search (32 bit or 64 bit).</param>
   667         /// <returns>The path to the Gpg4win install directory or null if none was found.</returns>
   668         private static string TryToReadGpg4winPathFromRegistry(RegistryHive hKey, RegistryView view)
   669         {
   670             string gpgPath = null;
   671 
   672             try
   673             {
   674                 using (var rootKey = RegistryKey.OpenBaseKey(hKey, view))
   675                 {
   676                     using (RegistryKey rk = rootKey.OpenSubKey("SOFTWARE\\Gpg4win"))
   677                     {
   678                         gpgPath = rk?.GetValue("Install Directory") as string;
   679                     }
   680                 }
   681             }
   682             catch (Exception ex)
   683             {
   684                 // Exceptions here are expected (the key does not exist in the view we currently 
   685                 // tried), thus we only long them in verbose mode.
   686                 Log.Verbose("TryToReadGpg4winPathFromRegistry: " + ex.Message);
   687                 return null;
   688             }
   689 
   690             return gpgPath;
   691         }
   692 
   693         /**************************************************************
   694          * 
   695          * Event Handling
   696          * 
   697          *************************************************************/
   698 
   699         /// <summary>
   700         /// Event handler for when an unhandled error occurs within the current application.
   701         /// </summary>
   702         private static void Application_UnhandledException(object sender, ThreadExceptionEventArgs e)
   703         {
   704             Globals.StopAndSendCrashReport(e.Exception);
   705             return;
   706         }
   707 
   708         /// <summary>
   709         /// Event handler for when an unhandled error occurs within the current domain.
   710         /// </summary>
   711         private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
   712         {
   713             Globals.StopAndSendCrashReport();
   714             return;
   715         }
   716 
   717     }
   718 }