PageRenderTime 42ms CodeModel.GetById 13ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

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