/ILSpy/App.xaml.cs

http://github.com/icsharpcode/ILSpy · C# · 238 lines · 174 code · 26 blank · 38 comment · 24 complexity · c0983b4bfdd0dbb769e54852c6dc836b MD5 · raw file

  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.Text;
  25. using System.Threading.Tasks;
  26. using System.Windows;
  27. using System.Windows.Documents;
  28. using System.Windows.Navigation;
  29. using System.Windows.Threading;
  30. using ICSharpCode.ILSpy.Options;
  31. using Microsoft.VisualStudio.Composition;
  32. namespace ICSharpCode.ILSpy
  33. {
  34. /// <summary>
  35. /// Interaction logic for App.xaml
  36. /// </summary>
  37. public partial class App : Application
  38. {
  39. internal static CommandLineArguments CommandLineArguments;
  40. static ExportProvider exportProvider;
  41. public static ExportProvider ExportProvider => exportProvider;
  42. static IExportProviderFactory exportProviderFactory;
  43. public static IExportProviderFactory ExportProviderFactory => exportProviderFactory;
  44. internal static readonly IList<ExceptionData> StartupExceptions = new List<ExceptionData>();
  45. internal class ExceptionData
  46. {
  47. public Exception Exception;
  48. public string PluginName;
  49. }
  50. public App()
  51. {
  52. var cmdArgs = Environment.GetCommandLineArgs().Skip(1);
  53. App.CommandLineArguments = new CommandLineArguments(cmdArgs);
  54. if ((App.CommandLineArguments.SingleInstance ?? true) && !MiscSettingsPanel.CurrentMiscSettings.AllowMultipleInstances) {
  55. cmdArgs = cmdArgs.Select(FullyQualifyPath);
  56. string message = string.Join(Environment.NewLine, cmdArgs);
  57. if (SendToPreviousInstance("ILSpy:\r\n" + message, !App.CommandLineArguments.NoActivate)) {
  58. Environment.Exit(0);
  59. }
  60. }
  61. InitializeComponent();
  62. if (!System.Diagnostics.Debugger.IsAttached) {
  63. AppDomain.CurrentDomain.UnhandledException += ShowErrorBox;
  64. Dispatcher.CurrentDispatcher.UnhandledException += Dispatcher_UnhandledException;
  65. }
  66. TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException;
  67. // Cannot show MessageBox here, because WPF would crash with a XamlParseException
  68. // Remember and show exceptions in text output, once MainWindow is properly initialized
  69. try {
  70. // Set up VS MEF. For now, only do MEF1 part discovery, since that was in use before.
  71. // To support both MEF1 and MEF2 parts, just change this to:
  72. // var discovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(Resolver.DefaultInstance),
  73. // new AttributedPartDiscovery(Resolver.DefaultInstance));
  74. var discovery = new AttributedPartDiscoveryV1(Resolver.DefaultInstance);
  75. var catalog = ComposableCatalog.Create(Resolver.DefaultInstance);
  76. var pluginDir = Path.GetDirectoryName(typeof(App).Module.FullyQualifiedName);
  77. if (pluginDir != null) {
  78. foreach (var plugin in Directory.GetFiles(pluginDir, "*.Plugin.dll")) {
  79. var name = Path.GetFileNameWithoutExtension(plugin);
  80. try {
  81. var asm = Assembly.Load(name);
  82. var parts = discovery.CreatePartsAsync(asm).GetAwaiter().GetResult();
  83. catalog = catalog.AddParts(parts);
  84. } catch (Exception ex) {
  85. StartupExceptions.Add(new ExceptionData { Exception = ex, PluginName = name });
  86. }
  87. }
  88. }
  89. // Add the built-in parts
  90. catalog = catalog.AddParts(discovery.CreatePartsAsync(Assembly.GetExecutingAssembly()).GetAwaiter().GetResult());
  91. // If/When the project switches to .NET Standard/Core, this will be needed to allow metadata interfaces (as opposed
  92. // to metadata classes). When running on .NET Framework, it's automatic.
  93. // catalog.WithDesktopSupport();
  94. // If/When any part needs to import ICompositionService, this will be needed:
  95. // catalog.WithCompositionService();
  96. var config = CompositionConfiguration.Create(catalog);
  97. exportProviderFactory = config.CreateExportProviderFactory();
  98. exportProvider = exportProviderFactory.CreateExportProvider();
  99. // This throws exceptions for composition failures. Alternatively, the configuration's CompositionErrors property
  100. // could be used to log the errors directly. Used at the end so that it does not prevent the export provider setup.
  101. config.ThrowOnErrors();
  102. } catch (Exception ex) {
  103. StartupExceptions.Add(new ExceptionData { Exception = ex });
  104. }
  105. Languages.Initialize(exportProvider);
  106. EventManager.RegisterClassHandler(typeof(Window),
  107. Hyperlink.RequestNavigateEvent,
  108. new RequestNavigateEventHandler(Window_RequestNavigate));
  109. ILSpyTraceListener.Install();
  110. }
  111. protected override void OnStartup(StartupEventArgs e)
  112. {
  113. var output = new StringBuilder();
  114. if (ILSpy.MainWindow.FormatExceptions(StartupExceptions.ToArray(), output)) {
  115. MessageBox.Show(output.ToString(), "Sorry we crashed!");
  116. Environment.Exit(1);
  117. }
  118. base.OnStartup(e);
  119. }
  120. string FullyQualifyPath(string argument)
  121. {
  122. // Fully qualify the paths before passing them to another process,
  123. // because that process might use a different current directory.
  124. if (string.IsNullOrEmpty(argument) || argument[0] == '/')
  125. return argument;
  126. try {
  127. return Path.Combine(Environment.CurrentDirectory, argument);
  128. } catch (ArgumentException) {
  129. return argument;
  130. }
  131. }
  132. void DotNet40_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
  133. {
  134. // On .NET 4.0, an unobserved exception in a task terminates the process unless we mark it as observed
  135. e.SetObserved();
  136. }
  137. #region Exception Handling
  138. static void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
  139. {
  140. UnhandledException(e.Exception);
  141. e.Handled = true;
  142. }
  143. static void ShowErrorBox(object sender, UnhandledExceptionEventArgs e)
  144. {
  145. Exception ex = e.ExceptionObject as Exception;
  146. if (ex != null) {
  147. UnhandledException(ex);
  148. }
  149. }
  150. static void UnhandledException(Exception exception)
  151. {
  152. Debug.WriteLine(exception.ToString());
  153. for (Exception ex = exception; ex != null; ex = ex.InnerException) {
  154. ReflectionTypeLoadException rtle = ex as ReflectionTypeLoadException;
  155. if (rtle != null && rtle.LoaderExceptions.Length > 0) {
  156. exception = rtle.LoaderExceptions[0];
  157. Debug.WriteLine(exception.ToString());
  158. break;
  159. }
  160. }
  161. MessageBox.Show(exception.ToString(), "Sorry, we crashed");
  162. }
  163. #endregion
  164. #region Pass Command Line Arguments to previous instance
  165. bool SendToPreviousInstance(string message, bool activate)
  166. {
  167. bool success = false;
  168. NativeMethods.EnumWindows(
  169. (hWnd, lParam) => {
  170. string windowTitle = NativeMethods.GetWindowText(hWnd, 100);
  171. if (windowTitle.StartsWith("ILSpy", StringComparison.Ordinal)) {
  172. Debug.WriteLine("Found {0:x4}: {1}", hWnd, windowTitle);
  173. IntPtr result = Send(hWnd, message);
  174. Debug.WriteLine("WM_COPYDATA result: {0:x8}", result);
  175. if (result == (IntPtr)1) {
  176. if (activate)
  177. NativeMethods.SetForegroundWindow(hWnd);
  178. success = true;
  179. return false; // stop enumeration
  180. }
  181. }
  182. return true; // continue enumeration
  183. }, IntPtr.Zero);
  184. return success;
  185. }
  186. unsafe static IntPtr Send(IntPtr hWnd, string message)
  187. {
  188. const uint SMTO_NORMAL = 0;
  189. CopyDataStruct lParam;
  190. lParam.Padding = IntPtr.Zero;
  191. lParam.Size = message.Length * 2;
  192. fixed (char *buffer = message) {
  193. lParam.Buffer = (IntPtr)buffer;
  194. IntPtr result;
  195. // SendMessage with 3s timeout (e.g. when the target process is stopped in the debugger)
  196. if (NativeMethods.SendMessageTimeout(
  197. hWnd, NativeMethods.WM_COPYDATA, IntPtr.Zero, ref lParam,
  198. SMTO_NORMAL, 3000, out result) != IntPtr.Zero)
  199. {
  200. return result;
  201. } else {
  202. return IntPtr.Zero;
  203. }
  204. }
  205. }
  206. #endregion
  207. void Window_RequestNavigate(object sender, RequestNavigateEventArgs e)
  208. {
  209. ILSpy.MainWindow.Instance.NavigateTo(e);
  210. }
  211. }
  212. }