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 @@ -34,10 +34,10 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// </summary>
/// <param name="projectReferences">List of current project's references.</param>
/// <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))
return new ILSpyParameters(new[] { path });
if (projectReferences.TryGetValue(reference.Name, out var refentry))
return new ILSpyParameters(new[] { refentry.AssemblyFile });
return null;
}

65
ILSpy.AddIn/Commands/OpenCodeItemCommand.cs

@ -1,10 +1,12 @@ @@ -1,10 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Text;
namespace ICSharpCode.ILSpy.AddIn.Commands
@ -70,33 +72,80 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -70,33 +72,80 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
var model = await roslynDocument.GetSemanticModelAsync().ConfigureAwait(false);
var node = ast.FindNode(new TextSpan(caretPosition.Position, 0), false, true);
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;
}
var symbol = GetSymbolResolvableByILSpy(model, node);
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;
}
var roslynProject = roslynDocument.Project;
var refsmap = GetReferences(roslynProject);
var symbolAssemblyName = symbol.ContainingAssembly?.Identity?.Name;
// Add our own project as well (not among references)
var project = owner.DTE.Solution.Projects.OfType<EnvDTE.Project>()
.FirstOrDefault(p => p.FileName == roslynProject.FilePath);
if (project != null) {
string projectOutputPath = GetProjectOutputPath(project, roslynProject);
refsmap.Add(roslynDocument.Project.AssemblyName, projectOutputPath);
if (project == null) {
owner.ShowMessage(OLEMSGICON.OLEMSGICON_WARNING, "Can't show ILSpy for this code element!");
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(refs, "/navigateTo:" +
OpenAssembliesInILSpy(new ILSpyParameters(validRefs.Select(r => r.AssemblyFile), "/navigateTo:" +
(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)
{
var current = node;

39
ILSpy.AddIn/Commands/OpenILSpyCommand.cs

@ -23,6 +23,20 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -23,6 +23,20 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
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
{
protected ILSpyAddInPackage owner;
@ -68,26 +82,18 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -68,26 +82,18 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
System.Diagnostics.Process.Start(GetILSpyPath(), commandLineArguments);
}
protected string GetProjectOutputPath(EnvDTE.Project project, Microsoft.CodeAnalysis.Project roslynProject)
{
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)
protected Dictionary<string, DetectedReference> GetReferences(Microsoft.CodeAnalysis.Project parentProject)
{
var dict = new Dictionary<string, string>();
var dict = new Dictionary<string, DetectedReference>();
foreach (var reference in parentProject.MetadataReferences) {
using (var assemblyDef = AssemblyDefinition.ReadAssembly(reference.Display)) {
string assemblyName = assemblyDef.Name.Name;
if (IsReferenceAssembly(assemblyDef)) {
dict.Add(assemblyDef.Name.Name, GacInterop.FindAssemblyInNetGac(assemblyDef.Name));
dict.Add(assemblyName,
new DetectedReference(assemblyName, GacInterop.FindAssemblyInNetGac(assemblyDef.Name), false));
} 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 @@ -95,7 +101,8 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
var roslynProject = owner.Workspace.CurrentSolution.GetProject(projectReference.ProjectId);
var project = owner.DTE.Solution.Projects.OfType<EnvDTE.Project>().FirstOrDefault(p => p.FileName == roslynProject.FilePath);
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;
}

2
ILSpy.AddIn/Commands/OpenReferenceCommand.cs

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

6
ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs

@ -48,12 +48,12 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -48,12 +48,12 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
/// </summary>
/// <param name="projectReferences">List of current project's references.</param>
/// <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;
if (!string.IsNullOrEmpty(fileName)) {
if (projectReferences.TryGetValue(projectItem.Name, out string path)) {
return new ILSpyParameters(new[] { path });
if (projectReferences.TryGetValue(projectItem.Name, out DetectedReference path)) {
return new ILSpyParameters(new[] { path.AssemblyFile });
}
}

20
ILSpy.AddIn/ILSpyAddInPackage.cs

@ -90,6 +90,16 @@ namespace ICSharpCode.ILSpy.AddIn @@ -90,6 +90,16 @@ namespace ICSharpCode.ILSpy.AddIn
#endregion
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));
Guid clsid = Guid.Empty;
@ -98,17 +108,19 @@ namespace ICSharpCode.ILSpy.AddIn @@ -98,17 +108,19 @@ namespace ICSharpCode.ILSpy.AddIn
uiShell.ShowMessageBox(
0,
ref clsid,
"ILSpy.AddIn",
"ILSpy AddIn",
string.Format(CultureInfo.CurrentCulture, format, items),
string.Empty,
0,
OLEMSGBUTTON.OLEMSGBUTTON_OK,
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
OLEMSGICON.OLEMSGICON_INFO,
buttons,
defaultButton,
icon,
0, // false
out result
)
);
return result;
}
public IEnumerable<T> GetSelectedItemsData<T>()

17
ILSpy.AddIn/Utils.cs

@ -14,8 +14,22 @@ using Microsoft.VisualStudio.TextManager.Interop; @@ -14,8 +14,22 @@ using Microsoft.VisualStudio.TextManager.Interop;
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
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass")]
[DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern unsafe char** CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs);
@ -230,7 +244,10 @@ namespace ICSharpCode.ILSpy.AddIn @@ -230,7 +244,10 @@ namespace ICSharpCode.ILSpy.AddIn
string projectOutputPath = project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString();
// 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 null;
}
}
}

Loading…
Cancel
Save