2023-01-22 07:32:18 +00:00
using System ;
2020-10-29 00:27:11 +00:00
using System.Threading.Tasks ;
2017-11-24 20:03:57 +00:00
using System.Windows.Forms ;
2020-10-29 00:27:11 +00:00
using PKHeX.Core ;
2017-11-24 20:03:57 +00:00
#if ! DEBUG
2020-02-02 18:20:51 +00:00
using System.Reflection ;
2017-01-28 17:48:00 +00:00
using System.IO ;
2016-08-22 01:05:41 +00:00
using System.Threading ;
2017-11-24 20:03:57 +00:00
#endif
2014-06-28 21:22:05 +00:00
2023-01-22 07:32:18 +00:00
namespace PKHeX.WinForms ;
internal static class Program
2014-06-28 21:22:05 +00:00
{
2023-01-22 07:32:18 +00:00
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
private static void Main ( )
2014-06-28 21:22:05 +00:00
{
2017-01-15 00:41:27 +00:00
#if ! DEBUG
2023-01-22 07:32:18 +00:00
// Add the event handler for handling UI thread exceptions to the event.
Application . ThreadException + = UIThreadException ;
2016-08-22 01:05:41 +00:00
2023-01-22 07:32:18 +00:00
// Set the unhandled exception mode to force all Windows Forms errors to go through our handler.
Application . SetUnhandledExceptionMode ( UnhandledExceptionMode . CatchException ) ;
2016-08-22 01:05:41 +00:00
2023-01-22 07:32:18 +00:00
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain . CurrentDomain . UnhandledException + = CurrentDomain_UnhandledException ;
2017-01-15 00:41:27 +00:00
#endif
2023-01-22 07:32:18 +00:00
// Run the application
Application . EnableVisualStyles ( ) ;
Application . SetCompatibleTextRenderingDefault ( false ) ;
var splash = new SplashScreen ( ) ;
new Task ( ( ) = > splash . ShowDialog ( ) ) . Start ( ) ;
new Task ( ( ) = > EncounterEvent . RefreshMGDB ( WinForms . Main . MGDatabasePath ) ) . Start ( ) ;
var main = new Main ( ) ;
2024-04-26 06:34:47 +00:00
splash . BeginInvoke ( splash . ForceClose ) ;
2023-01-22 07:32:18 +00:00
Application . Run ( main ) ;
}
// Pipelines build can sometimes tack on text to the version code. Strip it out.
public static readonly Version CurrentVersion = Version . Parse ( GetSaneVersionTag ( Application . ProductVersion ) ) ;
private static ReadOnlySpan < char > GetSaneVersionTag ( ReadOnlySpan < char > productVersion )
{
2023-01-24 07:31:41 +00:00
// Take only 0-9 and '.', stop on first char not in that set.
for ( int i = 0 ; i < productVersion . Length ; i + + )
{
char c = productVersion [ i ] ;
if ( c = = '.' )
continue ;
if ( char . IsNumber ( c ) )
continue ;
return productVersion [ . . i ] ;
}
return productVersion ;
2023-01-22 07:32:18 +00:00
}
2016-08-22 01:05:41 +00:00
2019-10-17 03:52:09 +00:00
#if ! DEBUG
2023-01-22 07:32:18 +00:00
private static void Error ( string msg ) = > MessageBox . Show ( msg , "PKHeX Error" , MessageBoxButtons . OK , MessageBoxIcon . Stop ) ;
2017-02-03 03:14:48 +00:00
2023-12-04 04:13:20 +00:00
// Handle the UI exceptions by showing a dialog box, and asking the user if they wish to abort execution.
2023-01-22 07:32:18 +00:00
private static void UIThreadException ( object sender , ThreadExceptionEventArgs t )
{
DialogResult result = DialogResult . Cancel ;
try
2016-08-22 01:05:41 +00:00
{
2024-03-16 01:54:20 +00:00
var e = t . Exception ;
string errorMessage = IsPluginError < IPlugin > ( e , out var pluginName )
? $"An error occurred in a PKHeX plugin. Please report this error to the plugin author/maintainer.\n{pluginName}"
: "An error occurred in PKHeX. Please report this error to the PKHeX author." ;
result = ErrorWindow . ShowErrorDialog ( errorMessage , e , true ) ;
2016-08-22 01:05:41 +00:00
}
2023-01-22 07:32:18 +00:00
catch ( Exception reportingException )
2016-08-22 01:05:41 +00:00
{
2023-01-22 07:32:18 +00:00
HandleReportingException ( t . Exception , reportingException ) ;
2017-11-24 20:03:57 +00:00
}
2023-01-22 07:32:18 +00:00
// Exits the program when the user clicks Abort.
if ( result = = DialogResult . Abort )
Application . Exit ( ) ;
}
2023-12-04 04:13:20 +00:00
// Handle the UI exceptions by showing a dialog box, and asking the user if they wish to abort execution.
2023-01-22 07:32:18 +00:00
// NOTE: This exception cannot be kept from terminating the application - it can only
// log the event, and inform the user about it.
private static void CurrentDomain_UnhandledException ( object sender , UnhandledExceptionEventArgs e )
{
var ex = e . ExceptionObject as Exception ;
try
2017-11-24 20:03:57 +00:00
{
2023-01-22 07:32:18 +00:00
if ( IsOldPkhexCorePresent ( ex ) )
2017-11-24 20:03:57 +00:00
{
2023-01-22 07:32:18 +00:00
Error ( "You have upgraded PKHeX incorrectly. Please delete PKHeX.Core.dll." ) ;
2017-11-24 20:03:57 +00:00
}
2023-01-22 07:32:18 +00:00
else if ( ex ! = null )
2017-11-24 20:03:57 +00:00
{
2023-01-22 07:32:18 +00:00
ErrorWindow . ShowErrorDialog ( "An unhandled exception has occurred.\nPKHeX must now close." , ex , false ) ;
2017-11-24 20:03:57 +00:00
}
2023-01-22 07:32:18 +00:00
else
2017-11-24 20:03:57 +00:00
{
2023-01-22 07:32:18 +00:00
Error ( "A fatal non-UI error has occurred in PKHeX, and the details could not be displayed. Please report this to the author." ) ;
2016-08-22 01:05:41 +00:00
}
}
2023-01-22 07:32:18 +00:00
catch ( Exception reportingException )
{
HandleReportingException ( ex , reportingException ) ;
}
}
2017-09-27 21:21:25 +00:00
2024-03-16 01:54:20 +00:00
private static bool IsPluginError < T > ( Exception exception , out string pluginName )
{
// Check the stacktrace to see if the namespace is a type that derives from IPlugin
pluginName = string . Empty ;
var stackTrace = new System . Diagnostics . StackTrace ( exception ) ;
foreach ( var frame in stackTrace . GetFrames ( ) )
{
var method = frame . GetMethod ( ) ;
var type = method ? . DeclaringType ;
if ( ! typeof ( T ) . IsAssignableFrom ( type ) )
continue ;
pluginName = type . Namespace ? ? string . Empty ;
return true ;
}
return false ;
}
2023-01-22 07:32:18 +00:00
private static void HandleReportingException ( Exception ? ex , Exception reportingException )
{
2024-01-17 05:43:10 +00:00
try
{
EmergencyErrorLog ( ex , reportingException ) ;
}
2024-03-16 01:54:20 +00:00
catch
2024-01-17 05:43:10 +00:00
{
// We've failed to even save the error details to a file. There's nothing else we can do.
}
2023-01-22 07:32:18 +00:00
if ( reportingException is FileNotFoundException x & & x . FileName ? . StartsWith ( "PKHeX.Core" ) = = true )
2017-09-27 21:21:25 +00:00
{
2023-01-22 07:32:18 +00:00
Error ( "Could not locate PKHeX.Core.dll. Make sure you're running PKHeX together with its code library. Usually caused when all files are not extracted." ) ;
return ;
2017-09-27 21:21:25 +00:00
}
2023-01-22 07:32:18 +00:00
try
{
Error ( "A fatal non-UI error has occurred in PKHeX, and there was a problem displaying the details. Please report this to the author." ) ;
}
finally
{
Application . Exit ( ) ;
}
}
2020-01-26 16:55:13 +00:00
2023-01-22 07:32:18 +00:00
/// <summary>
/// Attempt to log exceptions to a file when there's an error displaying exception details.
/// </summary>
/// <param name="originalException"></param>
/// <param name="errorHandlingException"></param>
private static bool EmergencyErrorLog ( Exception ? originalException , Exception errorHandlingException )
{
try
2020-01-26 16:55:13 +00:00
{
2023-01-22 07:32:18 +00:00
// Not using a string builder because something's very wrong, and we don't want to make things worse
var message = ( originalException ? . ToString ( ) ? ? "null first exception" ) + Environment . NewLine + errorHandlingException ;
File . WriteAllText ( $"PKHeX_Error_Report {DateTime.Now:yyyyMMddHHmmss}.txt" , message ) ;
2020-01-26 16:55:13 +00:00
}
2023-01-22 07:32:18 +00:00
catch ( Exception )
{
// We've failed to save the error details twice now. There's nothing else we can do.
return false ;
}
return true ;
2014-06-28 21:22:05 +00:00
}
2023-01-22 07:32:18 +00:00
private static bool IsOldPkhexCorePresent ( Exception ? ex )
{
return ex is MissingMethodException or TypeLoadException or TypeInitializationException
& & File . Exists ( "PKHeX.Core.dll" )
& & AssemblyName . GetAssemblyName ( "PKHeX.Core.dll" ) . Version < CurrentVersion ;
}
#endif
2014-06-28 21:22:05 +00:00
}