2323using System . Linq ;
2424using System . Reflection ;
2525using System . Runtime . Loader ;
26- using System . Text ;
2726using System . Threading . Tasks ;
2827using System . Windows ;
2928using System . Windows . Documents ;
3635
3736using Medo . Application ;
3837
39- using Microsoft . VisualStudio . Composition ;
40-
4138using TomsToolbox . Wpf . Styles ;
4239using ICSharpCode . ILSpyX . TreeView ;
4340
4744using System . Globalization ;
4845using System . Threading ;
4946
47+ using Microsoft . Extensions . DependencyInjection ;
48+
49+ using TomsToolbox . Composition . MicrosoftExtensions ;
50+ using TomsToolbox . Essentials ;
51+
5052namespace ICSharpCode . ILSpy
5153{
5254 /// <summary>
@@ -59,10 +61,9 @@ public partial class App : Application
5961
6062 public static IExportProvider ExportProvider { get ; private set ; }
6163
62- internal class ExceptionData
64+ internal record ExceptionData ( Exception Exception )
6365 {
64- public Exception Exception ;
65- public string PluginName ;
66+ public string PluginName { get ; init ; }
6667 }
6768
6869 public App ( )
@@ -80,6 +81,14 @@ public App()
8081
8182 InitializeComponent ( ) ;
8283
84+ if ( ! InitializeDependencyInjection ( SettingsService . Instance ) )
85+ {
86+ // There is something completely wrong with DI, probably some service registration is missing => nothing we can do to recover, so stop and shut down.
87+ Exit += ( _ , _ ) => MessageBox . Show ( StartupExceptions . FormatExceptions ( ) , "Sorry we crashed!" ) ;
88+ Shutdown ( 1 ) ;
89+ return ;
90+ }
91+
8392 if ( ! Debugger . IsAttached )
8493 {
8594 AppDomain . CurrentDomain . UnhandledException += ShowErrorBox ;
@@ -92,8 +101,6 @@ public App()
92101
93102 Resources . RegisterDefaultStyles ( ) ;
94103
95- InitializeMef ( ) . GetAwaiter ( ) . GetResult ( ) ;
96-
97104 // Register the export provider so that it can be accessed from WPF/XAML components.
98105 ExportProviderLocator . Register ( ExportProvider ) ;
99106 // Add data templates registered via MEF.
@@ -103,7 +110,7 @@ public App()
103110 ThemeManager . Current . Theme = sessionSettings . Theme ;
104111 if ( ! string . IsNullOrEmpty ( sessionSettings . CurrentCulture ) )
105112 {
106- Thread . CurrentThread . CurrentUICulture = CultureInfo . DefaultThreadCurrentUICulture = new CultureInfo ( sessionSettings . CurrentCulture ) ;
113+ Thread . CurrentThread . CurrentUICulture = CultureInfo . DefaultThreadCurrentUICulture = new ( sessionSettings . CurrentCulture ) ;
107114 }
108115
109116 EventManager . RegisterClassHandler ( typeof ( Window ) ,
@@ -125,6 +132,13 @@ public App()
125132 SettingsService . Instance . AssemblyListManager . CreateDefaultAssemblyLists ( ) ;
126133 }
127134
135+ public new static App Current => ( App ) Application . Current ;
136+
137+ public new MainWindow MainWindow {
138+ get => ( MainWindow ) base . MainWindow ;
139+ set => base . MainWindow = value ;
140+ }
141+
128142 private static void SingleInstance_NewInstanceDetected ( object sender , NewInstanceEventArgs e ) => ExportProvider . GetExportedValue < AssemblyTreeModel > ( ) . HandleSingleInstanceCommandLineArguments ( e . Args ) . HandleExceptions ( ) ;
129143
130144 static Assembly ResolvePluginDependencies ( AssemblyLoadContext context , AssemblyName assemblyName )
@@ -136,22 +150,17 @@ static Assembly ResolvePluginDependencies(AssemblyLoadContext context, AssemblyN
136150 return context . LoadFromAssemblyPath ( assemblyFileName ) ;
137151 }
138152
139- private static async Task InitializeMef ( )
153+ private static bool InitializeDependencyInjection ( SettingsService settingsService )
140154 {
141155 // Add custom logic for resolution of dependencies.
142156 // This necessary because the AssemblyLoadContext.LoadFromAssemblyPath and related methods,
143157 // do not automatically load dependencies.
144158 AssemblyLoadContext . Default . Resolving += ResolvePluginDependencies ;
145- // Cannot show MessageBox here, because WPF would crash with a XamlParseException
146- // Remember and show exceptions in text output, once MainWindow is properly initialized
147159 try
148160 {
149- // Set up VS MEF. For now, only do MEF1 part discovery, since that was in use before.
150- // To support both MEF1 and MEF2 parts, just change this to:
151- // var discovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(Resolver.DefaultInstance),
152- // new AttributedPartDiscovery(Resolver.DefaultInstance));
153- var discovery = new AttributedPartDiscoveryV1 ( Resolver . DefaultInstance ) ;
154- var catalog = ComposableCatalog . Create ( Resolver . DefaultInstance ) ;
161+ var services = new ServiceCollection ( ) ;
162+
163+
155164 var pluginDir = Path . GetDirectoryName ( typeof ( App ) . Module . FullyQualifiedName ) ;
156165 if ( pluginDir != null )
157166 {
@@ -160,62 +169,47 @@ private static async Task InitializeMef()
160169 var name = Path . GetFileNameWithoutExtension ( plugin ) ;
161170 try
162171 {
163- var asm = AssemblyLoadContext . Default . LoadFromAssemblyPath ( plugin ) ;
164- var parts = await discovery . CreatePartsAsync ( asm ) ;
165- catalog = catalog . AddParts ( parts ) ;
172+ var assembly = AssemblyLoadContext . Default . LoadFromAssemblyPath ( plugin ) ;
173+ services . BindExports ( assembly ) ;
166174 }
167175 catch ( Exception ex )
168176 {
169- StartupExceptions . Add ( new ExceptionData { Exception = ex , PluginName = name } ) ;
177+ // Cannot show MessageBox here, because WPF would crash with a XamlParseException
178+ // Remember and show exceptions in text output, once MainWindow is properly initialized
179+ StartupExceptions . Add ( new ( ex ) { PluginName = name } ) ;
170180 }
171181 }
172182 }
183+
173184 // Add the built-in parts: First, from ILSpyX
174- var xParts = await discovery . CreatePartsAsync ( typeof ( IAnalyzer ) . Assembly ) ;
175- catalog = catalog . AddParts ( xParts ) ;
185+ services . BindExports ( typeof ( IAnalyzer ) . Assembly ) ;
176186 // Then from ILSpy itself
177- var createdParts = await discovery . CreatePartsAsync ( Assembly . GetExecutingAssembly ( ) ) ;
178- catalog = catalog . AddParts ( createdParts ) ;
179-
180- // If/When the project switches to .NET Standard/Core, this will be needed to allow metadata interfaces (as opposed
181- // to metadata classes). When running on .NET Framework, it's automatic.
182- // catalog.WithDesktopSupport();
183- // If/When any part needs to import ICompositionService, this will be needed:
184- // catalog.WithCompositionService();
185- var config = CompositionConfiguration . Create ( catalog ) ;
186- var exportProviderFactory = config . CreateExportProviderFactory ( ) ;
187- ExportProvider = new ExportProviderAdapter ( exportProviderFactory . CreateExportProvider ( ) ) ;
188-
189- // This throws exceptions for composition failures. Alternatively, the configuration's CompositionErrors property
190- // could be used to log the errors directly. Used at the end so that it does not prevent the export provider setup.
191- config . ThrowOnErrors ( ) ;
192- }
193- catch ( CompositionFailedException ex ) when ( ex . InnerException is AggregateException agex )
194- {
195- foreach ( var inner in agex . InnerExceptions )
196- {
197- StartupExceptions . Add ( new ExceptionData { Exception = inner } ) ;
198- }
187+ services . BindExports ( Assembly . GetExecutingAssembly ( ) ) ;
188+ // Add the settings service
189+ services . AddSingleton ( settingsService ) ;
190+
191+ var serviceProvider = services . BuildServiceProvider ( new ServiceProviderOptions { ValidateOnBuild = true } ) ;
192+
193+ ExportProvider = new ExportProviderAdapter ( serviceProvider ) ;
194+
195+ return true ;
199196 }
200197 catch ( Exception ex )
201198 {
202- StartupExceptions . Add ( new ExceptionData { Exception = ex } ) ;
199+ if ( ex is AggregateException aggregate )
200+ StartupExceptions . AddRange ( aggregate . InnerExceptions . Select ( item => new ExceptionData ( ex ) ) ) ;
201+ else
202+ StartupExceptions . Add ( new ( ex ) ) ;
203+
204+ return false ;
203205 }
204206 }
205207
206208 protected override void OnStartup ( StartupEventArgs e )
207209 {
208210 base . OnStartup ( e ) ;
209211
210- var output = new StringBuilder ( ) ;
211-
212- if ( StartupExceptions . FormatExceptions ( output ) )
213- {
214- MessageBox . Show ( output . ToString ( ) , "Sorry we crashed!" ) ;
215- Environment . Exit ( 1 ) ;
216- }
217-
218- MainWindow = new MainWindow ( ) ;
212+ MainWindow = new ( ) ;
219213 MainWindow . Show ( ) ;
220214 }
221215
0 commit comments