Browse Source

Merge pull request #2857 from icsharpcode/handle-logical-resource-names

pull/2863/head
Siegfried Pammer 3 years ago committed by GitHub
parent
commit
fb9b665325
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  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. 131
      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();
} }
} }

131
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,12 +229,12 @@ 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)
{ {
var type = metadata.GetTypeDefinition(h); var type = metadata.GetTypeDefinition(h);
string file = CleanUpFileName(metadata.GetString(type.Name)) + ".cs"; string file = SanitizeFileName(metadata.GetString(type.Name) + ".cs");
string ns = metadata.GetString(type.Namespace); string ns = metadata.GetString(type.Namespace);
if (string.IsNullOrEmpty(ns)) if (string.IsNullOrEmpty(ns))
{ {
@ -259,45 +259,43 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
CancellationToken = cancellationToken CancellationToken = cancellationToken
}, },
delegate (IGrouping<string, TypeDefinitionHandle> file) { delegate (IGrouping<string, TypeDefinitionHandle> file) {
using (StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, file.Key))) try
{ {
try using StreamWriter w = new StreamWriter(Path.Combine(TargetDirectory, file.Key));
CSharpDecompiler decompiler = CreateDecompiler(ts);
foreach (var partialType in partialTypes)
{ {
CSharpDecompiler decompiler = CreateDecompiler(ts); decompiler.AddPartialTypeDefinition(partialType);
}
decompiler.CancellationToken = cancellationToken;
var declaredTypes = file.ToArray();
var syntaxTree = decompiler.DecompileTypes(declaredTypes);
foreach (var partialType in partialTypes) foreach (var node in syntaxTree.Descendants)
{
var td = (node.GetResolveResult() as TypeResolveResult)?.Type.GetDefinition();
if (td?.ParentModule != ts.MainModule)
continue;
while (td?.DeclaringTypeDefinition != null)
{ {
decompiler.AddPartialTypeDefinition(partialType); td = td.DeclaringTypeDefinition;
} }
if (td != null && td.MetadataToken is { IsNil: false } token && !processedTypes.Contains((TypeDefinitionHandle)token))
decompiler.CancellationToken = cancellationToken;
var declaredTypes = file.ToArray();
var syntaxTree = decompiler.DecompileTypes(declaredTypes);
foreach (var node in syntaxTree.Descendants)
{ {
var td = (node.GetResolveResult() as TypeResolveResult)?.Type.GetDefinition(); lock (workList)
if (td?.ParentModule != ts.MainModule)
continue;
while (td?.DeclaringTypeDefinition != null)
{
td = td.DeclaringTypeDefinition;
}
if (td != null && td.MetadataToken is { IsNil: false } token && !processedTypes.Contains((TypeDefinitionHandle)token))
{ {
lock (workList) workList.Add((TypeDefinitionHandle)token);
{
workList.Add((TypeDefinitionHandle)token);
}
} }
} }
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
}
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))
{
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException);
} }
syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, Settings.CSharpFormattingOptions));
}
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))
{
throw new DecompilerException(module, $"Error decompiling for '{file.Key}'", innerException);
} }
progress.Status = file.Key; progress.Status = file.Key;
Interlocked.Increment(ref progress.UnitsCompleted); Interlocked.Increment(ref progress.UnitsCompleted);
@ -308,7 +306,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 +316,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 +376,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 +396,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 +411,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)
@ -426,7 +424,7 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
// the directory part Namespace1\Namespace2\... reuses as many existing directories as // the directory part Namespace1\Namespace2\... reuses as many existing directories as
// possible, and only the remaining name parts are used as prefix for the filename. // possible, and only the remaining name parts are used as prefix for the filename.
// This is not affected by the UseNestedDirectoriesForNamespaces setting. // This is not affected by the UseNestedDirectoriesForNamespaces setting.
string[] splitName = fullName.Split(Path.DirectorySeparatorChar); string[] splitName = fullName.Split('\\', '/');
string fileName = string.Join(".", splitName); string fileName = string.Join(".", splitName);
string separator = Path.DirectorySeparatorChar.ToString(); string separator = Path.DirectorySeparatorChar.ToString();
for (int i = splitName.Length - 1; i > 0; i--) for (int i = splitName.Length - 1; i > 0; i--)
@ -444,7 +442,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 +452,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));
} }
} }
@ -618,10 +616,8 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
/// </summary> /// </summary>
static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName) static string CleanUpName(string text, bool separateAtDots, bool treatAsFileName)
{ {
// Remove anything that could be confused with a rooted path.
int pos = text.IndexOf(':'); int pos = text.IndexOf(':');
if (pos > 0)
text = text.Substring(0, pos);
pos = text.IndexOf('`');
if (pos > 0) if (pos > 0)
text = text.Substring(0, pos); text = text.Substring(0, pos);
text = text.Trim(); text = text.Trim();
@ -652,6 +648,12 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
} }
} }
} }
// Remove generics
pos = text.IndexOf('`');
if (pos > 0)
{
text = text.Substring(0, pos).Trim();
}
// Whitelist allowed characters, replace everything else: // Whitelist allowed characters, replace everything else:
StringBuilder b = new StringBuilder(text.Length + (extension?.Length ?? 0)); StringBuilder b = new StringBuilder(text.Length + (extension?.Length ?? 0));
foreach (var c in text) foreach (var c in text)
@ -693,7 +695,15 @@ namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler
b.Append('-'); b.Append('-');
string name = b.ToString(); string name = b.ToString();
if (extension != null) if (extension != null)
{
// make sure that adding the extension to the filename
// does not exceed maxSegmentLength.
// trim the name, if necessary.
if (name.Length + extension.Length > maxSegmentLength)
name = name.Remove(name.Length - extension.Length);
name += extension; name += extension;
}
if (IsReservedFileSystemName(name)) if (IsReservedFileSystemName(name))
return name + "_"; return name + "_";
else if (name == ".") else if (name == ".")
@ -753,4 +763,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