From 7f1a9462ffd3ff6e7ea4f659e8eee01716ea536a Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 23 Feb 2011 21:01:49 +0100 Subject: [PATCH] Unpack WPF resources when saving as project. --- ILSpy/CSharpLanguage.cs | 87 ++++++++++++++++++++----- ILSpy/TreeNodes/ResourceEntryNode.cs | 46 ++++++++----- ILSpy/TreeNodes/ResourceListTreeNode.cs | 5 +- 3 files changed, 102 insertions(+), 36 deletions(-) diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 8502d4a85..7ff152475 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -17,12 +17,15 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading; +using System.Resources; using System.Threading.Tasks; +using System.Xaml; using System.Xml; + using Decompiler; using Decompiler.Transforms; using ICSharpCode.Decompiler; @@ -117,7 +120,7 @@ namespace ICSharpCode.ILSpy if (options.SaveAsProjectDirectory != null) { HashSet directories = new HashSet(StringComparer.OrdinalIgnoreCase); var files = WriteCodeFilesInProject(assembly, options, directories).ToList(); - files.AddRange(WriteResourceFilesInProject(assembly, options, directories)); + files.AddRange(WriteResourceFilesInProject(assembly, fileName, options, directories)); WriteProjectFile(new TextOutputWriter(output), files, assembly.MainModule); } else { foreach (TypeDefinition type in assembly.MainModule.Types) { @@ -291,26 +294,76 @@ namespace ICSharpCode.ILSpy #endregion #region WriteResourceFilesInProject - IEnumerable> WriteResourceFilesInProject(AssemblyDefinition assembly, DecompilationOptions options, HashSet directories) + IEnumerable> WriteResourceFilesInProject(AssemblyDefinition assembly, string assemblyFileName, DecompilationOptions options, HashSet directories) { - foreach (EmbeddedResource r in assembly.MainModule.Resources.OfType()) { - string[] splitName = r.Name.Split('.'); - string fileName = TextView.DecompilerTextView.CleanUpName(r.Name); - for (int i = splitName.Length - 1; i > 0; i--) { - string ns = string.Join(".", splitName, 0, i); - if (directories.Contains(ns)) { - string name = string.Join(".", splitName, i, splitName.Length - i); - fileName = Path.Combine(ns, TextView.DecompilerTextView.CleanUpName(name)); - break; + AppDomain bamlDecompilerAppDomain = null; + try { + foreach (EmbeddedResource r in assembly.MainModule.Resources.OfType()) { + string fileName; + Stream s = r.GetResourceStream(); + s.Position = 0; + if (r.Name.EndsWith(".g.resources", StringComparison.OrdinalIgnoreCase)) { + IEnumerable rs = null; + try { + rs = new ResourceSet(s).Cast(); + } catch (ArgumentException) { + } + if (rs != null && rs.All(e => e.Value is Stream)) { + foreach (var pair in rs) { + fileName = Path.Combine(((string)pair.Key).Split('/').Select(p => TextView.DecompilerTextView.CleanUpName(p)).ToArray()); + string dirName = Path.GetDirectoryName(fileName); + if (!string.IsNullOrEmpty(dirName) && directories.Add(dirName)) { + Directory.CreateDirectory(Path.Combine(options.SaveAsProjectDirectory, dirName)); + } + Stream entryStream = (Stream)pair.Value; + entryStream.Position = 0; + if (fileName.EndsWith(".baml", StringComparison.OrdinalIgnoreCase)) { + MemoryStream ms = new MemoryStream(); + entryStream.CopyTo(ms); + BamlDecompiler decompiler = TreeNodes.ResourceEntryNode.CreateBamlDecompilerInAppDomain(ref bamlDecompilerAppDomain, assemblyFileName); + string xaml = null; + try { + xaml = decompiler.DecompileBaml(ms, assemblyFileName); + } catch (XamlXmlWriterException) {} // ignore XAML writer exceptions + if (xaml != null) { + File.WriteAllText(Path.Combine(options.SaveAsProjectDirectory, Path.ChangeExtension(fileName, ".xaml")), xaml); + yield return Tuple.Create("Page", Path.ChangeExtension(fileName, ".xaml")); + continue; + } + } + using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) { + entryStream.CopyTo(fs); + } + yield return Tuple.Create("Resource", fileName); + } + continue; + } } + fileName = GetFileNameForResource(r.Name, directories); + using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) { + s.CopyTo(fs); + } + yield return Tuple.Create("EmbeddedResource", fileName); } - Stream s = r.GetResourceStream(); - s.Position = 0; - using (FileStream fs = new FileStream(Path.Combine(options.SaveAsProjectDirectory, fileName), FileMode.Create, FileAccess.Write)) { - s.CopyTo(fs); + } finally { + if (bamlDecompilerAppDomain != null) + AppDomain.Unload(bamlDecompilerAppDomain); + } + } + + string GetFileNameForResource(string fullName, HashSet directories) + { + string[] splitName = fullName.Split('.'); + string fileName = TextView.DecompilerTextView.CleanUpName(fullName); + for (int i = splitName.Length - 1; i > 0; i--) { + string ns = string.Join(".", splitName, 0, i); + if (directories.Contains(ns)) { + string name = string.Join(".", splitName, i, splitName.Length - i); + fileName = Path.Combine(ns, TextView.DecompilerTextView.CleanUpName(name)); + break; } - yield return Tuple.Create("EmbeddedResource", fileName); } + return fileName; } #endregion diff --git a/ILSpy/TreeNodes/ResourceEntryNode.cs b/ILSpy/TreeNodes/ResourceEntryNode.cs index a167a916d..dc982bf00 100644 --- a/ILSpy/TreeNodes/ResourceEntryNode.cs +++ b/ILSpy/TreeNodes/ResourceEntryNode.cs @@ -86,24 +86,36 @@ namespace ICSharpCode.ILSpy.TreeNodes { var asm = this.Ancestors().OfType().FirstOrDefault().LoadedAssembly; - // Construct and initialize settings for a second AppDomain. - AppDomainSetup bamlDecompilerAppDomainSetup = new AppDomainSetup(); - bamlDecompilerAppDomainSetup.ApplicationBase = "file:///" + Path.GetDirectoryName(asm.FileName); - bamlDecompilerAppDomainSetup.DisallowBindingRedirects = false; - bamlDecompilerAppDomainSetup.DisallowCodeDownload = true; - bamlDecompilerAppDomainSetup.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; + 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); + } + } + + 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 bamlDecompilerAppDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup); - - BamlDecompiler decompiler = (BamlDecompiler)bamlDecompilerAppDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName); - - MemoryStream bamlStream = new MemoryStream(); - value.Position = 0; - value.CopyTo(bamlStream); - - output.Write(decompiler.DecompileBaml(bamlStream, asm.FileName)); - return true; + // Create the second AppDomain. + appDomain = AppDomain.CreateDomain("BamlDecompiler AD", null, bamlDecompilerAppDomainSetup); + } + return (BamlDecompiler)appDomain.CreateInstanceFromAndUnwrap(typeof(BamlDecompiler).Assembly.Location, typeof(BamlDecompiler).FullName); } public override bool Save(DecompilerTextView textView) diff --git a/ILSpy/TreeNodes/ResourceListTreeNode.cs b/ILSpy/TreeNodes/ResourceListTreeNode.cs index f3d3ed5ca..44ae1e2c5 100644 --- a/ILSpy/TreeNodes/ResourceListTreeNode.cs +++ b/ILSpy/TreeNodes/ResourceListTreeNode.cs @@ -156,9 +156,10 @@ namespace ICSharpCode.ILSpy.TreeNodes if (er != null) { try { Stream s = er.GetResourceStream(); + s.Position = 0; if (er.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) { - ResourceSet set = new ResourceSet(s); - foreach (DictionaryEntry entry in set.Cast().OrderBy(e => e.Key.ToString())) { + ResourceReader reader = new ResourceReader(s); + foreach (DictionaryEntry entry in reader.Cast().OrderBy(e => e.Key.ToString())) { if (entry.Value is Stream) Children.Add(new ResourceEntryNode(entry.Key.ToString(), (Stream)entry.Value)); }