You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

277 lines
9.4 KiB

15 years ago
15 years ago
15 years ago
15 years ago
  1. // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Diagnostics;
  21. using System.IO;
  22. using System.Linq;
  23. using System.Reflection;
  24. using System.Runtime.Loader;
  25. using System.Threading.Tasks;
  26. using System.Windows;
  27. using System.Windows.Threading;
  28. using ICSharpCode.ILSpy.AppEnv;
  29. using ICSharpCode.ILSpy.AssemblyTree;
  30. using ICSharpCode.ILSpyX.Analyzers;
  31. using Medo.Application;
  32. using TomsToolbox.Wpf.Styles;
  33. using ICSharpCode.ILSpyX.TreeView;
  34. using TomsToolbox.Composition;
  35. using TomsToolbox.Wpf.Composition;
  36. using ICSharpCode.ILSpy.Themes;
  37. using System.Globalization;
  38. using System.Threading;
  39. using Microsoft.Extensions.DependencyInjection;
  40. using TomsToolbox.Composition.MicrosoftExtensions;
  41. using TomsToolbox.Essentials;
  42. namespace ICSharpCode.ILSpy
  43. {
  44. /// <summary>
  45. /// Interaction logic for App.xaml
  46. /// </summary>
  47. public partial class App : Application
  48. {
  49. internal static CommandLineArguments CommandLineArguments;
  50. internal static readonly IList<ExceptionData> StartupExceptions = new List<ExceptionData>();
  51. public static IExportProvider ExportProvider { get; private set; }
  52. internal record ExceptionData(Exception Exception)
  53. {
  54. public string PluginName { get; init; }
  55. }
  56. public App()
  57. {
  58. var cmdArgs = Environment.GetCommandLineArgs().Skip(1);
  59. CommandLineArguments = CommandLineArguments.Create(cmdArgs);
  60. // This is only a temporary, read only handle to the settings service to access the AllowMultipleInstances setting before DI is initialized.
  61. // At runtime, you must use the service via DI!
  62. var settingsService = new SettingsService();
  63. bool forceSingleInstance = (CommandLineArguments.SingleInstance ?? true)
  64. && !settingsService.MiscSettings.AllowMultipleInstances;
  65. if (forceSingleInstance)
  66. {
  67. SingleInstance.Attach(); // will auto-exit for second instance
  68. SingleInstance.NewInstanceDetected += SingleInstance_NewInstanceDetected;
  69. }
  70. InitializeComponent();
  71. if (!InitializeDependencyInjection(settingsService))
  72. {
  73. // There is something completely wrong with DI, probably some service registration is missing => nothing we can do to recover, so stop and shut down.
  74. Exit += (_, _) => MessageBox.Show(StartupExceptions.FormatExceptions(), "Sorry we crashed!", MessageBoxButton.OK, MessageBoxImage.Error, MessageBoxResult.OK, MessageBoxOptions.DefaultDesktopOnly);
  75. Shutdown(1);
  76. return;
  77. }
  78. if (!Debugger.IsAttached)
  79. {
  80. AppDomain.CurrentDomain.UnhandledException += ShowErrorBox;
  81. Dispatcher.CurrentDispatcher.UnhandledException += Dispatcher_UnhandledException;
  82. }
  83. TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException;
  84. SharpTreeNode.SetImagesProvider(new WpfWindowsTreeNodeImagesProvider());
  85. Resources.RegisterDefaultStyles();
  86. // Register the export provider so that it can be accessed from WPF/XAML components.
  87. ExportProviderLocator.Register(ExportProvider);
  88. // Add data templates registered via MEF.
  89. Resources.MergedDictionaries.Add(DataTemplateManager.CreateDynamicDataTemplates(ExportProvider));
  90. var sessionSettings = settingsService.SessionSettings;
  91. ThemeManager.Current.Theme = sessionSettings.Theme;
  92. if (!string.IsNullOrEmpty(sessionSettings.CurrentCulture))
  93. {
  94. Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new(sessionSettings.CurrentCulture);
  95. }
  96. ILSpyTraceListener.Install();
  97. if (CommandLineArguments.ArgumentsParser.IsShowingInformation)
  98. {
  99. MessageBox.Show(CommandLineArguments.ArgumentsParser.GetHelpText(), "ILSpy Command Line Arguments");
  100. }
  101. if (CommandLineArguments.ArgumentsParser.RemainingArguments.Any())
  102. {
  103. string unknownArguments = string.Join(", ", CommandLineArguments.ArgumentsParser.RemainingArguments);
  104. MessageBox.Show(unknownArguments, "ILSpy Unknown Command Line Arguments Passed");
  105. }
  106. settingsService.AssemblyListManager.CreateDefaultAssemblyLists();
  107. }
  108. public new static App Current => (App)Application.Current;
  109. public new MainWindow MainWindow {
  110. get => (MainWindow)base.MainWindow;
  111. private set => base.MainWindow = value;
  112. }
  113. private static void SingleInstance_NewInstanceDetected(object sender, NewInstanceEventArgs e) => ExportProvider.GetExportedValue<AssemblyTreeModel>().HandleSingleInstanceCommandLineArguments(e.Args).HandleExceptions();
  114. static Assembly ResolvePluginDependencies(AssemblyLoadContext context, AssemblyName assemblyName)
  115. {
  116. var rootPath = Path.GetDirectoryName(typeof(App).Assembly.Location);
  117. var assemblyFileName = Path.Combine(rootPath, assemblyName.Name + ".dll");
  118. if (!File.Exists(assemblyFileName))
  119. return null;
  120. return context.LoadFromAssemblyPath(assemblyFileName);
  121. }
  122. private bool InitializeDependencyInjection(SettingsService settingsService)
  123. {
  124. // Add custom logic for resolution of dependencies.
  125. // This necessary because the AssemblyLoadContext.LoadFromAssemblyPath and related methods,
  126. // do not automatically load dependencies.
  127. AssemblyLoadContext.Default.Resolving += ResolvePluginDependencies;
  128. try
  129. {
  130. var services = new ServiceCollection();
  131. var pluginDir = Path.GetDirectoryName(typeof(App).Module.FullyQualifiedName);
  132. if (pluginDir != null)
  133. {
  134. foreach (var plugin in Directory.GetFiles(pluginDir, "*.Plugin.dll"))
  135. {
  136. var name = Path.GetFileNameWithoutExtension(plugin);
  137. try
  138. {
  139. var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(plugin);
  140. services.BindExports(assembly);
  141. }
  142. catch (Exception ex)
  143. {
  144. // Cannot show MessageBox here, because WPF would crash with a XamlParseException
  145. // Remember and show exceptions in text output, once MainWindow is properly initialized
  146. StartupExceptions.Add(new(ex) { PluginName = name });
  147. }
  148. }
  149. }
  150. // Add the built-in parts: First, from ILSpyX
  151. services.BindExports(typeof(IAnalyzer).Assembly);
  152. // Then from ILSpy itself
  153. services.BindExports(Assembly.GetExecutingAssembly());
  154. // Add the settings service
  155. services.AddSingleton(settingsService);
  156. // Add the export provider
  157. services.AddSingleton(_ => ExportProvider);
  158. // Add the docking manager
  159. services.AddSingleton(serviceProvider => serviceProvider.GetService<MainWindow>().DockManager);
  160. services.AddTransient(serviceProvider => serviceProvider.GetService<AssemblyTreeModel>().AssemblyList);
  161. var serviceProvider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
  162. ExportProvider = new ExportProviderAdapter(serviceProvider);
  163. Exit += (_, _) => serviceProvider.Dispose();
  164. return true;
  165. }
  166. catch (Exception ex)
  167. {
  168. if (ex is AggregateException aggregate)
  169. StartupExceptions.AddRange(aggregate.InnerExceptions.Select(item => new ExceptionData(ex)));
  170. else
  171. StartupExceptions.Add(new(ex));
  172. return false;
  173. }
  174. }
  175. protected override void OnStartup(StartupEventArgs e)
  176. {
  177. base.OnStartup(e);
  178. MainWindow = ExportProvider.GetExportedValue<MainWindow>();
  179. MainWindow.Show();
  180. }
  181. void DotNet40_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
  182. {
  183. // On .NET 4.0, an unobserved exception in a task terminates the process unless we mark it as observed
  184. e.SetObserved();
  185. }
  186. #region Exception Handling
  187. static void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
  188. {
  189. UnhandledException(e.Exception);
  190. e.Handled = true;
  191. }
  192. static void ShowErrorBox(object sender, UnhandledExceptionEventArgs e)
  193. {
  194. Exception ex = e.ExceptionObject as Exception;
  195. if (ex != null)
  196. {
  197. UnhandledException(ex);
  198. }
  199. }
  200. [ThreadStatic]
  201. static bool showingError;
  202. internal static void UnhandledException(Exception exception)
  203. {
  204. Debug.WriteLine(exception.ToString());
  205. for (Exception ex = exception; ex != null; ex = ex.InnerException)
  206. {
  207. ReflectionTypeLoadException rtle = ex as ReflectionTypeLoadException;
  208. if (rtle != null && rtle.LoaderExceptions.Length > 0)
  209. {
  210. exception = rtle.LoaderExceptions[0];
  211. Debug.WriteLine(exception.ToString());
  212. break;
  213. }
  214. }
  215. if (showingError)
  216. {
  217. // Ignore re-entrant calls
  218. // We run the risk of opening an infinite number of exception dialogs.
  219. return;
  220. }
  221. showingError = true;
  222. try
  223. {
  224. MessageBox.Show(exception.ToString(), "Sorry, we crashed");
  225. }
  226. finally
  227. {
  228. showingError = false;
  229. }
  230. }
  231. #endregion
  232. }
  233. }