diff --git a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
index 0b542b97c..8db01c4d5 100644
--- a/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
+++ b/ICSharpCode.Decompiler.Console/ICSharpCode.Decompiler.Console.csproj
@@ -3,6 +3,7 @@
Exe
netcoreapp2.1
+ true
true
true
ilspycmd
@@ -12,7 +13,7 @@
Copyright 2011-2019 AlphaSierraPapa
https://github.com/icsharpcode/ILSpy/
MIT
- ILSpyCmdNuGetPackageIcon.png
+ ILSpyCmdNuGetPackageIcon.png
https://github.com/icsharpcode/ILSpy/
6.0.0.0
@@ -26,6 +27,12 @@
NU1605
+
+
+
+
+
+
@@ -45,6 +52,7 @@
+
diff --git a/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs b/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
index 02fdcdf23..3821f042f 100644
--- a/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
+++ b/ICSharpCode.Decompiler.Console/IlspyCmdProgram.cs
@@ -12,6 +12,7 @@ using System.Threading;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using ICSharpCode.Decompiler.DebugInfo;
+using ICSharpCode.Decompiler.PdbProvider;
// ReSharper disable All
namespace ICSharpCode.Decompiler.Console
@@ -45,9 +46,13 @@ Remarks:
[Option("-il|--ilcode", "Show IL code.", CommandOptionType.NoValue)]
public bool ShowILCodeFlag { get; }
- [Option("-d|--debuginfo", "Generate PDB.", CommandOptionType.NoValue)]
+ [Option("-genpdb", "Generate PDB.", CommandOptionType.NoValue)]
public bool CreateDebugInfoFlag { get; }
+ [FileExistsOrNull]
+ [Option("-usepdb", "Use PDB.", CommandOptionType.SingleOrNoValue)]
+ public (bool IsSet, string Value) InputPDBFile { get; }
+
[Option("-l|--list ", "Lists all entities of the specified type(s). Valid types: c(lass), i(interface), s(truct), d(elegate), e(num)", CommandOptionType.MultipleValue)]
public string[] EntityTypes { get; } = new string[0];
@@ -142,7 +147,9 @@ Remarks:
foreach (var path in ReferencePaths) {
resolver.AddSearchDirectory(path);
}
- return new CSharpDecompiler(assemblyFileName, resolver, GetSettings());
+ return new CSharpDecompiler(assemblyFileName, resolver, GetSettings()) {
+ DebugInfoProvider = TryLoadPDB(module)
+ };
}
int ListContent(string assemblyFileName, TextWriter output, ISet kinds)
@@ -175,6 +182,7 @@ Remarks:
resolver.AddSearchDirectory(path);
}
decompiler.AssemblyResolver = resolver;
+ decompiler.DebugInfoProvider = TryLoadPDB(module);
decompiler.DecompileProject(module, outputDirectory);
return 0;
}
@@ -211,5 +219,16 @@ Remarks:
return 0;
}
+
+ IDebugInfoProvider TryLoadPDB(PEFile module)
+ {
+ if (InputPDBFile.IsSet) {
+ if (InputPDBFile.Value == null)
+ return DebugInfoUtils.LoadSymbols(module);
+ return DebugInfoUtils.FromFile(module, InputPDBFile.Value);
+ }
+
+ return null;
+ }
}
}
diff --git a/ICSharpCode.Decompiler.Console/ValidationAttributes.cs b/ICSharpCode.Decompiler.Console/ValidationAttributes.cs
index 802382bbf..a593b1674 100644
--- a/ICSharpCode.Decompiler.Console/ValidationAttributes.cs
+++ b/ICSharpCode.Decompiler.Console/ValidationAttributes.cs
@@ -1,19 +1,38 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.IO;
namespace ICSharpCode.Decompiler.Console
{
[AttributeUsage(AttributeTargets.Class)]
- public class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute
+ public sealed class ProjectOptionRequiresOutputDirectoryValidationAttribute : ValidationAttribute
{
+ public ProjectOptionRequiresOutputDirectoryValidationAttribute()
+ {
+ }
+
protected override ValidationResult IsValid(object value, ValidationContext context)
{
if (value is ILSpyCmdProgram obj) {
- if (obj.CreateCompilableProjectFlag && String.IsNullOrEmpty(obj.OutputDirectory)) {
+ if (obj.CreateCompilableProjectFlag && string.IsNullOrEmpty(obj.OutputDirectory)) {
return new ValidationResult("--project cannot be used unless --outputdir is also specified");
}
}
return ValidationResult.Success;
}
}
+
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class FileExistsOrNullAttribute : ValidationAttribute
+ {
+ protected override ValidationResult IsValid(object value, ValidationContext context)
+ {
+ var s = value as string;
+ if (string.IsNullOrEmpty(s))
+ return ValidationResult.Success;
+ if (File.Exists(s))
+ return ValidationResult.Success;
+ return new ValidationResult($"File '{s}' does not exist!");
+ }
+ }
}
diff --git a/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs b/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs
index 0b8f5a383..9f79d0c73 100644
--- a/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs
+++ b/ICSharpCode.Decompiler.PdbProvider.Cecil/MonoCecilDebugInfoProvider.cs
@@ -30,7 +30,7 @@ using Mono.Cecil;
using Mono.Cecil.Pdb;
using SRM = System.Reflection.Metadata;
-namespace ICSharpCode.Decompiler.PdbProvider.Cecil
+namespace ICSharpCode.Decompiler.PdbProvider
{
public class MonoCecilDebugInfoProvider : IDebugInfoProvider
{
diff --git a/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs b/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
index a268c3c42..30616b253 100644
--- a/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
+++ b/ICSharpCode.Decompiler.PowerShell/GetDecompilerCmdlet.cs
@@ -1,8 +1,12 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Management.Automation;
+using System.Reflection.PortableExecutable;
using System.Text;
using ICSharpCode.Decompiler.CSharp;
+using ICSharpCode.Decompiler.Metadata;
+using ICSharpCode.Decompiler.PdbProvider;
namespace ICSharpCode.Decompiler.PowerShell
{
@@ -24,18 +28,25 @@ namespace ICSharpCode.Decompiler.PowerShell
[Parameter(HelpMessage = "Remove dead code")]
public bool RemoveDeadCode { get; set; }
+ [Parameter(HelpMessage = "Use PDB")]
+ public string PDBFilePath { get; set; }
+
protected override void ProcessRecord()
{
string path = GetUnresolvedProviderPathFromPSPath(LiteralPath);
try {
+ var module = new PEFile(LiteralPath, new FileStream(LiteralPath, FileMode.Open, FileAccess.Read), PEStreamOptions.Default);
+ var debugInfo = DebugInfoUtils.FromFile(module, PDBFilePath);
var decompiler = new CSharpDecompiler(path, new DecompilerSettings(LanguageVersion) {
ThrowOnAssemblyResolveErrors = false,
RemoveDeadCode = RemoveDeadCode,
- RemoveDeadStores = RemoveDeadStores
+ RemoveDeadStores = RemoveDeadStores,
+ UseDebugSymbols = debugInfo != null,
+ ShowDebugInfo = debugInfo != null,
});
+ decompiler.DebugInfoProvider = debugInfo;
WriteObject(decompiler);
-
} catch (Exception e) {
WriteVerbose(e.ToString());
WriteError(new ErrorRecord(e, ErrorIds.AssemblyLoadFailed, ErrorCategory.OperationStopped, null));
diff --git a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
index f976d5fd5..d092cc8f9 100644
--- a/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
+++ b/ICSharpCode.Decompiler.PowerShell/ICSharpCode.Decompiler.PowerShell.csproj
@@ -2,6 +2,7 @@
netstandard2.0
+ true
true
ICSharpCode.Decompiler.PowerShell
@@ -9,6 +10,13 @@
+
+
+
+
+
+
+
diff --git a/ILSpy/DebugInfo/DebugInfoUtils.cs b/ILSpy/DebugInfo/DebugInfoUtils.cs
new file mode 100644
index 000000000..f52a790d7
--- /dev/null
+++ b/ILSpy/DebugInfo/DebugInfoUtils.cs
@@ -0,0 +1,113 @@
+// Copyright (c) 2018 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.IO;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Reflection.PortableExecutable;
+using System.Runtime.InteropServices;
+using ICSharpCode.Decompiler.DebugInfo;
+using ICSharpCode.Decompiler.Metadata;
+
+namespace ICSharpCode.Decompiler.PdbProvider
+{
+ static class DebugInfoUtils
+ {
+ public static IDebugInfoProvider LoadSymbols(PEFile module)
+ {
+ try {
+ var reader = module.Reader;
+ // try to open portable pdb file/embedded pdb info:
+ if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) {
+ return new PortableDebugInfoProvider(pdbFileName, provider);
+ } else {
+ // search for pdb in same directory as dll
+ pdbFileName = Path.Combine(Path.GetDirectoryName(module.FileName), Path.GetFileNameWithoutExtension(module.FileName) + ".pdb");
+ if (File.Exists(pdbFileName)) {
+ return new MonoCecilDebugInfoProvider(module, pdbFileName);
+ }
+ }
+ } catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
+ // Ignore PDB load errors
+ }
+ return null;
+ }
+
+ public static IDebugInfoProvider FromFile(PEFile module, string pdbFileName)
+ {
+ if (string.IsNullOrEmpty(pdbFileName))
+ return null;
+
+ Stream stream = OpenStream(pdbFileName);
+ if (stream == null)
+ return null;
+
+ if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
+ && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
+ stream.Position = 0;
+ return new MonoCecilDebugInfoProvider(module, pdbFileName);
+ } else {
+ stream.Position = 0;
+ var provider = MetadataReaderProvider.FromPortablePdbStream(stream);
+ return new PortableDebugInfoProvider(pdbFileName, provider);
+ }
+ }
+
+ const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00";
+ static readonly byte[] buffer = new byte[LegacyPDBPrefix.Length];
+
+ static bool TryOpenPortablePdb(PEFile module, out MetadataReaderProvider provider, out string pdbFileName)
+ {
+ provider = null;
+ pdbFileName = null;
+ var reader = module.Reader;
+ foreach (var entry in reader.ReadDebugDirectory()) {
+ if (entry.IsPortableCodeView) {
+ return reader.TryOpenAssociatedPortablePdb(module.FileName, OpenStream, out provider, out pdbFileName);
+ }
+ if (entry.Type == DebugDirectoryEntryType.CodeView) {
+ string pdbDirectory = Path.GetDirectoryName(module.FileName);
+ pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(module.FileName) + ".pdb");
+ if (File.Exists(pdbFileName)) {
+ Stream stream = OpenStream(pdbFileName);
+ if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
+ && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
+ return false;
+ }
+ stream.Position = 0;
+ provider = MetadataReaderProvider.FromPortablePdbStream(stream);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ static Stream OpenStream(string fileName)
+ {
+ if (!File.Exists(fileName))
+ return null;
+ var memory = new MemoryStream();
+ using (var stream = File.OpenRead(fileName))
+ stream.CopyTo(memory);
+ memory.Position = 0;
+ return memory;
+ }
+ }
+}
diff --git a/ILSpy/DebugInfo/PortableDebugInfoProvider.cs b/ILSpy/DebugInfo/PortableDebugInfoProvider.cs
index c76cc2977..b811ce681 100644
--- a/ILSpy/DebugInfo/PortableDebugInfoProvider.cs
+++ b/ILSpy/DebugInfo/PortableDebugInfoProvider.cs
@@ -20,9 +20,8 @@ using System;
using System.Collections.Generic;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.DebugInfo;
-using ICSharpCode.Decompiler.Metadata;
-namespace ICSharpCode.ILSpy.DebugInfo
+namespace ICSharpCode.Decompiler.PdbProvider
{
class PortableDebugInfoProvider : IDebugInfoProvider
{
diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index a785aad71..163bdc228 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -135,6 +135,7 @@
CreateListDialog.xaml
+
DebugSteps.xaml
diff --git a/ILSpy/LoadedAssembly.cs b/ILSpy/LoadedAssembly.cs
index 78b3e1854..46fe60622 100644
--- a/ILSpy/LoadedAssembly.cs
+++ b/ILSpy/LoadedAssembly.cs
@@ -28,10 +28,9 @@ using System.Threading;
using System.Threading.Tasks;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.Metadata;
-using ICSharpCode.Decompiler.PdbProvider.Cecil;
+using ICSharpCode.Decompiler.PdbProvider;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
-using ICSharpCode.ILSpy.DebugInfo;
using ICSharpCode.ILSpy.Options;
namespace ICSharpCode.ILSpy
@@ -167,7 +166,7 @@ namespace ICSharpCode.ILSpy
if (DecompilerSettingsPanel.CurrentDecompilerSettings.UseDebugSymbols) {
try {
- LoadSymbols(module);
+ debugInfoProvider = DebugInfoUtils.LoadSymbols(module);
} catch (IOException) {
} catch (UnauthorizedAccessException) {
} catch (InvalidOperationException) {
@@ -180,70 +179,6 @@ namespace ICSharpCode.ILSpy
return module;
}
- void LoadSymbols(PEFile module)
- {
- try {
- var reader = module.Reader;
- // try to open portable pdb file/embedded pdb info:
- if (TryOpenPortablePdb(module, out var provider, out var pdbFileName)) {
- debugInfoProvider = new PortableDebugInfoProvider(pdbFileName, provider);
- } else {
- // search for pdb in same directory as dll
- string pdbDirectory = Path.GetDirectoryName(fileName);
- pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb");
- if (File.Exists(pdbFileName)) {
- debugInfoProvider = new MonoCecilDebugInfoProvider(module, pdbFileName);
- return;
- }
-
- // TODO: use symbol cache, get symbols from microsoft
- }
- } catch (Exception ex) when (ex is BadImageFormatException || ex is COMException) {
- // Ignore PDB load errors
- }
- }
-
- const string LegacyPDBPrefix = "Microsoft C/C++ MSF 7.00";
- byte[] buffer = new byte[LegacyPDBPrefix.Length];
-
- bool TryOpenPortablePdb(PEFile module, out MetadataReaderProvider provider, out string pdbFileName)
- {
- provider = null;
- pdbFileName = null;
- var reader = module.Reader;
- foreach (var entry in reader.ReadDebugDirectory()) {
- if (entry.IsPortableCodeView) {
- return reader.TryOpenAssociatedPortablePdb(fileName, OpenStream, out provider, out pdbFileName);
- }
- if (entry.Type == DebugDirectoryEntryType.CodeView) {
- string pdbDirectory = Path.GetDirectoryName(fileName);
- pdbFileName = Path.Combine(pdbDirectory, Path.GetFileNameWithoutExtension(fileName) + ".pdb");
- if (File.Exists(pdbFileName)) {
- Stream stream = OpenStream(pdbFileName);
- if (stream.Read(buffer, 0, buffer.Length) == LegacyPDBPrefix.Length
- && System.Text.Encoding.ASCII.GetString(buffer) == LegacyPDBPrefix) {
- return false;
- }
- stream.Position = 0;
- provider = MetadataReaderProvider.FromPortablePdbStream(stream);
- return true;
- }
- }
- }
- return false;
- }
-
- Stream OpenStream(string fileName)
- {
- if (!File.Exists(fileName))
- return null;
- var memory = new MemoryStream();
- using (var stream = File.OpenRead(fileName))
- stream.CopyTo(memory);
- memory.Position = 0;
- return memory;
- }
-
[ThreadStatic]
static int assemblyLoadDisableCount;