Browse Source

Use VS MEF for the extensibility hosting.

This replaces the uses of ComposeParts() with explicit GetExports for those fields previously marked [Import] or [ImportMany] in non-MEF-part classes.
pull/1058/head
Tim Van Holder 8 years ago
parent
commit
6f2af83131
  1. 2
      ILSpy/AboutPage.cs
  2. 70
      ILSpy/App.xaml.cs
  3. 13
      ILSpy/ContextMenuEntry.cs
  4. 2
      ILSpy/ILSpy.csproj
  5. 2
      ILSpy/Languages/CSharpLanguage.cs
  6. 7
      ILSpy/Languages/Languages.cs
  7. 12
      ILSpy/MainWindow.xaml.cs
  8. 10
      ILSpy/Options/OptionsDialog.xaml.cs
  9. 2
      ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs
  10. 2
      ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

2
ILSpy/AboutPage.cs

@ -81,7 +81,7 @@ namespace ICSharpCode.ILSpy @@ -81,7 +81,7 @@ namespace ICSharpCode.ILSpy
};
});
output.WriteLine();
foreach (var plugin in App.CompositionContainer.GetExportedValues<IAboutPageAddition>())
foreach (var plugin in App.ExportProvider.GetExportedValues<IAboutPageAddition>())
plugin.Write(output);
output.WriteLine();
using (Stream s = typeof(AboutPage).Assembly.GetManifestResourceStream(typeof(AboutPage), "README.txt")) {

70
ILSpy/App.xaml.cs

@ -18,7 +18,6 @@ @@ -18,7 +18,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -32,6 +31,8 @@ using System.Windows.Threading; @@ -32,6 +31,8 @@ using System.Windows.Threading;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.Options;
using Microsoft.VisualStudio.Composition;
namespace ICSharpCode.ILSpy
{
/// <summary>
@ -39,15 +40,18 @@ namespace ICSharpCode.ILSpy @@ -39,15 +40,18 @@ namespace ICSharpCode.ILSpy
/// </summary>
public partial class App : Application
{
static CompositionContainer compositionContainer;
public static CompositionContainer CompositionContainer {
get { return compositionContainer; }
}
internal static CommandLineArguments CommandLineArguments;
internal static IList<ExceptionData> StartupExceptions = new List<ExceptionData>();
static ExportProvider exportProvider;
public static ExportProvider ExportProvider => exportProvider;
static IExportProviderFactory exportProviderFactory;
public static IExportProviderFactory ExportProviderFactory => exportProviderFactory;
internal static readonly IList<ExceptionData> StartupExceptions = new List<ExceptionData>();
internal class ExceptionData
{
@ -68,32 +72,52 @@ namespace ICSharpCode.ILSpy @@ -68,32 +72,52 @@ namespace ICSharpCode.ILSpy
}
InitializeComponent();
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(App).Assembly));
// Don't use DirectoryCatalog, that causes problems if the plugins are from the Internet zone
// see http://stackoverflow.com/questions/8063841/mef-loading-plugins-from-a-network-shared-folder
string appPath = Path.GetDirectoryName(typeof(App).Module.FullyQualifiedName);
foreach (string plugin in Directory.GetFiles(appPath, "*.Plugin.dll")) {
string shortName = Path.GetFileNameWithoutExtension(plugin);
try {
var asm = Assembly.Load(shortName);
asm.GetTypes();
catalog.Catalogs.Add(new AssemblyCatalog(asm));
} catch (Exception ex) {
// Cannot show MessageBox here, because WPF would crash with a XamlParseException
// Remember and show exceptions in text output, once MainWindow is properly initialized
StartupExceptions.Add(new ExceptionData { Exception = ex, PluginName = shortName });
try {
// Set up VS MEF. For now, only do MEF1 part discovery, since that was in use before.
// To support both MEF1 and MEF2 parts, just change this to:
// var discovery = PartDiscovery.Combine(new AttributedPartDiscoveryV1(Resolver.DefaultInstance),
// new AttributedPartDiscovery(Resolver.DefaultInstance));
var discovery = new AttributedPartDiscoveryV1(Resolver.DefaultInstance);
var catalog = ComposableCatalog.Create(Resolver.DefaultInstance);
var pluginDir = Path.GetDirectoryName(typeof(App).Module.FullyQualifiedName);
if (pluginDir != null) {
foreach (var plugin in Directory.GetFiles(pluginDir, "*.Plugin.dll")) {
var name = Path.GetFileNameWithoutExtension(plugin);
try {
var asm = Assembly.Load(name);
var parts = discovery.CreatePartsAsync(asm).Result;
catalog = catalog.AddParts(parts);
} catch (Exception ex) {
StartupExceptions.Add(new ExceptionData { Exception = ex, PluginName = name });
}
}
compositionContainer = new CompositionContainer(catalog);
}
// Add the built-in parts
catalog = catalog.AddParts(discovery.CreatePartsAsync(Assembly.GetExecutingAssembly()).Result);
// If/When the project switches to .NET Standard/Core, this will be needed to allow metadata interfaces (as opposed
// to metadata classes). When running on .NET Framework, it's automatic.
// catalog.WithDesktopSupport();
// If/When any part needs to import ICompositionService, this will be needed:
// catalog.WithCompositionService();
var config = CompositionConfiguration.Create(catalog);
exportProviderFactory = config.CreateExportProviderFactory();
exportProvider = exportProviderFactory.CreateExportProvider();
// This throws exceptions for composition failures. Alternatively, the configuration's CompositionErrors property
// could be used to log the errors directly. Used at the end so that it does not prevent the export provider setup.
config.ThrowOnErrors();
}
catch (Exception ex) {
StartupExceptions.Add(new ExceptionData { Exception = ex });
}
if (!System.Diagnostics.Debugger.IsAttached) {
AppDomain.CurrentDomain.UnhandledException += ShowErrorBox;
Dispatcher.CurrentDispatcher.UnhandledException += Dispatcher_UnhandledException;
}
TaskScheduler.UnobservedTaskException += DotNet40_UnobservedTaskException;
Languages.Initialize(compositionContainer);
Languages.Initialize(exportProvider);
EventManager.RegisterClassHandler(typeof(Window),
Hyperlink.RequestNavigateEvent,

13
ILSpy/ContextMenuEntry.cs

@ -149,21 +149,22 @@ namespace ICSharpCode.ILSpy @@ -149,21 +149,22 @@ namespace ICSharpCode.ILSpy
readonly SharpTreeView treeView;
readonly DecompilerTextView textView;
readonly ListBox listBox;
readonly Lazy<IContextMenuEntry, IContextMenuEntryMetadata>[] entries;
[ImportMany(typeof(IContextMenuEntry))]
Lazy<IContextMenuEntry, IContextMenuEntryMetadata>[] entries = null;
private ContextMenuProvider()
{
entries = App.ExportProvider.GetExports<IContextMenuEntry, IContextMenuEntryMetadata>().ToArray();
}
ContextMenuProvider(SharpTreeView treeView, DecompilerTextView textView = null)
ContextMenuProvider(SharpTreeView treeView, DecompilerTextView textView = null) : this()
{
this.treeView = treeView;
this.textView = textView;
App.CompositionContainer.ComposeParts(this);
}
ContextMenuProvider(ListBox listBox)
ContextMenuProvider(ListBox listBox) : this()
{
this.listBox = listBox;
App.CompositionContainer.ComposeParts(this);
}
void treeView_ContextMenuOpening(object sender, ContextMenuEventArgs e)

2
ILSpy/ILSpy.csproj

@ -42,7 +42,6 @@ @@ -42,7 +42,6 @@
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
@ -51,6 +50,7 @@ @@ -51,6 +50,7 @@
<ItemGroup>
<PackageReference Include="AvalonEdit" Version="5.0.4" />
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="15.5.23" />
</ItemGroup>
<ItemGroup>

2
ILSpy/Languages/CSharpLanguage.cs

@ -390,7 +390,7 @@ namespace ICSharpCode.ILSpy @@ -390,7 +390,7 @@ namespace ICSharpCode.ILSpy
}
return new[] { Tuple.Create("EmbeddedResource", fileName) };
}
foreach (var handler in App.CompositionContainer.GetExportedValues<IResourceFileHandler>()) {
foreach (var handler in App.ExportProvider.GetExportedValues<IResourceFileHandler>()) {
if (handler.CanHandle(fileName, options)) {
entryStream.Position = 0;
return new[] { Tuple.Create(handler.EntryType, handler.WriteResourceToFile(assembly, fileName, entryStream, options)) };

7
ILSpy/Languages/Languages.cs

@ -18,9 +18,10 @@ @@ -18,9 +18,10 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using Microsoft.VisualStudio.Composition;
namespace ICSharpCode.ILSpy
{
public static class Languages
@ -39,10 +40,10 @@ namespace ICSharpCode.ILSpy @@ -39,10 +40,10 @@ namespace ICSharpCode.ILSpy
get { return allLanguages; }
}
internal static void Initialize(CompositionContainer composition)
internal static void Initialize(ExportProvider ep)
{
List<Language> languages = new List<Language>();
languages.AddRange(composition.GetExportedValues<Language>());
languages.AddRange(ep.GetExportedValues<Language>());
languages.Sort((a, b) => a.Name.CompareTo(b.Name));
#if DEBUG
languages.AddRange(ILAstLanguage.GetDebugLanguages());

12
ILSpy/MainWindow.xaml.cs

@ -20,7 +20,6 @@ using System; @@ -20,7 +20,6 @@ using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.IO;
using System.Linq;
@ -55,8 +54,7 @@ namespace ICSharpCode.ILSpy @@ -55,8 +54,7 @@ namespace ICSharpCode.ILSpy
AssemblyList assemblyList;
AssemblyListTreeNode assemblyListTreeNode;
[Import]
DecompilerTextView decompilerTextView = null;
readonly DecompilerTextView decompilerTextView;
static MainWindow instance;
@ -80,7 +78,7 @@ namespace ICSharpCode.ILSpy @@ -80,7 +78,7 @@ namespace ICSharpCode.ILSpy
this.DataContext = sessionSettings;
InitializeComponent();
App.CompositionContainer.ComposeParts(this);
decompilerTextView = App.ExportProvider.GetExportedValue<DecompilerTextView>();
mainPane.Content = decompilerTextView;
if (sessionSettings.SplitterPosition > 0 && sessionSettings.SplitterPosition < 1) {
@ -105,13 +103,12 @@ namespace ICSharpCode.ILSpy @@ -105,13 +103,12 @@ namespace ICSharpCode.ILSpy
}
#region Toolbar extensibility
[ImportMany("ToolbarCommand", typeof(ICommand))]
Lazy<ICommand, IToolbarCommandMetadata>[] toolbarCommands = null;
void InitToolbar()
{
int navigationPos = 0;
int openPos = 1;
var toolbarCommands = App.ExportProvider.GetExports<ICommand, IToolbarCommandMetadata>("ToolbarCommand");
foreach (var commandGroup in toolbarCommands.OrderBy(c => c.Metadata.ToolbarOrder).GroupBy(c => c.Metadata.ToolbarCategory)) {
if (commandGroup.Key == "Navigation") {
foreach (var command in commandGroup) {
@ -148,11 +145,10 @@ namespace ICSharpCode.ILSpy @@ -148,11 +145,10 @@ namespace ICSharpCode.ILSpy
#endregion
#region Main Menu extensibility
[ImportMany("MainMenuCommand", typeof(ICommand))]
Lazy<ICommand, IMainMenuCommandMetadata>[] mainMenuCommands = null;
void InitMainMenu()
{
var mainMenuCommands = App.ExportProvider.GetExports<ICommand, IMainMenuCommandMetadata>("MainMenuCommand");
foreach (var topLevelMenu in mainMenuCommands.OrderBy(c => c.Metadata.MenuOrder).GroupBy(c => c.Metadata.Menu)) {
var topLevelMenuItem = mainMenu.Items.OfType<MenuItem>().FirstOrDefault(m => (m.Header as string) == topLevelMenu.Key);
foreach (var category in topLevelMenu.GroupBy(c => c.Metadata.MenuCategory)) {

10
ILSpy/Options/OptionsDialog.xaml.cs

@ -30,13 +30,17 @@ namespace ICSharpCode.ILSpy.Options @@ -30,13 +30,17 @@ namespace ICSharpCode.ILSpy.Options
/// </summary>
public partial class OptionsDialog : Window
{
[ImportMany("OptionPages", typeof(UIElement), RequiredCreationPolicy = CreationPolicy.NonShared)]
Lazy<UIElement, IOptionsMetadata>[] optionPages = null;
readonly Lazy<UIElement, IOptionsMetadata>[] optionPages;
public OptionsDialog()
{
InitializeComponent();
App.CompositionContainer.ComposeParts(this);
// These used to have [ImportMany(..., RequiredCreationPolicy = CreationPolicy.NonShared)], so they use their own
// ExportProvider instance.
// FIXME: Ideally, the export provider should be disposed when it's no longer needed.
var ep = App.ExportProviderFactory.CreateExportProvider();
this.optionPages = ep.GetExports<UIElement, IOptionsMetadata>("OptionPages").ToArray();
ILSpySettings settings = ILSpySettings.Load();
foreach (var optionPage in optionPages.OrderBy(p => p.Metadata.Order)) {
TabItem tabItem = new TabItem();

2
ILSpy/TreeNodes/ResourceNodes/ResourceEntryNode.cs

@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -61,7 +61,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public static ILSpyTreeNode Create(string key, object data)
{
ILSpyTreeNode result = null;
foreach (var factory in App.CompositionContainer.GetExportedValues<IResourceNodeFactory>()) {
foreach (var factory in App.ExportProvider.GetExportedValues<IResourceNodeFactory>()) {
result = factory.CreateNode(key, data);
if (result != null)
return result;

2
ILSpy/TreeNodes/ResourceNodes/ResourceTreeNode.cs

@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -122,7 +122,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
public static ILSpyTreeNode Create(Resource resource)
{
ILSpyTreeNode result = null;
foreach (var factory in App.CompositionContainer.GetExportedValues<IResourceNodeFactory>()) {
foreach (var factory in App.ExportProvider.GetExportedValues<IResourceNodeFactory>()) {
result = factory.CreateNode(resource);
if (result != null)
break;

Loading…
Cancel
Save