Browse Source

AddIn: Added detection of non-existing assembly files when launching ILSpy, showing a warning message in those cases. In case of project output assemblies, offers building the solution.

pull/1176/head
Andreas Weizel 7 years ago
parent
commit
a94671183e
  1. 6
      ILSpy.AddIn/Commands/AssemblyReferenceForILSpy.cs
  2. 65
      ILSpy.AddIn/Commands/OpenCodeItemCommand.cs
  3. 39
      ILSpy.AddIn/Commands/OpenILSpyCommand.cs
  4. 2
      ILSpy.AddIn/Commands/OpenReferenceCommand.cs
  5. 6
      ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs
  6. 20
      ILSpy.AddIn/ILSpyAddInPackage.cs
  7. 17
      ILSpy.AddIn/Utils.cs

6
ILSpy.AddIn/Commands/AssemblyReferenceForILSpy.cs

@ -34,10 +34,10 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// </summary> /// </summary>
/// <param name="projectReferences">List of current project's references.</param> /// <param name="projectReferences">List of current project's references.</param>
/// <returns>Parameters object or <c>null, if not applicable.</c></returns> /// <returns>Parameters object or <c>null, if not applicable.</c></returns>
public ILSpyParameters GetILSpyParameters(Dictionary<string, string> projectReferences) public ILSpyParameters GetILSpyParameters(Dictionary<string, DetectedReference> projectReferences)
{ {
if (projectReferences.TryGetValue(reference.Name, out var path)) if (projectReferences.TryGetValue(reference.Name, out var refentry))
return new ILSpyParameters(new[] { path }); return new ILSpyParameters(new[] { refentry.AssemblyFile });
return null; return null;
} }

65
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

@ -1,10 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text; using Microsoft.VisualStudio.Text;
namespace ICSharpCode.ILSpy.AddIn.Commands namespace ICSharpCode.ILSpy.AddIn.Commands
@ -70,33 +72,80 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
var model = await roslynDocument.GetSemanticModelAsync().ConfigureAwait(false); var model = await roslynDocument.GetSemanticModelAsync().ConfigureAwait(false);
var node = ast.FindNode(new TextSpan(caretPosition.Position, 0), false, true); var node = ast.FindNode(new TextSpan(caretPosition.Position, 0), false, true);
if (node == null) { if (node == null) {
owner.ShowMessage("Can't show ILSpy for this code element!"); owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING, "Can't show ILSpy for this code element!");
return; return;
} }
var symbol = GetSymbolResolvableByILSpy(model, node); var symbol = GetSymbolResolvableByILSpy(model, node);
if (symbol == null) { if (symbol == null) {
owner.ShowMessage("Can't show ILSpy for this code element!"); owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING, "Can't show ILSpy for this code element!");
return; return;
} }
var roslynProject = roslynDocument.Project; var roslynProject = roslynDocument.Project;
var refsmap = GetReferences(roslynProject); var refsmap = GetReferences(roslynProject);
var symbolAssemblyName = symbol.ContainingAssembly?.Identity?.Name;
// Add our own project as well (not among references) // Add our own project as well (not among references)
var project = owner.DTE.Solution.Projects.OfType<EnvDTE.Project>() var project = owner.DTE.Solution.Projects.OfType<EnvDTE.Project>()
.FirstOrDefault(p => p.FileName == roslynProject.FilePath); .FirstOrDefault(p => p.FileName == roslynProject.FilePath);
if (project != null) { if (project == null) {
string projectOutputPath = GetProjectOutputPath(project, roslynProject); owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING, "Can't show ILSpy for this code element!");
refsmap.Add(roslynDocument.Project.AssemblyName, projectOutputPath); return;
}
string assemblyName = roslynDocument.Project.AssemblyName;
string projectOutputPath = Utils.GetProjectOutputAssembly(project, roslynProject);
refsmap.Add(assemblyName, new DetectedReference(assemblyName, projectOutputPath, true));
// Divide into valid and invalid (= not found) referenced assemblies
CheckAssemblies(refsmap, out var validRefs, out var invalidRefs);
var invalidSymbolReference = invalidRefs.FirstOrDefault(r => r.IsProjectReference && (r.Name == symbolAssemblyName));
if (invalidSymbolReference != null) {
if (string.IsNullOrEmpty(invalidSymbolReference.AssemblyFile)) {
// No assembly file given at all. This has been seen while project is still loading after opening...
owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING,
"Symbol can't be opened. This might happen while project is loading.",
Environment.NewLine, invalidSymbolReference.AssemblyFile);
}
if (invalidSymbolReference.IsProjectReference) {
// Some project references don't have assemblies, maybe not compiled yet?
if (owner.ShowMessage(
OLEMSGBUTTON.OLEMSGBUTTON_YESNO, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, OLEMSGICON.OLEMSGICON_WARNING,
"The project output for '{0}' could not be found for analysis. Would you like to rebuild the solution?",
symbolAssemblyName) == (int)MessageButtonResult.IDYES) {
owner.DTE.ExecuteCommand("Build.BuildSolution");
}
} else {
// External assembly is missing, we should abort
owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING,
"Referenced assembly{0}{0}'{1}'{0}{0} could not be found.",
Environment.NewLine, invalidSymbolReference.AssemblyFile);
}
return;
} }
var refs = refsmap.Select(fn => fn.Value).Where(f => File.Exists(f)); OpenAssembliesInILSpy(new ILSpyParameters(validRefs.Select(r => r.AssemblyFile), "/navigateTo:" +
OpenAssembliesInILSpy(new ILSpyParameters(refs, "/navigateTo:" +
(symbol.OriginalDefinition ?? symbol).GetDocumentationCommentId())); (symbol.OriginalDefinition ?? symbol).GetDocumentationCommentId()));
} }
void CheckAssemblies(Dictionary<string, DetectedReference> inputReferenceList,
out List<DetectedReference> validRefs,
out List<DetectedReference> invalidRefs)
{
validRefs = new List<DetectedReference>();
invalidRefs = new List<DetectedReference>();
foreach (var reference in inputReferenceList.Select(r => r.Value)) {
if ((reference.AssemblyFile == null) || !File.Exists(reference.AssemblyFile)) {
invalidRefs.Add(reference);
} else {
validRefs.Add(reference);
}
}
}
ISymbol GetSymbolResolvableByILSpy(SemanticModel model, SyntaxNode node) ISymbol GetSymbolResolvableByILSpy(SemanticModel model, SyntaxNode node)
{ {
var current = node; var current = node;

39
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

@ -23,6 +23,20 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
public string[] Arguments { get; private set; } public string[] Arguments { get; private set; }
} }
public class DetectedReference
{
public DetectedReference(string name, string assemblyFile, bool isProjectReference)
{
this.Name = name;
this.AssemblyFile = assemblyFile;
this.IsProjectReference = isProjectReference;
}
public string Name { get; private set; }
public string AssemblyFile { get; private set; }
public bool IsProjectReference { get; private set; }
}
abstract class ILSpyCommand abstract class ILSpyCommand
{ {
protected ILSpyAddInPackage owner; protected ILSpyAddInPackage owner;
@ -68,26 +82,18 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
System.Diagnostics.Process.Start(GetILSpyPath(), commandLineArguments); System.Diagnostics.Process.Start(GetILSpyPath(), commandLineArguments);
} }
protected string GetProjectOutputPath(EnvDTE.Project project, Microsoft.CodeAnalysis.Project roslynProject) protected Dictionary<string, DetectedReference> GetReferences(Microsoft.CodeAnalysis.Project parentProject)
{
string outputFileName = Path.GetFileName(roslynProject.OutputFilePath);
//get the directory path based on the project file.
string projectPath = Path.GetDirectoryName(project.FullName);
//get the output path based on the active configuration
string projectOutputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString();
//combine the project path and output path to get the bin path
return Path.Combine(projectPath, projectOutputPath, outputFileName);
}
protected Dictionary<string, string> GetReferences(Microsoft.CodeAnalysis.Project parentProject)
{ {
var dict = new Dictionary<string, string>(); var dict = new Dictionary<string, DetectedReference>();
foreach (var reference in parentProject.MetadataReferences) { foreach (var reference in parentProject.MetadataReferences) {
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) { using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name;
if (IsReferenceAssembly(assemblyDef)) { if (IsReferenceAssembly(assemblyDef)) {
dict.Add(assemblyDef.Name.Name, GacInterop.FindAssemblyInNetGac(assemblyDef.Name)); dict.Add(assemblyName,
new DetectedReference(assemblyName, GacInterop.FindAssemblyInNetGac(assemblyDef.Name), false));
} else { } else {
dict.Add(assemblyDef.Name.Name, reference.Display); dict.Add(assemblyName,
new DetectedReference(assemblyName, reference.Display, false));
} }
} }
} }
@ -95,7 +101,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
var roslynProject = owner.Workspace.CurrentSolution.GetProject(projectReference.ProjectId); var roslynProject = owner.Workspace.CurrentSolution.GetProject(projectReference.ProjectId);
var project = owner.DTE.Solution.Projects.OfType<EnvDTE.Project>().FirstOrDefault(p => p.FileName == roslynProject.FilePath); var project = owner.DTE.Solution.Projects.OfType<EnvDTE.Project>().FirstOrDefault(p => p.FileName == roslynProject.FilePath);
if (roslynProject != null && project != null) if (roslynProject != null && project != null)
dict.Add(roslynProject.AssemblyName, GetProjectOutputPath(project, roslynProject)); dict.Add(roslynProject.AssemblyName,
new DetectedReference(roslynProject.AssemblyName, Utils.GetProjectOutputAssembly(project, roslynProject), true));
} }
return dict; return dict;
} }

2
ILSpy.AddIn/Commands/OpenReferenceCommand.cs

@ -73,7 +73,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
if (!string.IsNullOrEmpty(fileName)) { if (!string.IsNullOrEmpty(fileName)) {
var roslynProject = owner.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == fileName); var roslynProject = owner.Workspace.CurrentSolution.Projects.FirstOrDefault(p => p.FilePath == fileName);
var references = GetReferences(roslynProject); var references = GetReferences(roslynProject);
if (references.TryGetValue(projectItem.Name, out string path)) { if (references.TryGetValue(projectItem.Name, out DetectedReference path)) {
OpenAssembliesInILSpy(projectRefItem.GetILSpyParameters(references)); OpenAssembliesInILSpy(projectRefItem.GetILSpyParameters(references));
return; return;
} }

6
ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs

@ -48,12 +48,12 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// </summary> /// </summary>
/// <param name="projectReferences">List of current project's references.</param> /// <param name="projectReferences">List of current project's references.</param>
/// <returns>Parameters object or <c>null, if not applicable.</c></returns> /// <returns>Parameters object or <c>null, if not applicable.</c></returns>
public ILSpyParameters GetILSpyParameters(Dictionary<string, string> projectReferences) public ILSpyParameters GetILSpyParameters(Dictionary<string, DetectedReference> projectReferences)
{ {
string fileName = projectItem.ContainingProject?.FileName; string fileName = projectItem.ContainingProject?.FileName;
if (!string.IsNullOrEmpty(fileName)) { if (!string.IsNullOrEmpty(fileName)) {
if (projectReferences.TryGetValue(projectItem.Name, out string path)) { if (projectReferences.TryGetValue(projectItem.Name, out DetectedReference path)) {
return new ILSpyParameters(new[] { path }); return new ILSpyParameters(new[] { path.AssemblyFile });
} }
} }

20
ILSpy.AddIn/ILSpyAddInPackage.cs

@ -90,6 +90,16 @@ namespace ICSharpCode.ILSpy.AddIn
#endregion #endregion
public void ShowMessage(string format, params object[] items) public void ShowMessage(string format, params object[] items)
{
ShowMessage(OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, OLEMSGICON.OLEMSGICON_INFO, format, items);
}
public void ShowMessage(OLEMSGICON icon, string format, params object[] items)
{
ShowMessage(OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, icon, format, items);
}
public int ShowMessage(OLEMSGBUTTON buttons, OLEMSGDEFBUTTON defaultButton, OLEMSGICON icon, string format, params object[] items)
{ {
IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell)); IVsUIShell uiShell = (IVsUIShell)GetService(typeof(SVsUIShell));
Guid clsid = Guid.Empty; Guid clsid = Guid.Empty;
@ -98,17 +108,19 @@ namespace ICSharpCode.ILSpy.AddIn
uiShell.ShowMessageBox( uiShell.ShowMessageBox(
0, 0,
ref clsid, ref clsid,
"ILSpy.AddIn", "ILSpy AddIn",
string.Format(CultureInfo.CurrentCulture, format, items), string.Format(CultureInfo.CurrentCulture, format, items),
string.Empty, string.Empty,
0, 0,
OLEMSGBUTTON.OLEMSGBUTTON_OK, buttons,
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, defaultButton,
OLEMSGICON.OLEMSGICON_INFO, icon,
0, // false 0, // false
out result out result
) )
); );
return result;
} }
public IEnumerable<T> GetSelectedItemsData<T>() public IEnumerable<T> GetSelectedItemsData<T>()

17
ILSpy.AddIn/Utils.cs

@ -14,8 +14,22 @@ using Microsoft.VisualStudio.TextManager.Interop;
namespace ICSharpCode.ILSpy.AddIn namespace ICSharpCode.ILSpy.AddIn
{ {
public enum MessageButtonResult : int
{
IDOK = 1,
IDCANCEL = 2,
IDABORT = 3,
IDRETRY = 4,
IDIGNORE = 5,
IDYES = 6,
IDNO = 7,
IDTRYAGAIN = 10,
IDCONTINUE = 11,
}
static class Utils static class Utils
{ {
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)] [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern unsafe char** CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs); static extern unsafe char** CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
@ -230,7 +244,10 @@ namespace ICSharpCode.ILSpy.AddIn
string projectOutputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString(); string projectOutputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString();
// Combine the project path and output path to get the bin path // Combine the project path and output path to get the bin path
if ((projectPath != null) && (projectOutputPath != null) && (outputFileName != null))
return Path.Combine(projectPath, projectOutputPath, outputFileName); return Path.Combine(projectPath, projectOutputPath, outputFileName);
return null;
} }
} }
} }

Loading…
Cancel
Save