mirror of https://github.com/icsharpcode/ILSpy.git
27 changed files with 1418 additions and 339 deletions
@ -0,0 +1,121 @@ |
|||||||
|
// Copyright (c) 2020 Daniel Grunwald
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.Decompiler.CSharp.ProjectDecompiler; |
||||||
|
using NUnit.Framework; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Tests |
||||||
|
{ |
||||||
|
[TestFixture] |
||||||
|
public sealed class TargetFrameworkTests |
||||||
|
{ |
||||||
|
[TestCase(-1)] |
||||||
|
[TestCase(0)] |
||||||
|
[TestCase(1)] |
||||||
|
[TestCase(99)] |
||||||
|
[TestCase(int.MinValue)] |
||||||
|
public void VerifyThrowsForInvalidVersion(int invalidVersion) |
||||||
|
{ |
||||||
|
// Arrange - nothing
|
||||||
|
|
||||||
|
// Act
|
||||||
|
void CreateInstance() => new TargetFramework(identifier: null, invalidVersion, profile: null); |
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Throws<ArgumentException>(CreateInstance); |
||||||
|
} |
||||||
|
|
||||||
|
[TestCase(100, "v1.0")] |
||||||
|
[TestCase(102, "v1.0.2")] |
||||||
|
[TestCase(130, "v1.3")] |
||||||
|
[TestCase(145, "v1.4.5")] |
||||||
|
[TestCase(1670, "v16.7")] |
||||||
|
[TestCase(1800, "v18.0")] |
||||||
|
public void VerifyVersion(int version, string expectedVersion) |
||||||
|
{ |
||||||
|
// Arrange - nothing
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var targetFramework = new TargetFramework(identifier: null, version, profile: null); |
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.AreEqual(version, targetFramework.VersionNumber); |
||||||
|
Assert.AreEqual(expectedVersion, targetFramework.VersionString); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
public void VerifyPortableLibrary() |
||||||
|
{ |
||||||
|
// Arrange
|
||||||
|
const string identifier = ".NETPortable"; |
||||||
|
|
||||||
|
// Act
|
||||||
|
var targetFramework = new TargetFramework(identifier, 100, profile: null); |
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.IsTrue(targetFramework.IsPortableClassLibrary); |
||||||
|
Assert.AreEqual(identifier, targetFramework.Identifier); |
||||||
|
} |
||||||
|
|
||||||
|
[Test] |
||||||
|
[Pairwise] |
||||||
|
public void VerifyIdentifierAndProfile( |
||||||
|
[Values(null, "", ".NETFramework")] string identifier, |
||||||
|
[Values(null, "", ".Client")] string profile) |
||||||
|
{ |
||||||
|
// Arrange - nothing
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var targetFramework = new TargetFramework(identifier, 100, profile); |
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.AreEqual(identifier, targetFramework.Identifier); |
||||||
|
Assert.AreEqual(profile, targetFramework.Profile); |
||||||
|
} |
||||||
|
|
||||||
|
[TestCase(null, 350, "net35")] |
||||||
|
[TestCase(".NETFramework", 350, "net35")] |
||||||
|
[TestCase(".NETFramework", 400, "net40")] |
||||||
|
[TestCase(".NETFramework", 451, "net451")] |
||||||
|
[TestCase(".NETCoreApp", 200, "netcoreapp2.0")] |
||||||
|
[TestCase(".NETCoreApp", 310, "netcoreapp3.1")] |
||||||
|
[TestCase(".NETStandard", 130, "netstandard1.3")] |
||||||
|
[TestCase(".NETStandard", 200, "netstandard2.0")] |
||||||
|
[TestCase("Silverlight", 400, "sl4")] |
||||||
|
[TestCase("Silverlight", 550, "sl5")] |
||||||
|
[TestCase(".NETCore", 450, "netcore45")] |
||||||
|
[TestCase(".NETCore", 451, "netcore451")] |
||||||
|
[TestCase("WindowsPhone", 700, "wp7")] |
||||||
|
[TestCase("WindowsPhone", 810, "wp81")] |
||||||
|
[TestCase(".NETMicroFramework", 100, "netmf")] |
||||||
|
[TestCase(".NETMicroFramework", 210, "netmf")] |
||||||
|
[TestCase(".NETPortable", 100, null)] |
||||||
|
[TestCase("Unsupported", 100, null)] |
||||||
|
public void VerifyMoniker(string identifier, int version, string expectedMoniker) |
||||||
|
{ |
||||||
|
// Arrange - nothing
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var targetFramework = new TargetFramework(identifier, version, profile: null); |
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.AreEqual(expectedMoniker, targetFramework.Moniker); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Reflection.Metadata; |
||||||
|
using System.Reflection.PortableExecutable; |
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Tests |
||||||
|
{ |
||||||
|
sealed class TestAssemblyResolver : UniversalAssemblyResolver |
||||||
|
{ |
||||||
|
readonly HashSet<string> localAssemblies = new HashSet<string>(); |
||||||
|
|
||||||
|
public TestAssemblyResolver(string mainAssemblyFileName, string baseDir, string targetFramework) |
||||||
|
: base(mainAssemblyFileName, false, targetFramework, PEStreamOptions.PrefetchMetadata, MetadataReaderOptions.ApplyWindowsRuntimeProjections) |
||||||
|
{ |
||||||
|
var assemblyNames = new DirectoryInfo(baseDir).EnumerateFiles("*.dll").Select(f => Path.GetFileNameWithoutExtension(f.Name)); |
||||||
|
foreach (var name in assemblyNames) { |
||||||
|
localAssemblies.Add(name); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public override bool IsGacAssembly(IAssemblyReference reference) |
||||||
|
{ |
||||||
|
return reference != null && !localAssemblies.Contains(reference.Name); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
// Copyright (c) 2020 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// An interface for a service that creates and writes a project file structure
|
||||||
|
/// for a specific module being decompiled.
|
||||||
|
/// </summary>
|
||||||
|
interface IProjectFileWriter |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Writes the content of a new project file for the specified <paramref name="module"/> being decompiled.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target to write to.</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
|
||||||
|
/// of the project entry type and the file path.</param>
|
||||||
|
/// <param name="module">The module being decompiled.</param>
|
||||||
|
void Write(TextWriter target, IProjectInfoProvider project, IEnumerable<(string itemType, string fileName)> files, PEFile module); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
// Copyright (c) 2020 Daniel Grunwald
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// An interface that provides common information for a project being decompiled to.
|
||||||
|
/// </summary>
|
||||||
|
interface IProjectInfoProvider |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Gets the assembly resolver active for the project.
|
||||||
|
/// </summary>
|
||||||
|
IAssemblyResolver AssemblyResolver { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the C# language version of the project.
|
||||||
|
/// </summary>
|
||||||
|
LanguageVersion LanguageVersion { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the unique ID of the project.
|
||||||
|
/// </summary>
|
||||||
|
Guid ProjectGuid { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the key file being used for strong name signing. Can be null if no file is available.
|
||||||
|
/// </summary>
|
||||||
|
string StrongNameKeyFile { get; } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,175 @@ |
|||||||
|
// Copyright (c) 2020 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Reflection.PortableExecutable; |
||||||
|
using System.Xml; |
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
using ICSharpCode.Decompiler.Solution; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="IProjectFileWriter"/> implementation that creates the projects in the default format.
|
||||||
|
/// </summary>
|
||||||
|
sealed class ProjectFileWriterDefault : IProjectFileWriter |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ProjectFileWriterDefault"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new instance of the <see cref="ProjectFileWriterDefault"/> class.</returns>
|
||||||
|
public static IProjectFileWriter Create() => new ProjectFileWriterDefault(); |
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Write( |
||||||
|
TextWriter target, |
||||||
|
IProjectInfoProvider project, |
||||||
|
IEnumerable<(string itemType, string fileName)> files, |
||||||
|
PEFile module) |
||||||
|
{ |
||||||
|
const string ns = "http://schemas.microsoft.com/developer/msbuild/2003"; |
||||||
|
string platformName = TargetServices.GetPlatformName(module); |
||||||
|
var targetFramework = TargetServices.DetectTargetFramework(module); |
||||||
|
|
||||||
|
List<Guid> typeGuids = new List<Guid>(); |
||||||
|
if (targetFramework.IsPortableClassLibrary) |
||||||
|
typeGuids.Add(ProjectTypeGuids.PortableLibrary); |
||||||
|
typeGuids.Add(ProjectTypeGuids.CSharpWindows); |
||||||
|
|
||||||
|
using (XmlTextWriter w = new XmlTextWriter(target)) { |
||||||
|
w.Formatting = Formatting.Indented; |
||||||
|
w.WriteStartDocument(); |
||||||
|
w.WriteStartElement("Project", ns); |
||||||
|
w.WriteAttributeString("ToolsVersion", "4.0"); |
||||||
|
w.WriteAttributeString("DefaultTargets", "Build"); |
||||||
|
|
||||||
|
w.WriteStartElement("PropertyGroup"); |
||||||
|
w.WriteElementString("ProjectGuid", project.ProjectGuid.ToString("B").ToUpperInvariant()); |
||||||
|
w.WriteElementString("ProjectTypeGuids", string.Join(";", typeGuids.Select(g => g.ToString("B").ToUpperInvariant()))); |
||||||
|
|
||||||
|
w.WriteStartElement("Configuration"); |
||||||
|
w.WriteAttributeString("Condition", " '$(Configuration)' == '' "); |
||||||
|
w.WriteValue("Debug"); |
||||||
|
w.WriteEndElement(); // </Configuration>
|
||||||
|
|
||||||
|
w.WriteStartElement("Platform"); |
||||||
|
w.WriteAttributeString("Condition", " '$(Platform)' == '' "); |
||||||
|
w.WriteValue(platformName); |
||||||
|
w.WriteEndElement(); // </Platform>
|
||||||
|
|
||||||
|
if (module.Reader.PEHeaders.IsDll) { |
||||||
|
w.WriteElementString("OutputType", "Library"); |
||||||
|
} else { |
||||||
|
switch (module.Reader.PEHeaders.PEHeader.Subsystem) { |
||||||
|
case Subsystem.WindowsGui: |
||||||
|
w.WriteElementString("OutputType", "WinExe"); |
||||||
|
break; |
||||||
|
case Subsystem.WindowsCui: |
||||||
|
w.WriteElementString("OutputType", "Exe"); |
||||||
|
break; |
||||||
|
default: |
||||||
|
w.WriteElementString("OutputType", "Library"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
w.WriteElementString("LangVersion", project.LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); |
||||||
|
|
||||||
|
w.WriteElementString("AssemblyName", module.Name); |
||||||
|
if (targetFramework.Identifier != null) |
||||||
|
w.WriteElementString("TargetFrameworkIdentifier", targetFramework.Identifier); |
||||||
|
if (targetFramework.VersionString != null) |
||||||
|
w.WriteElementString("TargetFrameworkVersion", targetFramework.VersionString); |
||||||
|
if (targetFramework.Profile != null) |
||||||
|
w.WriteElementString("TargetFrameworkProfile", targetFramework.Profile); |
||||||
|
w.WriteElementString("WarningLevel", "4"); |
||||||
|
w.WriteElementString("AllowUnsafeBlocks", "True"); |
||||||
|
|
||||||
|
if (project.StrongNameKeyFile != null) { |
||||||
|
w.WriteElementString("SignAssembly", "True"); |
||||||
|
w.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(project.StrongNameKeyFile)); |
||||||
|
} |
||||||
|
|
||||||
|
w.WriteEndElement(); // </PropertyGroup>
|
||||||
|
|
||||||
|
w.WriteStartElement("PropertyGroup"); // platform-specific
|
||||||
|
w.WriteAttributeString("Condition", " '$(Platform)' == '" + platformName + "' "); |
||||||
|
w.WriteElementString("PlatformTarget", platformName); |
||||||
|
if (targetFramework.VersionNumber > 400 && platformName == "AnyCPU" && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) == 0) { |
||||||
|
w.WriteElementString("Prefer32Bit", "false"); |
||||||
|
} |
||||||
|
w.WriteEndElement(); // </PropertyGroup> (platform-specific)
|
||||||
|
|
||||||
|
w.WriteStartElement("PropertyGroup"); // Debug
|
||||||
|
w.WriteAttributeString("Condition", " '$(Configuration)' == 'Debug' "); |
||||||
|
w.WriteElementString("OutputPath", "bin\\Debug\\"); |
||||||
|
w.WriteElementString("DebugSymbols", "true"); |
||||||
|
w.WriteElementString("DebugType", "full"); |
||||||
|
w.WriteElementString("Optimize", "false"); |
||||||
|
w.WriteEndElement(); // </PropertyGroup> (Debug)
|
||||||
|
|
||||||
|
w.WriteStartElement("PropertyGroup"); // Release
|
||||||
|
w.WriteAttributeString("Condition", " '$(Configuration)' == 'Release' "); |
||||||
|
w.WriteElementString("OutputPath", "bin\\Release\\"); |
||||||
|
w.WriteElementString("DebugSymbols", "true"); |
||||||
|
w.WriteElementString("DebugType", "pdbonly"); |
||||||
|
w.WriteElementString("Optimize", "true"); |
||||||
|
w.WriteEndElement(); // </PropertyGroup> (Release)
|
||||||
|
|
||||||
|
|
||||||
|
w.WriteStartElement("ItemGroup"); // References
|
||||||
|
foreach (var r in module.AssemblyReferences) { |
||||||
|
if (r.Name != "mscorlib") { |
||||||
|
w.WriteStartElement("Reference"); |
||||||
|
w.WriteAttributeString("Include", r.Name); |
||||||
|
var asm = project.AssemblyResolver.Resolve(r); |
||||||
|
if (asm != null && !project.AssemblyResolver.IsGacAssembly(r)) { |
||||||
|
w.WriteElementString("HintPath", asm.FileName); |
||||||
|
} |
||||||
|
w.WriteEndElement(); |
||||||
|
} |
||||||
|
} |
||||||
|
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) { |
||||||
|
w.WriteStartElement("ItemGroup"); |
||||||
|
foreach (string file in gr.OrderBy(f => f, StringComparer.OrdinalIgnoreCase)) { |
||||||
|
w.WriteStartElement(gr.Key); |
||||||
|
w.WriteAttributeString("Include", file); |
||||||
|
w.WriteEndElement(); |
||||||
|
} |
||||||
|
w.WriteEndElement(); |
||||||
|
} |
||||||
|
if (targetFramework.IsPortableClassLibrary) { |
||||||
|
w.WriteStartElement("Import"); |
||||||
|
w.WriteAttributeString("Project", "$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"); |
||||||
|
w.WriteEndElement(); |
||||||
|
} else { |
||||||
|
w.WriteStartElement("Import"); |
||||||
|
w.WriteAttributeString("Project", "$(MSBuildToolsPath)\\Microsoft.CSharp.targets"); |
||||||
|
w.WriteEndElement(); |
||||||
|
} |
||||||
|
|
||||||
|
w.WriteEndDocument(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
// Copyright (c) 2020 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.IO; |
||||||
|
using System.Linq; |
||||||
|
using System.Reflection.PortableExecutable; |
||||||
|
using System.Xml; |
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="IProjectFileWriter"/> implementation that creates the projects in the SDK style format.
|
||||||
|
/// </summary>
|
||||||
|
sealed class ProjectFileWriterSdkStyle : IProjectFileWriter |
||||||
|
{ |
||||||
|
const string AspNetCorePrefix = "Microsoft.AspNetCore"; |
||||||
|
const string PresentationFrameworkName = "PresentationFramework"; |
||||||
|
const string WindowsFormsName = "System.Windows.Forms"; |
||||||
|
const string TrueString = "True"; |
||||||
|
const string FalseString = "False"; |
||||||
|
const string AnyCpuString = "AnyCPU"; |
||||||
|
|
||||||
|
static readonly HashSet<string> ImplicitReferences = new HashSet<string> { |
||||||
|
"mscorlib", |
||||||
|
"netstandard", |
||||||
|
"PresentationFramework", |
||||||
|
"System", |
||||||
|
"System.Diagnostics.Debug", |
||||||
|
"System.Diagnostics.Tools", |
||||||
|
"System.Drawing", |
||||||
|
"System.Runtime", |
||||||
|
"System.Runtime.Extensions", |
||||||
|
"System.Windows.Forms", |
||||||
|
"System.Xaml", |
||||||
|
}; |
||||||
|
|
||||||
|
enum ProjectType { Default, WinForms, Wpf, Web } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ProjectFileWriterSdkStyle"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A new instance of the <see cref="ProjectFileWriterSdkStyle"/> class.</returns>
|
||||||
|
public static IProjectFileWriter Create() => new ProjectFileWriterSdkStyle(); |
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Write( |
||||||
|
TextWriter target, |
||||||
|
IProjectInfoProvider project, |
||||||
|
IEnumerable<(string itemType, string fileName)> files, |
||||||
|
PEFile module) |
||||||
|
{ |
||||||
|
using (XmlTextWriter xmlWriter = new XmlTextWriter(target)) { |
||||||
|
xmlWriter.Formatting = Formatting.Indented; |
||||||
|
Write(xmlWriter, project, module); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void Write(XmlTextWriter xml, IProjectInfoProvider project, PEFile module) |
||||||
|
{ |
||||||
|
xml.WriteStartElement("Project"); |
||||||
|
|
||||||
|
var projectType = GetProjectType(module); |
||||||
|
xml.WriteAttributeString("Sdk", GetSdkString(projectType)); |
||||||
|
|
||||||
|
PlaceIntoTag("PropertyGroup", xml, () => WriteAssemblyInfo(xml, module, projectType)); |
||||||
|
PlaceIntoTag("PropertyGroup", xml, () => WriteProjectInfo(xml, project)); |
||||||
|
PlaceIntoTag("ItemGroup", xml, () => WriteReferences(xml, module, project)); |
||||||
|
|
||||||
|
xml.WriteEndElement(); |
||||||
|
} |
||||||
|
|
||||||
|
static void PlaceIntoTag(string tagName, XmlTextWriter xml, Action content) |
||||||
|
{ |
||||||
|
xml.WriteStartElement(tagName); |
||||||
|
try { |
||||||
|
content(); |
||||||
|
} finally { |
||||||
|
xml.WriteEndElement(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void WriteAssemblyInfo(XmlTextWriter xml, PEFile module, ProjectType projectType) |
||||||
|
{ |
||||||
|
xml.WriteElementString("AssemblyName", module.Name); |
||||||
|
|
||||||
|
// Since we create AssemblyInfo.cs manually, we need to disable the auto-generation
|
||||||
|
xml.WriteElementString("GenerateAssemblyInfo", FalseString); |
||||||
|
|
||||||
|
// 'Library' is default, so only need to specify output type for executables
|
||||||
|
if (!module.Reader.PEHeaders.IsDll) { |
||||||
|
WriteOutputType(xml, module.Reader.PEHeaders.PEHeader.Subsystem); |
||||||
|
} |
||||||
|
|
||||||
|
WriteDesktopExtensions(xml, projectType); |
||||||
|
|
||||||
|
string platformName = TargetServices.GetPlatformName(module); |
||||||
|
var targetFramework = TargetServices.DetectTargetFramework(module); |
||||||
|
|
||||||
|
if (targetFramework.Moniker == null) { |
||||||
|
throw new NotSupportedException($"Cannot decompile this assembly to a SDK style project. Use default project format instead."); |
||||||
|
} |
||||||
|
|
||||||
|
xml.WriteElementString("TargetFramework", targetFramework.Moniker); |
||||||
|
|
||||||
|
// 'AnyCPU' is default, so only need to specify platform if it differs
|
||||||
|
if (platformName != AnyCpuString) { |
||||||
|
xml.WriteElementString("PlatformTarget", platformName); |
||||||
|
} |
||||||
|
|
||||||
|
if (platformName == AnyCpuString && (module.Reader.PEHeaders.CorHeader.Flags & CorFlags.Prefers32Bit) != 0) { |
||||||
|
xml.WriteElementString("Prefer32Bit", TrueString); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void WriteOutputType(XmlTextWriter xml, Subsystem moduleSubsystem) |
||||||
|
{ |
||||||
|
switch (moduleSubsystem) { |
||||||
|
case Subsystem.WindowsGui: |
||||||
|
xml.WriteElementString("OutputType", "WinExe"); |
||||||
|
break; |
||||||
|
case Subsystem.WindowsCui: |
||||||
|
xml.WriteElementString("OutputType", "Exe"); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void WriteDesktopExtensions(XmlTextWriter xml, ProjectType projectType) |
||||||
|
{ |
||||||
|
if (projectType == ProjectType.Wpf) { |
||||||
|
xml.WriteElementString("UseWPF", TrueString); |
||||||
|
} else if (projectType == ProjectType.WinForms) { |
||||||
|
xml.WriteElementString("UseWindowsForms", TrueString); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void WriteProjectInfo(XmlTextWriter xml, IProjectInfoProvider project) |
||||||
|
{ |
||||||
|
xml.WriteElementString("LangVersion", project.LanguageVersion.ToString().Replace("CSharp", "").Replace('_', '.')); |
||||||
|
xml.WriteElementString("AllowUnsafeBlocks", TrueString); |
||||||
|
|
||||||
|
if (project.StrongNameKeyFile != null) { |
||||||
|
xml.WriteElementString("SignAssembly", TrueString); |
||||||
|
xml.WriteElementString("AssemblyOriginatorKeyFile", Path.GetFileName(project.StrongNameKeyFile)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void WriteReferences(XmlTextWriter xml, PEFile module, IProjectInfoProvider project) |
||||||
|
{ |
||||||
|
foreach (var reference in module.AssemblyReferences.Where(r => !ImplicitReferences.Contains(r.Name))) { |
||||||
|
xml.WriteStartElement("Reference"); |
||||||
|
xml.WriteAttributeString("Include", reference.Name); |
||||||
|
|
||||||
|
var asembly = project.AssemblyResolver.Resolve(reference); |
||||||
|
if (asembly != null) { |
||||||
|
xml.WriteElementString("HintPath", asembly.FileName); |
||||||
|
} |
||||||
|
|
||||||
|
xml.WriteEndElement(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static string GetSdkString(ProjectType projectType) |
||||||
|
{ |
||||||
|
switch (projectType) { |
||||||
|
case ProjectType.WinForms: |
||||||
|
case ProjectType.Wpf: |
||||||
|
return "Microsoft.NET.Sdk.WindowsDesktop"; |
||||||
|
case ProjectType.Web: |
||||||
|
return "Microsoft.NET.Sdk.Web"; |
||||||
|
default: |
||||||
|
return "Microsoft.NET.Sdk"; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static ProjectType GetProjectType(PEFile module) |
||||||
|
{ |
||||||
|
foreach (var referenceName in module.AssemblyReferences.Select(r => r.Name)) { |
||||||
|
if (referenceName.StartsWith(AspNetCorePrefix, StringComparison.Ordinal)) { |
||||||
|
return ProjectType.Web; |
||||||
|
} |
||||||
|
|
||||||
|
if (referenceName == PresentationFrameworkName) { |
||||||
|
return ProjectType.Wpf; |
||||||
|
} |
||||||
|
|
||||||
|
if (referenceName == WindowsFormsName) { |
||||||
|
return ProjectType.WinForms; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return ProjectType.Default; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,142 @@ |
|||||||
|
// Copyright (c) 2020 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Text; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// A class describing the target framework of a module.
|
||||||
|
/// </summary>
|
||||||
|
sealed class TargetFramework |
||||||
|
{ |
||||||
|
const string DotNetPortableIdentifier = ".NETPortable"; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="TargetFramework"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identifier">The framework identifier string. Can be null.</param>
|
||||||
|
/// <param name="version">The framework version string. Must be greater than 100 (where 100 corresponds to v1.0).</param>
|
||||||
|
/// <param name="profile">The framework profile. Can be null.</param>
|
||||||
|
public TargetFramework(string identifier, int version, string profile) |
||||||
|
{ |
||||||
|
if (version < 100) { |
||||||
|
throw new ArgumentException("The version number must be greater than or equal to 100", nameof(version)); |
||||||
|
} |
||||||
|
|
||||||
|
Identifier = identifier; |
||||||
|
VersionNumber = version; |
||||||
|
VersionString = "v" + GetVersionString(version, withDots: true); |
||||||
|
Moniker = GetTargetFrameworkMoniker(Identifier, version); |
||||||
|
Profile = profile; |
||||||
|
IsPortableClassLibrary = identifier == DotNetPortableIdentifier; |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the target framework identifier. Can be null if not defined.
|
||||||
|
/// </summary>
|
||||||
|
public string Identifier { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the target framework moniker. Can be null if not supported.
|
||||||
|
/// </summary>
|
||||||
|
public string Moniker { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the target framework version, e.g. "v4.5".
|
||||||
|
/// </summary>
|
||||||
|
public string VersionString { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the target framework version as integer (multiplied by 100), e.g. 450.
|
||||||
|
/// </summary>
|
||||||
|
public int VersionNumber { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the target framework profile. Can be null if not set or not available.
|
||||||
|
/// </summary>
|
||||||
|
public string Profile { get; } |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the target is a portable class library (PCL).
|
||||||
|
/// </summary>
|
||||||
|
public bool IsPortableClassLibrary { get; } |
||||||
|
|
||||||
|
static string GetTargetFrameworkMoniker(string frameworkIdentifier, int version) |
||||||
|
{ |
||||||
|
// Reference: https://docs.microsoft.com/en-us/dotnet/standard/frameworks
|
||||||
|
switch (frameworkIdentifier) { |
||||||
|
case null: |
||||||
|
case ".NETFramework": |
||||||
|
return "net" + GetVersionString(version, withDots: false); |
||||||
|
|
||||||
|
case ".NETCoreApp": |
||||||
|
return "netcoreapp" + GetVersionString(version, withDots: true); |
||||||
|
|
||||||
|
case ".NETStandard": |
||||||
|
return "netstandard" + GetVersionString(version, withDots: true); |
||||||
|
|
||||||
|
case "Silverlight": |
||||||
|
return "sl" + version / 100; |
||||||
|
|
||||||
|
case ".NETCore": |
||||||
|
return "netcore" + GetVersionString(version, withDots: false); |
||||||
|
|
||||||
|
case "WindowsPhone": |
||||||
|
return "wp" + GetVersionString(version, withDots: false, omitMinorWhenZero: true); |
||||||
|
|
||||||
|
case ".NETMicroFramework": |
||||||
|
return "netmf"; |
||||||
|
|
||||||
|
default: |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static string GetVersionString(int version, bool withDots, bool omitMinorWhenZero = false) |
||||||
|
{ |
||||||
|
int major = version / 100; |
||||||
|
int minor = version % 100 / 10; |
||||||
|
int patch = version % 10; |
||||||
|
|
||||||
|
if (omitMinorWhenZero && minor == 0 && patch == 0) { |
||||||
|
return major.ToString(); |
||||||
|
} |
||||||
|
|
||||||
|
var versionBuilder = new StringBuilder(8); |
||||||
|
versionBuilder.Append(major); |
||||||
|
|
||||||
|
if (withDots) { |
||||||
|
versionBuilder.Append('.'); |
||||||
|
} |
||||||
|
|
||||||
|
versionBuilder.Append(minor); |
||||||
|
|
||||||
|
if (patch != 0) { |
||||||
|
if (withDots) { |
||||||
|
versionBuilder.Append('.'); |
||||||
|
} |
||||||
|
|
||||||
|
versionBuilder.Append(patch); |
||||||
|
} |
||||||
|
|
||||||
|
return versionBuilder.ToString(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,129 @@ |
|||||||
|
// Copyright (c) 2020 Siegfried Pammer
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||||
|
// software and associated documentation files (the "Software"), to deal in the Software
|
||||||
|
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||||
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||||
|
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in all copies or
|
||||||
|
// substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||||
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||||
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||||
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
// DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
using System; |
||||||
|
using System.Linq; |
||||||
|
using System.Reflection.PortableExecutable; |
||||||
|
using ICSharpCode.Decompiler.Metadata; |
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.CSharp.ProjectDecompiler |
||||||
|
{ |
||||||
|
/// <summary>
|
||||||
|
/// Helper services for determining the target framework and platform of a module.
|
||||||
|
/// </summary>
|
||||||
|
static class TargetServices |
||||||
|
{ |
||||||
|
const string VersionToken = "Version="; |
||||||
|
const string ProfileToken = "Profile="; |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="TargetFramework"/> for the specified <paramref name="module"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module to get the target framework description for. Cannot be null.</param>
|
||||||
|
/// <returns>A new instance of the <see cref="TargetFramework"/> class that describes the specified <paramref name="module"/>.
|
||||||
|
/// </returns>
|
||||||
|
public static TargetFramework DetectTargetFramework(PEFile module) |
||||||
|
{ |
||||||
|
if (module is null) { |
||||||
|
throw new ArgumentNullException(nameof(module)); |
||||||
|
} |
||||||
|
|
||||||
|
int versionNumber; |
||||||
|
switch (module.GetRuntime()) { |
||||||
|
case TargetRuntime.Net_1_0: |
||||||
|
versionNumber = 100; |
||||||
|
break; |
||||||
|
|
||||||
|
case TargetRuntime.Net_1_1: |
||||||
|
versionNumber = 110; |
||||||
|
break; |
||||||
|
|
||||||
|
case TargetRuntime.Net_2_0: |
||||||
|
versionNumber = 200; |
||||||
|
// TODO: Detect when .NET 3.0/3.5 is required
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
versionNumber = 400; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
string targetFrameworkIdentifier = null; |
||||||
|
string targetFrameworkProfile = null; |
||||||
|
|
||||||
|
string targetFramework = module.DetectTargetFrameworkId(); |
||||||
|
if (!string.IsNullOrEmpty(targetFramework)) { |
||||||
|
string[] frameworkParts = targetFramework.Split(','); |
||||||
|
targetFrameworkIdentifier = frameworkParts.FirstOrDefault(a => !a.StartsWith(VersionToken, StringComparison.OrdinalIgnoreCase) && !a.StartsWith(ProfileToken, StringComparison.OrdinalIgnoreCase)); |
||||||
|
string frameworkVersion = frameworkParts.FirstOrDefault(a => a.StartsWith(VersionToken, StringComparison.OrdinalIgnoreCase)); |
||||||
|
|
||||||
|
if (frameworkVersion != null) { |
||||||
|
versionNumber = int.Parse(frameworkVersion.Substring(VersionToken.Length + 1).Replace(".", "")); |
||||||
|
if (versionNumber < 100) versionNumber *= 10; |
||||||
|
} |
||||||
|
|
||||||
|
string frameworkProfile = frameworkParts.FirstOrDefault(a => a.StartsWith(ProfileToken, StringComparison.OrdinalIgnoreCase)); |
||||||
|
if (frameworkProfile != null) |
||||||
|
targetFrameworkProfile = frameworkProfile.Substring(ProfileToken.Length); |
||||||
|
} |
||||||
|
|
||||||
|
return new TargetFramework(targetFrameworkIdentifier, versionNumber, targetFrameworkProfile); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the string representation (name) of the target platform of the specified <paramref name="module"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module">The module to get the target framework description for. Cannot be null.</param>
|
||||||
|
/// <returns>The platform name, e.g. "AnyCPU" or "x86".</returns>
|
||||||
|
public static string GetPlatformName(PEFile module) |
||||||
|
{ |
||||||
|
if (module is null) { |
||||||
|
throw new ArgumentNullException(nameof(module)); |
||||||
|
} |
||||||
|
|
||||||
|
var headers = module.Reader.PEHeaders; |
||||||
|
var architecture = headers.CoffHeader.Machine; |
||||||
|
var characteristics = headers.CoffHeader.Characteristics; |
||||||
|
var corflags = headers.CorHeader.Flags; |
||||||
|
|
||||||
|
switch (architecture) { |
||||||
|
case Machine.I386: |
||||||
|
if ((corflags & CorFlags.Prefers32Bit) != 0) |
||||||
|
return "AnyCPU"; |
||||||
|
|
||||||
|
if ((corflags & CorFlags.Requires32Bit) != 0) |
||||||
|
return "x86"; |
||||||
|
|
||||||
|
// According to ECMA-335, II.25.3.3.1 CorFlags.Requires32Bit and Characteristics.Bit32Machine must be in sync
|
||||||
|
// for assemblies containing managed code. However, this is not true for C++/CLI assemblies.
|
||||||
|
if ((corflags & CorFlags.ILOnly) == 0 && (characteristics & Characteristics.Bit32Machine) != 0) |
||||||
|
return "x86"; |
||||||
|
return "AnyCPU"; |
||||||
|
|
||||||
|
case Machine.Amd64: |
||||||
|
return "x64"; |
||||||
|
|
||||||
|
case Machine.IA64: |
||||||
|
return "Itanium"; |
||||||
|
|
||||||
|
default: |
||||||
|
return architecture.ToString(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue