Browse Source

- Introduce ProjectItemInfo instead of tuples in WholeProjectDecompiler project item generation.

- Set 'LogicalName' attribute for all decompiled resources. This makes it possible to correctly recompile projects with resource names that are not valid filenames.
- Set Generator and SubType properties for XAML files.
pull/2857/head
Siegfried Pammer 3 years ago
parent
commit
ff0e929866
  1. 5
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectFileWriter.cs
  2. 14
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterDefault.cs
  3. 31
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterSdkStyle.cs
  4. 53
      ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs
  5. 2
      ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs
  6. 4
      ILSpy/Languages/CSharpLanguage.cs
  7. 3
      ILSpy/Languages/IResourceFileHandler.cs

5
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/IProjectFileWriter.cs

@ -34,9 +34,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary> /// </summary>
/// <param name="target">The target to write to.</param> /// <param name="target">The target to write to.</param>
/// <param name="project">The information about the project being created.</param> /// <param name="project">The information about the project being created.</param>
/// <param name="files">A collection of source files to be included into the project, each item is a pair /// <param name="files">A collection of source files to be included into the project.</param>
/// of the project entry type and the file path.</param>
/// <param name="module">The module being decompiled.</param> /// <param name="module">The module being decompiled.</param>
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<(string itemType, string fileName)> files, PEFile module); void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module);
} }
} }

14
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterDefault.cs

@ -25,6 +25,7 @@ using System.Xml;
using ICSharpCode.Decompiler.Metadata; using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Solution; using ICSharpCode.Decompiler.Solution;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
@ -43,7 +44,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public void Write( public void Write(
TextWriter target, TextWriter target,
IProjectInfoProvider project, IProjectInfoProvider project,
IEnumerable<(string itemType, string fileName)> files, IEnumerable<ProjectItemInfo> files,
PEFile module) PEFile module)
{ {
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; const string ns = "http://schemas.microsoft.com/developer/msbuild/2003";
@ -162,13 +163,18 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
} }
w.WriteEndElement(); // </ItemGroup> (References) w.WriteEndElement(); // </ItemGroup> (References)
foreach (IGrouping<string, string> gr in from f in files group f.fileName by f.itemType into g orderby g.Key select g) foreach (IGrouping<string, ProjectItemInfo> gr in files.GroupBy(f => f.ItemType).OrderBy(g => g.Key))
{ {
w.WriteStartElement("ItemGroup"); w.WriteStartElement("ItemGroup");
foreach (string file in gr.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) foreach (var item in gr.OrderBy(f => f.FileName, StringComparer.OrdinalIgnoreCase))
{ {
w.WriteStartElement(gr.Key); w.WriteStartElement(gr.Key);
w.WriteAttributeString("Include", file); w.WriteAttributeString("Include", item.FileName);
if (item.AdditionalProperties != null)
{
foreach (var (key, value) in item.AdditionalProperties)
w.WriteAttributeString(key, value);
}
w.WriteEndElement(); w.WriteEndElement();
} }
w.WriteEndElement(); w.WriteEndElement();

31
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/ProjectFileWriterSdkStyle.cs

@ -66,7 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
public void Write( public void Write(
TextWriter target, TextWriter target,
IProjectInfoProvider project, IProjectInfoProvider project,
IEnumerable<(string itemType, string fileName)> files, IEnumerable<ProjectItemInfo> files,
PEFile module) PEFile module)
{ {
using (XmlTextWriter xmlWriter = new XmlTextWriter(target)) using (XmlTextWriter xmlWriter = new XmlTextWriter(target))
@ -76,7 +76,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
} }
} }
static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<(string itemType, string fileName)> files, PEFile module) static void Write(XmlTextWriter xml, IProjectInfoProvider project, IEnumerable<ProjectItemInfo> files, PEFile module)
{ {
xml.WriteStartElement("Project"); xml.WriteStartElement("Project");
@ -188,27 +188,27 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
} }
} }
static void WriteMiscellaneousPropertyGroup(XmlTextWriter xml, IEnumerable<(string itemType, string fileName)> files) static void WriteMiscellaneousPropertyGroup(XmlTextWriter xml, IEnumerable<ProjectItemInfo> files)
{ {
var (itemType, fileName) = files.FirstOrDefault(t => t.itemType == "ApplicationIcon"); var (itemType, fileName) = files.FirstOrDefault(t => t.ItemType == "ApplicationIcon");
if (fileName != null) if (fileName != null)
xml.WriteElementString("ApplicationIcon", fileName); xml.WriteElementString("ApplicationIcon", fileName);
(itemType, fileName) = files.FirstOrDefault(t => t.itemType == "ApplicationManifest"); (itemType, fileName) = files.FirstOrDefault(t => t.ItemType == "ApplicationManifest");
if (fileName != null) if (fileName != null)
xml.WriteElementString("ApplicationManifest", fileName); xml.WriteElementString("ApplicationManifest", fileName);
if (files.Any(t => t.itemType == "EmbeddedResource")) if (files.Any(t => t.ItemType == "EmbeddedResource"))
xml.WriteElementString("RootNamespace", string.Empty); xml.WriteElementString("RootNamespace", string.Empty);
// TODO: We should add CustomToolNamespace for resources, otherwise we should add empty RootNamespace // TODO: We should add CustomToolNamespace for resources, otherwise we should add empty RootNamespace
} }
static void WriteResources(XmlTextWriter xml, IEnumerable<(string itemType, string fileName)> files) static void WriteResources(XmlTextWriter xml, IEnumerable<ProjectItemInfo> files)
{ {
// remove phase // remove phase
foreach (var (itemType, fileName) in files.Where(t => t.itemType == "EmbeddedResource")) foreach (var item in files.Where(t => t.ItemType == "EmbeddedResource"))
{ {
string buildAction = Path.GetExtension(fileName).ToUpperInvariant() switch { string buildAction = Path.GetExtension(item.FileName).ToUpperInvariant() switch {
".CS" => "Compile", ".CS" => "Compile",
".RESX" => "EmbeddedResource", ".RESX" => "EmbeddedResource",
_ => "None" _ => "None"
@ -217,18 +217,23 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
continue; continue;
xml.WriteStartElement(buildAction); xml.WriteStartElement(buildAction);
xml.WriteAttributeString("Remove", fileName); xml.WriteAttributeString("Remove", item.FileName);
xml.WriteEndElement(); xml.WriteEndElement();
} }
// include phase // include phase
foreach (var (itemType, fileName) in files.Where(t => t.itemType == "EmbeddedResource")) foreach (var item in files.Where(t => t.ItemType == "EmbeddedResource"))
{ {
if (Path.GetExtension(fileName) == ".resx") if (Path.GetExtension(item.FileName) == ".resx")
continue; continue;
xml.WriteStartElement("EmbeddedResource"); xml.WriteStartElement("EmbeddedResource");
xml.WriteAttributeString("Include", fileName); xml.WriteAttributeString("Include", item.FileName);
if (item.AdditionalProperties != null)
{
foreach (var (key, value) in item.AdditionalProperties)
xml.WriteAttributeString(key, value);
}
xml.WriteEndElement(); xml.WriteEndElement();
} }
} }

53
ICSharpCode.Decompiler/CSharp/ProjectDecompiler/WholeProjectDecompiler.cs

@ -151,8 +151,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
TargetDirectory = targetDirectory; TargetDirectory = targetDirectory;
directories.Clear(); directories.Clear();
var resources = WriteResourceFilesInProject(moduleDefinition).ToList(); var resources = WriteResourceFilesInProject(moduleDefinition).ToList();
var files = WriteCodeFilesInProject(moduleDefinition, resources.SelectMany(r => r.partialTypes ?? Enumerable.Empty<PartialTypeInfo>()).ToList(), cancellationToken).ToList(); var files = WriteCodeFilesInProject(moduleDefinition, resources.SelectMany(r => r.PartialTypes ?? Enumerable.Empty<PartialTypeInfo>()).ToList(), cancellationToken).ToList();
files.AddRange(resources.Select(r => (r.itemType, r.fileName))); files.AddRange(resources);
files.AddRange(WriteMiscellaneousFilesInProject(moduleDefinition)); files.AddRange(WriteMiscellaneousFilesInProject(moduleDefinition));
if (StrongNameKeyFile != null) if (StrongNameKeyFile != null)
{ {
@ -190,7 +190,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
return decompiler; return decompiler;
} }
IEnumerable<(string itemType, string fileName)> WriteAssemblyInfo(DecompilerTypeSystem ts, CancellationToken cancellationToken) IEnumerable<ProjectItemInfo> WriteAssemblyInfo(DecompilerTypeSystem ts, CancellationToken cancellationToken)
{ {
var decompiler = CreateDecompiler(ts); var decompiler = CreateDecompiler(ts);
decompiler.CancellationToken = cancellationToken; decompiler.CancellationToken = cancellationToken;
@ -205,10 +205,10 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions)); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
} }
return new[] { ("Compile", assemblyInfo) }; return new[] { new ProjectItemInfo("Compile", assemblyInfo) };
} }
IEnumerable<(string itemType, string fileName)> WriteCodeFilesInProject(Metadata.PEFile module, IList<PartialTypeInfo> partialTypes, CancellationToken cancellationToken) IEnumerable<ProjectItemInfo> WriteCodeFilesInProject(Metadata.PEFile module, IList<PartialTypeInfo> partialTypes, CancellationToken cancellationToken)
{ {
var metadata = module.Metadata; var metadata = module.Metadata;
var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td)) var files = module.Metadata.GetTopLevelTypeDefinitions().Where(td => IncludeTypeWhenDecompilingProject(module, td))
@ -229,7 +229,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
progress.TotalUnits = files.Count; progress.TotalUnits = files.Count;
} }
return files.Select(f => ("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken)); return files.Select(f => new ProjectItemInfo("Compile", f.Key)).Concat(WriteAssemblyInfo(ts, cancellationToken));
string GetFileFileNameForHandle(TypeDefinitionHandle h) string GetFileFileNameForHandle(TypeDefinitionHandle h)
{ {
@ -308,7 +308,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
#endregion #endregion
#region WriteResourceFilesInProject #region WriteResourceFilesInProject
protected virtual IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceFilesInProject(Metadata.PEFile module) protected virtual IEnumerable<ProjectItemInfo> WriteResourceFilesInProject(Metadata.PEFile module)
{ {
foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded)) foreach (var r in module.Resources.Where(r => r.ResourceType == ResourceType.Embedded))
{ {
@ -318,7 +318,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) if (r.Name.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
{ {
bool decodedIntoIndividualFiles; bool decodedIntoIndividualFiles;
var individualResources = new List<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)>(); var individualResources = new List<ProjectItemInfo>();
try try
{ {
var resourcesFile = new ResourcesFile(stream); var resourcesFile = new ResourcesFile(stream);
@ -378,12 +378,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
stream.Position = 0; stream.Position = 0;
stream.CopyTo(fs); stream.CopyTo(fs);
} }
yield return ("EmbeddedResource", fileName, null); yield return new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", r.Name);
} }
} }
} }
protected virtual IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) protected virtual IEnumerable<ProjectItemInfo> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
{ {
if (fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase)) if (fileName.EndsWith(".resources", StringComparison.OrdinalIgnoreCase))
{ {
@ -398,7 +398,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
writer.AddResource(entry.Key, entry.Value); writer.AddResource(entry.Key, entry.Value);
} }
} }
return new[] { ("EmbeddedResource", resx, (List<PartialTypeInfo>)null) }; return new[] { new ProjectItemInfo("EmbeddedResource", resx).With("LogicalName", resourceName) };
} }
catch (BadImageFormatException) catch (BadImageFormatException)
{ {
@ -413,7 +413,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
{ {
entryStream.CopyTo(fs); entryStream.CopyTo(fs);
} }
return new[] { ("EmbeddedResource", fileName, (List<PartialTypeInfo>)null) }; return new[] { new ProjectItemInfo("EmbeddedResource", fileName).With("LogicalName", resourceName) };
} }
string GetFileNameForResource(string fullName) string GetFileNameForResource(string fullName)
@ -444,7 +444,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
#endregion #endregion
#region WriteMiscellaneousFilesInProject #region WriteMiscellaneousFilesInProject
protected virtual IEnumerable<(string itemType, string fileName)> WriteMiscellaneousFilesInProject(PEFile module) protected virtual IEnumerable<ProjectItemInfo> WriteMiscellaneousFilesInProject(PEFile module)
{ {
var resources = module.Reader.ReadWin32Resources(); var resources = module.Reader.ReadWin32Resources();
if (resources == null) if (resources == null)
@ -454,21 +454,21 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
if (appIcon != null) if (appIcon != null)
{ {
File.WriteAllBytes(Path.Combine(TargetDirectory, "app.ico"), appIcon); File.WriteAllBytes(Path.Combine(TargetDirectory, "app.ico"), appIcon);
yield return ("ApplicationIcon", "app.ico"); yield return new ProjectItemInfo("ApplicationIcon", "app.ico");
} }
byte[] appManifest = CreateApplicationManifest(resources); byte[] appManifest = CreateApplicationManifest(resources);
if (appManifest != null && !IsDefaultApplicationManifest(appManifest)) if (appManifest != null && !IsDefaultApplicationManifest(appManifest))
{ {
File.WriteAllBytes(Path.Combine(TargetDirectory, "app.manifest"), appManifest); File.WriteAllBytes(Path.Combine(TargetDirectory, "app.manifest"), appManifest);
yield return ("ApplicationManifest", "app.manifest"); yield return new ProjectItemInfo("ApplicationManifest", "app.manifest");
} }
var appConfig = module.FileName + ".config"; var appConfig = module.FileName + ".config";
if (File.Exists(appConfig)) if (File.Exists(appConfig))
{ {
File.Copy(appConfig, Path.Combine(TargetDirectory, "app.config"), overwrite: true); File.Copy(appConfig, Path.Combine(TargetDirectory, "app.config"), overwrite: true);
yield return ("ApplicationConfig", Path.GetFileName(appConfig)); yield return new ProjectItemInfo("ApplicationConfig", Path.GetFileName(appConfig));
} }
} }
@ -753,4 +753,25 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
return TargetServices.DetectTargetFramework(module).Moniker != null; return TargetServices.DetectTargetFramework(module).Moniker != null;
} }
} }
public record struct ProjectItemInfo(string ItemType, string FileName)
{
public List<PartialTypeInfo> PartialTypes { get; set; } = null;
public Dictionary<string, string> AdditionalProperties { get; set; } = null;
public ProjectItemInfo With(string name, string value)
{
AdditionalProperties ??= new Dictionary<string, string>();
AdditionalProperties.Add(name, value);
return this;
}
public ProjectItemInfo With(IEnumerable<KeyValuePair<string, string>> pairs)
{
AdditionalProperties ??= new Dictionary<string, string>();
AdditionalProperties.AddRange(pairs);
return this;
}
}
} }

2
ILSpy.BamlDecompiler/BamlResourceNodeFactory.cs

@ -72,6 +72,8 @@ namespace ILSpy.BamlDecompiler
{ {
fileName = Path.ChangeExtension(fileName, ".xaml"); fileName = Path.ChangeExtension(fileName, ".xaml");
} }
context.AdditionalProperties.Add("Generator", "MSBuild:Compile");
context.AdditionalProperties.Add("SubType", "Designer");
string saveFileName = Path.Combine(context.DecompilationOptions.SaveAsProjectDirectory, fileName); string saveFileName = Path.Combine(context.DecompilationOptions.SaveAsProjectDirectory, fileName);
Directory.CreateDirectory(Path.GetDirectoryName(saveFileName)); Directory.CreateDirectory(Path.GetDirectoryName(saveFileName));
result.Xaml.Save(saveFileName); result.Xaml.Save(saveFileName);

4
ILSpy/Languages/CSharpLanguage.cs

@ -517,7 +517,7 @@ namespace ICSharpCode.ILSpy
this.options = options; this.options = options;
} }
protected override IEnumerable<(string itemType, string fileName, List<PartialTypeInfo> partialTypes)> WriteResourceToFile(string fileName, string resourceName, Stream entryStream) protected override IEnumerable<ProjectItemInfo> WriteResourceToFile(string fileName, string resourceName, Stream entryStream)
{ {
var context = new ResourceFileHandlerContext(options); var context = new ResourceFileHandlerContext(options);
foreach (var handler in App.ExportProvider.GetExportedValues<IResourceFileHandler>()) foreach (var handler in App.ExportProvider.GetExportedValues<IResourceFileHandler>())
@ -527,7 +527,7 @@ namespace ICSharpCode.ILSpy
entryStream.Position = 0; entryStream.Position = 0;
fileName = handler.WriteResourceToFile(assembly, fileName, entryStream, context); fileName = handler.WriteResourceToFile(assembly, fileName, entryStream, context);
return new[] { (handler.EntryType, fileName, context.PartialTypes) }; return new[] { new ProjectItemInfo(handler.EntryType, fileName) { PartialTypes = context.PartialTypes }.With(context.AdditionalProperties) };
} }
} }
return base.WriteResourceToFile(fileName, resourceName, entryStream); return base.WriteResourceToFile(fileName, resourceName, entryStream);

3
ILSpy/Languages/IResourceFileHandler.cs

@ -36,6 +36,9 @@ namespace ICSharpCode.ILSpy
readonly List<PartialTypeInfo> partialTypes = new(); readonly List<PartialTypeInfo> partialTypes = new();
internal List<PartialTypeInfo> PartialTypes => partialTypes; internal List<PartialTypeInfo> PartialTypes => partialTypes;
readonly Dictionary<string, string> additionalProperties = new();
public Dictionary<string, string> AdditionalProperties => additionalProperties;
public DecompilationOptions DecompilationOptions { get; } public DecompilationOptions DecompilationOptions { get; }
public ResourceFileHandlerContext(DecompilationOptions options) public ResourceFileHandlerContext(DecompilationOptions options)

Loading…
Cancel
Save