Browse Source

Use MEF for extensibility in resource nodes.

pull/70/head
Daniel Grunwald 15 years ago
parent
commit
a0a14488e4
  1. 10
      ILSpy/AboutPage.cs
  2. 12
      ILSpy/App.xaml.cs
  3. 92
      ILSpy/BamlDecompiler.cs
  4. 2
      ILSpy/CSharpLanguage.cs
  5. 1
      ILSpy/ILSpy.csproj
  6. 11
      ILSpy/TextView/AvalonEditTextOutput.cs
  7. 2
      ILSpy/TextView/DecompilerTextView.cs
  8. 2
      ILSpy/TextView/DecompilerTextView.xaml
  9. 2
      ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs
  10. 4
      ILSpy/TreeNodes/ILSpyTreeNode.cs
  11. 149
      ILSpy/TreeNodes/ResourceEntryNode.cs
  12. 108
      ILSpy/TreeNodes/ResourceListTreeNode.cs
  13. 174
      ILSpy/TreeNodes/ResourceTreeNode.cs

10
ILSpy/AboutPage.cs

@ -52,6 +52,8 @@ namespace ICSharpCode.ILSpy @@ -52,6 +52,8 @@ namespace ICSharpCode.ILSpy
};
});
output.WriteLine();
foreach (var plugin in App.CompositionContainer.GetExportedValues<IAboutPageAddition>())
plugin.Write(output);
output.WriteLine();
using (Stream s = typeof(AboutPage).Assembly.GetManifestResourceStream(typeof(AboutPage), "README.txt")) {
using (StreamReader r = new StreamReader(s)) {
@ -256,4 +258,12 @@ namespace ICSharpCode.ILSpy @@ -256,4 +258,12 @@ namespace ICSharpCode.ILSpy
return tcs.Task;
}
}
/// <summary>
/// Interface that allows plugins to extend the about page.
/// </summary>
public interface IAboutPageAddition
{
void Write(ISmartTextOutput textOutput);
}
}

12
ILSpy/App.xaml.cs

@ -17,6 +17,7 @@ @@ -17,6 +17,7 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Hosting;
using System.Diagnostics;
using System.Threading;
@ -32,6 +33,12 @@ namespace ICSharpCode.ILSpy @@ -32,6 +33,12 @@ namespace ICSharpCode.ILSpy
/// </summary>
public partial class App : Application
{
static CompositionContainer compositionContainer;
public static CompositionContainer CompositionContainer {
get { return compositionContainer; }
}
public App()
{
InitializeComponent();
@ -39,9 +46,10 @@ namespace ICSharpCode.ILSpy @@ -39,9 +46,10 @@ namespace ICSharpCode.ILSpy
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(App).Assembly));
catalog.Catalogs.Add(new DirectoryCatalog(".", "*.Plugin.dll"));
var container = new CompositionContainer(catalog);
Languages.Initialize(container);
compositionContainer = new CompositionContainer(catalog);
Languages.Initialize(compositionContainer);
if (!Debugger.IsAttached) {
AppDomain.CurrentDomain.UnhandledException += ShowErrorBox;

92
ILSpy/BamlDecompiler.cs

@ -2,16 +2,23 @@ @@ -2,16 +2,23 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Baml2006;
using System.Xaml;
using System.Xml;
namespace ICSharpCode.ILSpy
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.ILSpy.TextView;
using ICSharpCode.ILSpy.TreeNodes;
namespace ICSharpCode.ILSpy.Baml
{
/// <remarks>Caution: use in separate AppDomain only!</remarks>
public class BamlDecompiler : MarshalByRefObject
sealed class BamlDecompiler : MarshalByRefObject
{
public BamlDecompiler()
{
@ -60,4 +67,85 @@ namespace ICSharpCode.ILSpy @@ -60,4 +67,85 @@ namespace ICSharpCode.ILSpy
return w.ToString();
}
}
[Export(typeof(IResourceNodeFactory))]
sealed class BamlResourceNodeFactory : IResourceNodeFactory
{
public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource)
{
return null;
}
public ILSpyTreeNode CreateNode(string key, Stream data)
{
if (key.EndsWith(".baml", StringComparison.OrdinalIgnoreCase))
return new BamlResourceEntryNode(key, data);
else
return null;
}
}
sealed class BamlResourceEntryNode : ResourceEntryNode
{
public BamlResourceEntryNode(string key, Stream data) : base(key, data)
{
}
internal override bool View(DecompilerTextView textView)
{
AvalonEditTextOutput output = new AvalonEditTextOutput();
IHighlightingDefinition highlighting = null;
textView.RunWithCancellation(
token => Task.Factory.StartNew(
() => {
try {
if (LoadBaml(output))
highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml");
} catch (Exception ex) {
output.Write(ex.ToString());
}
return output;
}),
t => textView.Show(t.Result, highlighting)
);
return true;
}
bool LoadBaml(AvalonEditTextOutput output)
{
var asm = this.Ancestors().OfType<AssemblyTreeNode>().FirstOrDefault().LoadedAssembly;
AppDomain bamlDecompilerAppDomain = null;
try {
BamlDecompiler decompiler = CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, asm.FileName);
MemoryStream bamlStream = new MemoryStream();
data.Position = 0;
data.CopyTo(bamlStream);
output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName));
return true;
} finally {
if (bamlDecompilerAppDomain != null)
AppDomain.Unload(bamlDecompilerAppDomain);
}
}
public static BamlDecompiler CreateBamlDecompilerInAppDomain(ref AppDomain appDomain, string assemblyFileName)
{
if (appDomain == null) {
// Construct and initialize settings for a second AppDomain.
AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup();
bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(assemblyFileName);
bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false;
bamlDecompilerAppDomainSetup.DisallowCodeDownload = true;
bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
// Create the second AppDomain.
appDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup);
}
return (BamlDecompiler)appDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName);
}
}
}

2
ILSpy/CSharpLanguage.cs

@ -324,7 +324,7 @@ namespace ICSharpCode.ILSpy @@ -324,7 +324,7 @@ namespace ICSharpCode.ILSpy
if (fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) {
MemoryStream ms = new MemoryStream();
entryStream.CopyTo(ms);
BamlDecompiler decompiler = TreeNodes.ResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assemblyFileName);
var decompiler = Baml.BamlResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assemblyFileName);
string xaml = null;
try {
xaml = decompiler.DecompileBaml(ms, assemblyFileName);

1
ILSpy/ILSpy.csproj

@ -157,6 +157,7 @@ @@ -157,6 +157,7 @@
<Compile Include="TreeNodes\ReferenceFolderTreeNode.cs" />
<Compile Include="TreeNodes\ResourceEntryNode.cs" />
<Compile Include="TreeNodes\ResourceListTreeNode.cs" />
<Compile Include="TreeNodes\ResourceTreeNode.cs" />
<Compile Include="TreeNodes\ThreadingSupport.cs" />
<Compile Include="TreeNodes\TypeTreeNode.cs" />
<EmbeddedResource Include="TextView\ILAsm-Mode.xshd" />

11
ILSpy/TextView/AvalonEditTextOutput.cs

@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TextView
/// <summary>
/// Text output implementation for AvalonEdit.
/// </summary>
sealed class AvalonEditTextOutput : ISmartTextOutput
public sealed class AvalonEditTextOutput : ISmartTextOutput
{
readonly StringBuilder b = new StringBuilder();
@ -78,18 +78,17 @@ namespace ICSharpCode.ILSpy.TextView @@ -78,18 +78,17 @@ namespace ICSharpCode.ILSpy.TextView
Stack<NewFolding> openFoldings = new Stack<NewFolding>();
/// <summary>List of all foldings that were written to the output</summary>
public readonly List<NewFolding> Foldings = new List<NewFolding>();
internal readonly List<NewFolding> Foldings = new List<NewFolding>();
public readonly DefinitionLookup DefinitionLookup = new DefinitionLookup();
internal readonly DefinitionLookup DefinitionLookup = new DefinitionLookup();
/// <summary>Embedded UIElements, see <see cref="UIElementGenerator"/>.</summary>
public readonly List<KeyValuePair<int, Lazy<UIElement>>> UIElements = new List<KeyValuePair<int, Lazy<UIElement>>>();
internal readonly List<KeyValuePair<int, Lazy<UIElement>>> UIElements = new List<KeyValuePair<int, Lazy<UIElement>>>();
/// <summary>
/// Gets the list of references (hyperlinks).
/// </summary>
public TextSegmentCollection<ReferenceSegment> References {
internal TextSegmentCollection<ReferenceSegment> References {
get { return references; }
}

2
ILSpy/TextView/DecompilerTextView.cs

@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy.TextView @@ -49,7 +49,7 @@ namespace ICSharpCode.ILSpy.TextView
/// Manages the TextEditor showing the decompiled code.
/// Contains all the threading logic that makes the decompiler work in the background.
/// </summary>
sealed partial class DecompilerTextView : UserControl
public sealed partial class DecompilerTextView : UserControl
{
readonly ReferenceElementGenerator referenceElementGenerator;
readonly UIElementGenerator uiElementGenerator;

2
ILSpy/TextView/DecompilerTextView.xaml

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
<UserControl x:Class="ICSharpCode.ILSpy.TextView.DecompilerTextView" x:ClassModifier="internal" x:Name="self"
<UserControl x:Class="ICSharpCode.ILSpy.TextView.DecompilerTextView" x:ClassModifier="public" x:Name="self"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ae="clr-namespace:ICSharpCode.AvalonEdit;assembly=ICSharpCode.AvalonEdit">

2
ILSpy/TreeNodes/Analyzer/AnalyzerTreeNode.cs

@ -22,7 +22,7 @@ using ICSharpCode.TreeView; @@ -22,7 +22,7 @@ using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
class AnalyzerTreeNode : SharpTreeNode
public class AnalyzerTreeNode : SharpTreeNode
{
Language language;

4
ILSpy/TreeNodes/ILSpyTreeNode.cs

@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -29,7 +29,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
/// <summary>
/// Base class of all ILSpy tree nodes.
/// </summary>
abstract class ILSpyTreeNode : SharpTreeNode
public abstract class ILSpyTreeNode : SharpTreeNode
{
FilterSettings filterSettings;
bool childrenNeedFiltering;
@ -166,7 +166,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -166,7 +166,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
enum FilterResult
public enum FilterResult
{
/// <summary>
/// Hides the node.

149
ILSpy/TreeNodes/ResourceEntryNode.cs

@ -6,20 +6,24 @@ using System.IO; @@ -6,20 +6,24 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.ComponentModel.Composition;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TextView;
using Microsoft.Win32;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes
{
class ResourceEntryNode : ILSpyTreeNode
/// <summary>
/// Entry in a .resources file
/// </summary>
public class ResourceEntryNode : ILSpyTreeNode
{
string key;
Stream value;
protected readonly string key;
protected readonly Stream data;
public override object Text {
get { return key.ToString(); }
@ -29,106 +33,93 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -29,106 +33,93 @@ namespace ICSharpCode.ILSpy.TreeNodes
get { return Images.Resource; }
}
public ResourceEntryNode(string key, Stream value)
public ResourceEntryNode(string key, Stream data)
{
if (key == null)
throw new ArgumentNullException("key");
if (data == null)
throw new ArgumentNullException("data");
this.key = key;
this.value = value;
this.data = data;
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
public static ILSpyTreeNode Create(string key, Stream data)
{
language.WriteCommentLine(output, string.Format("{0} = {1}", key, value));
ILSpyTreeNode result = null;
foreach (var factory in App.CompositionContainer.GetExportedValues<IResourceNodeFactory>()) {
result = factory.CreateNode(key, data);
if (result != null)
break;
}
return result ?? new ResourceEntryNode(key, data);
}
internal override bool View(DecompilerTextView textView)
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
AvalonEditTextOutput output = new AvalonEditTextOutput();
IHighlightingDefinition highlighting = null;
if (LoadImage(output)) {
textView.Show(output, highlighting);
} else {
textView.RunWithCancellation(
token => Task.Factory.StartNew(
() => {
try {
if (LoadBaml(output))
highlighting = HighlightingManager.Instance.GetDefinitionByExtension(".xml");
} catch (Exception ex) {
output.Write(ex.ToString());
}
return output;
}),
t => textView.Show(t.Result, highlighting)
);
}
return true;
language.WriteCommentLine(output, string.Format("{0} = {1}", key, data));
}
bool LoadImage(AvalonEditTextOutput output)
public override bool Save(DecompilerTextView textView)
{
try {
value.Position = 0;
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = value;
image.EndInit();
output.AddUIElement(() => new Image { Source = image });
output.WriteLine();
output.AddButton(Images.Save, "Save", delegate { Save(null); });
} catch (Exception) {
return false;
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key));
if (dlg.ShowDialog() == true) {
data.Position = 0;
using (var fs = dlg.OpenFile()) {
data.CopyTo(fs);
}
}
return true;
}
}
[Export(typeof(IResourceNodeFactory))]
sealed class ImageResourceNodeFactory : IResourceNodeFactory
{
static readonly string[] imageFileExtensions = { ".png", ".gif", ".bmp", ".jpg", ".ico" };
bool LoadBaml(AvalonEditTextOutput output)
public ILSpyTreeNode CreateNode(Mono.Cecil.Resource resource)
{
var asm = this.Ancestors().OfType<AssemblyTreeNode>().FirstOrDefault().LoadedAssembly;
AppDomain bamlDecompilerAppDomain = null;
try {
BamlDecompiler decompiler = CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, asm.FileName);
MemoryStream bamlStream = new MemoryStream();
value.Position = 0;
value.CopyTo(bamlStream);
output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName));
return true;
} finally {
if (bamlDecompilerAppDomain != null)
AppDomain.Unload(bamlDecompilerAppDomain);
EmbeddedResource er = resource as EmbeddedResource;
if (er != null) {
return CreateNode(er.Name, er.GetResourceStream());
}
return null;
}
public static BamlDecompiler CreateBamlDecompilerInAppDomain(ref AppDomain appDomain, string assemblyFileName)
public ILSpyTreeNode CreateNode(string key, Stream data)
{
if (appDomain == null) {
// Construct and initialize settings for a second AppDomain.
AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup();
bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(assemblyFileName);
bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false;
bamlDecompilerAppDomainSetup.DisallowCodeDownload = true;
bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
// Create the second AppDomain.
appDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup);
foreach (string fileExt in imageFileExtensions) {
if (key.EndsWith(fileExt, StringComparison.OrdinalIgnoreCase))
return new ImageResourceEntryNode(key, data);
}
return (BamlDecompiler)appDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName);
return null;
}
}
sealed class ImageResourceEntryNode : ResourceEntryNode
{
public ImageResourceEntryNode(string key, Stream data) : base(key, data)
{
}
public override bool Save(DecompilerTextView textView)
internal override bool View(DecompilerTextView textView)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = Path.GetFileName(DecompilerTextView.CleanUpName(key));
if (dlg.ShowDialog() == true) {
value.Position = 0;
using (var fs = dlg.OpenFile()) {
value.CopyTo(fs);
}
try {
AvalonEditTextOutput output = new AvalonEditTextOutput();
data.Position = 0;
BitmapImage image = new BitmapImage();
image.BeginInit();
image.StreamSource = data;
image.EndInit();
output.AddUIElement(() => new Image { Source = image });
output.WriteLine();
output.AddButton(Images.Save, "Save", delegate { Save(null); });
textView.Show(output, null);
return true;
} catch (Exception) {
return false;
}
return true;
}
}
}

108
ILSpy/TreeNodes/ResourceListTreeNode.cs

@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -43,7 +43,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
protected override void LoadChildren()
{
foreach (Resource r in module.Resources)
this.Children.Add(new ResourceTreeNode(r));
this.Children.Add(ResourceTreeNode.Create(r));
}
public override FilterResult Filter(FilterSettings settings)
@ -63,110 +63,4 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -63,110 +63,4 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
}
class ResourceTreeNode : ILSpyTreeNode
{
Resource r;
public ResourceTreeNode(Resource r)
{
this.LazyLoading = true;
this.r = r;
}
public Resource Resource {
get { return r; }
}
public override object Text {
get { return r.Name; }
}
public override object Icon {
get { return Images.Resource; }
}
public override FilterResult Filter(FilterSettings settings)
{
if (!settings.ShowInternalApi && (r.Attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private)
return FilterResult.Hidden;
if (settings.SearchTermMatches(r.Name))
return FilterResult.Match;
else
return FilterResult.Hidden;
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes));
ISmartTextOutput smartOutput = output as ISmartTextOutput;
if (smartOutput != null && r is EmbeddedResource) {
smartOutput.AddButton(Images.Save, "Save", delegate { Save(null); });
output.WriteLine();
}
}
internal override bool View(DecompilerTextView textView)
{
EmbeddedResource er = r as EmbeddedResource;
if (er != null) {
Stream s = er.GetResourceStream();
if (s != null && s.Length < DecompilerTextView.DefaultOutputLengthLimit) {
s.Position = 0;
FileType type = GuessFileType.DetectFileType(s);
if (type != FileType.Binary) {
s.Position = 0;
AvalonEditTextOutput output = new AvalonEditTextOutput();
output.Write(FileReader.OpenStream(s, Encoding.UTF8).ReadToEnd());
string ext;
if (type == FileType.Xml)
ext = ".xml";
else
ext = Path.GetExtension(DecompilerTextView.CleanUpName(er.Name));
textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(ext));
return true;
}
}
}
return false;
}
public override bool Save(TextView.DecompilerTextView textView)
{
EmbeddedResource er = r as EmbeddedResource;
if (er != null) {
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(er.Name);
if (dlg.ShowDialog() == true) {
Stream s = er.GetResourceStream();
s.Position = 0;
using (var fs = dlg.OpenFile()) {
s.CopyTo(fs);
}
}
return true;
}
return false;
}
protected override void LoadChildren()
{
EmbeddedResource er = r as EmbeddedResource;
if (er != null) {
try {
Stream s = er.GetResourceStream();
s.Position = 0;
if (er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) {
ResourceReader reader = new ResourceReader(s);
foreach (DictionaryEntry entry in reader.Cast<DictionaryEntry>().OrderBy(e => e.Key.ToString())) {
if (entry.Value is Stream)
Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value));
}
}
} catch (ArgumentException) {
}
}
}
}
}

174
ILSpy/TreeNodes/ResourceTreeNode.cs

@ -0,0 +1,174 @@ @@ -0,0 +1,174 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Resources;
using System.Text;
using System.Windows;
using System.Windows.Threading;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TextView;
using Microsoft.Win32;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes
{
public class ResourceTreeNode : ILSpyTreeNode
{
Resource r;
public ResourceTreeNode(Resource r)
{
if (r == null)
throw new ArgumentNullException("r");
this.r = r;
}
public Resource Resource {
get { return r; }
}
public override object Text {
get { return r.Name; }
}
public override object Icon {
get { return Images.Resource; }
}
public override FilterResult Filter(FilterSettings settings)
{
if (!settings.ShowInternalApi && (r.Attributes & ManifestResourceAttributes.VisibilityMask) == ManifestResourceAttributes.Private)
return FilterResult.Hidden;
if (settings.SearchTermMatches(r.Name))
return FilterResult.Match;
else
return FilterResult.Hidden;
}
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, string.Format("{0} ({1}, {2})", r.Name, r.ResourceType, r.Attributes));
ISmartTextOutput smartOutput = output as ISmartTextOutput;
if (smartOutput != null && r is EmbeddedResource) {
smartOutput.AddButton(Images.Save, "Save", delegate { Save(null); });
output.WriteLine();
}
}
internal override bool View(DecompilerTextView textView)
{
EmbeddedResource er = r as EmbeddedResource;
if (er != null) {
Stream s = er.GetResourceStream();
if (s != null && s.Length < DecompilerTextView.DefaultOutputLengthLimit) {
s.Position = 0;
FileType type = GuessFileType.DetectFileType(s);
if (type != FileType.Binary) {
s.Position = 0;
AvalonEditTextOutput output = new AvalonEditTextOutput();
output.Write(FileReader.OpenStream(s, Encoding.UTF8).ReadToEnd());
string ext;
if (type == FileType.Xml)
ext = ".xml";
else
ext = Path.GetExtension(DecompilerTextView.CleanUpName(er.Name));
textView.Show(output, HighlightingManager.Instance.GetDefinitionByExtension(ext));
return true;
}
}
}
return false;
}
public override bool Save(TextView.DecompilerTextView textView)
{
EmbeddedResource er = r as EmbeddedResource;
if (er != null) {
SaveFileDialog dlg = new SaveFileDialog();
dlg.FileName = DecompilerTextView.CleanUpName(er.Name);
if (dlg.ShowDialog() == true) {
Stream s = er.GetResourceStream();
s.Position = 0;
using (var fs = dlg.OpenFile()) {
s.CopyTo(fs);
}
}
return true;
}
return false;
}
public static ILSpyTreeNode Create(Resource resource)
{
ILSpyTreeNode result = null;
foreach (var factory in App.CompositionContainer.GetExportedValues<IResourceNodeFactory>()) {
result = factory.CreateNode(resource);
if (result != null)
break;
}
return result ?? new ResourceTreeNode(resource);
}
}
/// <summary>
/// This interface allows plugins to create custom nodes for resources.
/// </summary>
public interface IResourceNodeFactory
{
ILSpyTreeNode CreateNode(Resource resource);
ILSpyTreeNode CreateNode(string key, Stream data);
}
[Export(typeof(IResourceNodeFactory))]
sealed class ResourcesFileTreeNodeFactory : IResourceNodeFactory
{
public ILSpyTreeNode CreateNode(Resource resource)
{
EmbeddedResource er = resource as EmbeddedResource;
if (er != null && er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) {
return new ResourcesFileTreeNode(er);
}
return null;
}
public ILSpyTreeNode CreateNode(string key, Stream data)
{
return null;
}
}
sealed class ResourcesFileTreeNode : ResourceTreeNode
{
public ResourcesFileTreeNode(EmbeddedResource er) : base(er)
{
this.LazyLoading = true;
}
protected override void LoadChildren()
{
EmbeddedResource er = this.Resource as EmbeddedResource;
if (er != null) {
Stream s = er.GetResourceStream();
s.Position = 0;
ResourceReader reader;
try {
reader = new ResourceReader(s);
} catch (ArgumentException) {
return;
}
foreach (DictionaryEntry entry in reader.Cast<DictionaryEntry>().OrderBy(e => e.Key.ToString())) {
if (entry.Value is Stream)
Children.Add(ResourceEntryNode.Create(entry.Key.ToString(), (Stream)entry.Value));
}
}
}
}
}
Loading…
Cancel
Save