Skip to content

Commit

Permalink
Merge pull request mikehadlow#53 from yitzhaks/ColoredDgml
Browse files Browse the repository at this point in the history
Colorful DGML
  • Loading branch information
mikehadlow authored Jul 27, 2018
2 parents 44a8a97 + 471fcfa commit 33d4df6
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 57 deletions.
1 change: 1 addition & 0 deletions AsmSpy.CommandLine/AsmSpy.CommandLine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
Expand Down
17 changes: 6 additions & 11 deletions AsmSpy.CommandLine/BindingRedirectExport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using AsmSpy.Core;
using static System.FormattableString;

namespace AsmSpy.CommandLine
{
Expand Down Expand Up @@ -74,7 +74,6 @@ public static XmlDocument Generate(IDependencyAnalyzerResult result, bool skipSy
}
}


var sortedAssemblies = assemblyInfos.OrderByDescending(a => a.AssemblyName.Version).ToList();
var highestAssemblyVersion = sortedAssemblies.Select(a => a.AssemblyName).First().Version;
var lowestAssemblyVersion = sortedAssemblies.Select(a => a.AssemblyName).Last().Version;
Expand All @@ -90,15 +89,15 @@ public static XmlDocument Generate(IDependencyAnalyzerResult result, bool skipSy
var assemblyIdentity = document.CreateElement("assemblyIdentity");
assemblyIdentity.SetAttribute("name", assemblyToUse.Name);
var publicKeyToken = GetPublicKeyTokenFromAssembly(assemblyToUse);
if (publicKeyToken != "None")
if (publicKeyToken != null)
{
assemblyIdentity.SetAttribute("publicKeyToken", publicKeyToken);
}
var cultureName = assemblyToUse.CultureName;
assemblyIdentity.SetAttribute("culture", string.IsNullOrEmpty(cultureName) ? "neutral" : cultureName);
depedententAssembly.AppendChild(assemblyIdentity);
var bindingRedirect = document.CreateElement("bindingRedirect");
bindingRedirect.SetAttribute("oldVersion", $"{lowestAssemblyVersion}-{highestAssemblyVersion}");
bindingRedirect.SetAttribute("oldVersion", Invariant($"{lowestAssemblyVersion}-{highestAssemblyVersion}"));
bindingRedirect.SetAttribute("newVersion", assemblyToUse.Version.ToString());
depedententAssembly.AppendChild(bindingRedirect);
document.DocumentElement.FirstChild.AppendChild(depedententAssembly);
Expand All @@ -110,14 +109,10 @@ public static XmlDocument Generate(IDependencyAnalyzerResult result, bool skipSy
private static string GetPublicKeyTokenFromAssembly(AssemblyName assembly)
{
var bytes = assembly.GetPublicKeyToken();
if (bytes == null || bytes.Length == 0)
return "None";

var publicKeyToken = string.Empty;
for (var i = 0; i < bytes.GetLength(0); i++)
publicKeyToken += $"{bytes[i]:x2}";
if (!bytes?.Any() ?? true)
return null;

return publicKeyToken;
return string.Join(string.Empty, bytes.Select(@byte => @byte.ToString("x2", CultureInfo.InvariantCulture)));
}
}
}
123 changes: 78 additions & 45 deletions AsmSpy.CommandLine/DgmlExport.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
using System.Collections.Generic;
using System.Globalization;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text;
using AsmSpy.Core;
using static System.FormattableString;

namespace AsmSpy.CommandLine
{
public class DgmlExport : IDependencyVisualizer
{
private static readonly IReadOnlyDictionary<AssemblySource, Color> AssemblySourceColors = new Dictionary<AssemblySource, Color>()
{
{ AssemblySource.NotFound, Color.Red },
{ AssemblySource.Local, Color.Green },
{ AssemblySource.GlobalAssemblyCache, Color.Yellow },
{ AssemblySource.Unknown, Color.Gray },
};

private readonly IDependencyAnalyzerResult _result;
private readonly string _exportFileName;
private readonly ILogger _logger;
Expand All @@ -27,63 +36,87 @@ public DgmlExport(IDependencyAnalyzerResult result, string exportFileName, ILogg

public void Visualize()
{
var nodes = new StringBuilder();
foreach (var assemblyReference in _result.Assemblies.Values)
Stream fileStream = null;
try
{
if (SkipSystem && assemblyReference.IsSystem)
continue;
fileStream = File.OpenWrite(_exportFileName);
using (var dgml = new StreamWriter(fileStream))
{
fileStream = null; // now the StreamWriter owns the stream (fix warning CA2202)

nodes.AppendFormat(CultureInfo.InvariantCulture, "<Node Id=\"{0}\" Label=\"{1}\" Category=\"Assembly\" />\n",
assemblyReference.AssemblyName.FullName, assemblyReference.AssemblyName.Name);
}
dgml.WriteLine(@"<?xml version=""1.0"" encoding=""utf-8""?>");
dgml.WriteLine(@"<DirectedGraph Title=""AsmSpy:References"" xmlns=""http://schemas.microsoft.com/vs/2009/dgml"">");

var links = new StringBuilder();
foreach (var assemblyReference in _result.Assemblies.Values)
{
if (SkipSystem && assemblyReference.IsSystem)
continue;
dgml.WriteLine(@"<Nodes>");
foreach (var assemblyReference in _result.Assemblies.Values)
{
if (SkipSystem && assemblyReference.IsSystem)
continue;

foreach (var referenceTo in assemblyReference.References)
{
if (SkipSystem && referenceTo.IsSystem)
continue;
dgml.WriteLine(Invariant($@"<Node Id=""{assemblyReference.AssemblyName.FullName}"" Label=""{assemblyReference.AssemblyName.Name}"" Category=""Assembly"">"));
dgml.WriteLine(Invariant($@"<Category Ref=""{assemblyReference.AssemblySource}"" />"));
dgml.WriteLine(@"</Node>");
}

links.AppendFormat(CultureInfo.InvariantCulture, "<Link Source=\"{0}\" Target=\"{1}\" Category=\"Reference\" />\n",
assemblyReference.AssemblyName.FullName, referenceTo.AssemblyName.FullName);
}
}
dgml.WriteLine(@"</Nodes>");

var dgml = new StringBuilder();
dgml.Append("<?xml version=\"1.0\" encoding=\"utf - 8\"?>\n");
dgml.Append("<DirectedGraph Title=\"AsmSpy:References\" xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\">\n");
dgml.WriteLine(@"<Links>");

dgml.Append("<Nodes>\n");
dgml.Append(nodes);

dgml.Append("</Nodes>\n");
foreach (var assemblyReference in _result.Assemblies.Values)
{
if (SkipSystem && assemblyReference.IsSystem)
continue;

dgml.Append("<Links>\n");
dgml.Append(links);
dgml.Append("</Links>\n");
foreach (var referenceTo in assemblyReference.References)
{
if (SkipSystem && referenceTo.IsSystem)
continue;

dgml.Append("<Categories>\n");
dgml.Append("<Category Id=\"Assembly\"/>\n");
dgml.Append("<Category Id=\"Reference\"/>\n");
dgml.Append("</Categories>\n");
dgml.WriteLine(Invariant($@"<Link Source=""{assemblyReference.AssemblyName.FullName}"" Target=""{referenceTo.AssemblyName.FullName}"" Category=""Reference"" />"));
}
}

dgml.Append("</DirectedGraph>\n");
try
{
File.WriteAllText(_exportFileName, dgml.ToString());
_logger.LogMessage(string.Format(CultureInfo.InvariantCulture, "Exported to file {0}", _exportFileName));
dgml.WriteLine(@"</Links>");

dgml.WriteLine(@"<Categories>");
dgml.WriteLine(@"<Category Id=""Assembly""/>");
dgml.WriteLine(@"<Category Id=""Reference""/>");

foreach (var kvp in AssemblySourceColors)
{
dgml.WriteLine(Invariant($@"<Category Id=""{kvp.Key}"" Label=""{kvp.Key}"" Background=""{ColorTranslator.ToHtml(kvp.Value)}"" IsTag=""True"" />"));
}

dgml.WriteLine(@"</Categories>");

dgml.WriteLine(@"<Styles>");

foreach (var kvp in AssemblySourceColors)
{
dgml.WriteLine(Invariant($@"<Style TargetType=""Node"" GroupLabel=""AssemblySource: {kvp.Key}"" ValueLabel=""Has category"">"));
dgml.WriteLine(Invariant($@"<Condition Expression=""HasCategory('{kvp.Key}')"" />"));
dgml.WriteLine(Invariant($@"<Setter Property=""Background"" Value=""{ColorTranslator.ToHtml(kvp.Value)}"" />"));
dgml.WriteLine(@"</Style>");
}

dgml.WriteLine(@"</Styles>");

dgml.WriteLine(@"</DirectedGraph>");
}

_logger.LogMessage(Invariant($"Exported to file {_exportFileName}"));
}
catch (System.UnauthorizedAccessException uae)
catch (UnauthorizedAccessException uae)
{
_logger.LogError(string.Format(CultureInfo.InvariantCulture, "Could not write file {0} due to error {1}", _exportFileName, uae.Message));
_logger.LogError(Invariant($"Could not write file {_exportFileName} due to error {uae.Message}"));
}
catch (DirectoryNotFoundException dnfe)
{
_logger.LogError(string.Format(CultureInfo.InvariantCulture, "Could not write file {0} due to error {1}", _exportFileName, dnfe.Message));
_logger.LogError(Invariant($"Could not write file {_exportFileName} due to error {dnfe.Message}"));
}
finally
{
fileStream?.Dispose();
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion AsmSpy.Core/Native/FileInfoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ internal static bool IsAssembly(this FileInfo fileInfo)
}

var data = new byte[PageSize];
using (var fs = File.Open(fileInfo.FullName, FileMode.Open, FileAccess.Read))
using (var fs = File.Open(fileInfo.FullName, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var iRead = fs.Read(data, 0, PageSize);
if (iRead != PageSize)
Expand Down Expand Up @@ -48,6 +48,7 @@ internal static bool IsAssembly(this FileInfo fileInfo)
}
}
}

return true;
}

Expand Down

0 comments on commit 33d4df6

Please sign in to comment.