Skip to content

Commit

Permalink
VisualDump 2.0
Browse files Browse the repository at this point in the history
- You no longer need to call `DumpExtensions.EnableDumping()` to activate Dumping! (#4)
- Fixed `IOException: The semaphore timeout period has expired` during some calls to DumpExtensions
- `Visual Dump` initialization is much faster now!
- Fixed Visual Studio 2019 installation exception (#2)
  • Loading branch information
Kir-Antipov committed Jul 13, 2019
1 parent 6e2d64f commit b81783d
Show file tree
Hide file tree
Showing 42 changed files with 1,097 additions and 234 deletions.
151 changes: 110 additions & 41 deletions DumpExtensions/DumpExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Web;
using System.Data;
using System.Linq;
Expand All @@ -7,25 +8,67 @@
using System.Numerics;
using System.Reflection;
using System.Collections;
using System.Diagnostics;
using VisualDump.Helpers;
using System.ComponentModel;
using VisualDump.ExtraTypes;
using VisualDump.HTMLClients;
using VisualDump.HTMLProviders;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Text.RegularExpressions;
using System.Runtime.CompilerServices;
using VisualDump.HTMLProviders.DefaultProviders;

public static class DumpExtensions
{
#region Var
public static bool Enabled { get; private set; }
private static ConcurrentDictionary<Type, HTMLProvider> Providers { get; }
public static bool Enabled
{
get => _enabled;
private set
{
if (_enabled != value)
{
_enabled = value;
CloseClient();
}
}
}
private static bool _enabled = true;

private static HTMLClient Client
{
get
{
if (_client is null && Enabled)
{
if (string.IsNullOrEmpty(ServerName))
ServerName = GetDefaultServerName();
_client = (HTMLClient)Activator.CreateInstance(HTMLClientType, ServerName);
_client.WaitForConnection(MaxWaitTime);
}
return _client;
}
}
private static HTMLClient _client;

private static Assembly EntryAssembly
{
get => _entryAssembly;
set => _entryAssembly = _entryAssembly ?? value;
}
private static Assembly _entryAssembly;

private static HTMLClient Client;
private static string ServerName { get; set; }
private static Type HTMLClientType { get; set; } = typeof(PipeHTMLClient);

private static readonly Regex ProcessRegex = new Regex(@"(.+) - Microsoft Visual Studio", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex ProcessInActionRegex = new Regex(@"(.+) \(.+\) - Microsoft Visual Studio", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private const int MaxWaitTime = 5000;

private static readonly object _sync = new object();

private static ConcurrentDictionary<Type, HTMLProvider> Providers { get; }
#endregion

#region Init
Expand Down Expand Up @@ -63,42 +106,80 @@ static DumpExtensions()
#endif
[typeof(IEnumerable)] = new IEnumerableHTMLProvider()
};
HashSet<string> defaultAssemblies = new HashSet<string> { "mscorlib", "System", "System.Core", "System.Drawing", "System.Windows.Forms" };
foreach (Type p in AppDomain.CurrentDomain.GetAssemblies().Where(x => !defaultAssemblies.Contains(x.GetName().Name)).SelectMany(x => x.GetExportedTypes().Where(y => IsHTMLProvider(y) && y.GetConstructors().Where(z => z.GetParameters().Length == 0).Count() == 1)))
foreach (Type p in AppDomain.CurrentDomain.GetAssemblies()
.Where(x => {
string name = x.GetName().Name;
return !name.StartsWith("System") && !name.StartsWith("Microsoft") && name != "mscorlib" && name != "netstandard";
})
.SelectMany(x => x.GetExportedTypes()
.Where(y => typeof(HTMLProvider).IsAssignableFrom(y) && y.GetConstructor(Type.EmptyTypes) != null)))
Register(p);
}
#endregion

#region Functions
[MethodImpl(MethodImplOptions.NoInlining)]
public static void EnableDumping()
{
EntryAssembly = Assembly.GetCallingAssembly();
Enabled = true;
SetServerName(string.IsNullOrEmpty(ServerName) ? Assembly.GetCallingAssembly().GetName().Name : ServerName);
}
public static void DisableDumping()
public static void DisableDumping() => Enabled = false;

private static string GetDefaultServerName()
{
string asmName = (EntryAssembly ?? Assembly.GetEntryAssembly()).GetName().Name;
Process activeDevenv = Process.GetProcesses().Where(x => x.ProcessName == "devenv").FirstOrDefault(x => x.EnumerateWindows().Select(WindowsInterop.GetWindowText).Where(title => !string.IsNullOrWhiteSpace(title)).Any(title => {
Match m = ProcessInActionRegex.Match(title);
m = m.Success ? m : ProcessRegex.Match(title);
return m.Success && m.Groups[1].Value == asmName;
}));
if (activeDevenv is null)
return asmName;
string searchPattern = $"VisualDump-{activeDevenv.Id}";
return Directory.EnumerateFiles(@"\\.\pipe\")
.Select(Path.GetFileNameWithoutExtension)
.Where(x => x.StartsWith(searchPattern))
.FirstOrDefault() ?? asmName;
}
private static void CloseClient()
{
Enabled = false;
Client?.Dispose();
lock (_sync)
{
if (_client != null)
{
_client.Dispose();
_client = null;
}
}
}

public static T Dump<T>(this T Obj) => Dump(Obj, string.Empty, new object[0]);
public static T Dump<T>(this T Obj, string Header) => Dump(Obj, Header, new object[0]);
public static T Dump<T>(this T Obj, params object[] Args) => Dump(Obj, string.Empty, Args);
public static T Dump<T>(this T Obj, string Header, params object[] Args)
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Dump<T>(this T Obj) => Dump(Obj, string.Empty, new object[0], Assembly.GetCallingAssembly());
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Dump<T>(this T Obj, string Header) => Dump(Obj, Header, new object[0], Assembly.GetCallingAssembly());
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Dump<T>(this T Obj, params object[] Args) => Dump(Obj, string.Empty, Args, Assembly.GetCallingAssembly());
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Dump<T>(this T Obj, string Header, params object[] Args) => Dump(Obj, Header, Args, Assembly.GetCallingAssembly());
private static T Dump<T>(T Obj, string Header, object[] Args, Assembly EntryAssembly)
{
if (Enabled)
{
string html = GetProvider(Obj?.GetType()).ToHTML(Obj, Args);
string html = GetProvider(Obj?.GetType()).ToHTML(Obj, new Stack<object>(), Args);
if (!string.IsNullOrEmpty(Header))
html = WrapWithHeader(html, Header);
SendHTML(WrapWithWrapper(html));
lock (_sync)
{
DumpExtensions.EntryAssembly = EntryAssembly;
Client?.Send(WrapWithWrapper(html));
}
}
return Obj;
}

private static void SendHTML(string HTML) => Client?.Send(HTML);
private static string WrapWithHeader(string HTML, string Header) => $"<div class='header-box'><div class='header'><h3>{HttpUtility.HtmlEncode(Header)}</h3></div>{HTML}</div>";
private static string WrapWithWrapper(string HTML) => $"<div class='wrapper'>{HTML}</div>";
private static string WrapWithHeader(string HTML, string Header) => $"<div class='header-box'><div class='header'><h3>{HttpUtility.HtmlEncode(Header)}</h3></div>{HTML}</div>";

internal static HTMLProvider GetProvider(Type T)
{
Expand Down Expand Up @@ -143,37 +224,25 @@ internal static bool Register(HTMLProvider Provider, Type T)
}
return false;
}
private static bool IsHTMLProvider(Type T)
{
while (T.BaseType != null)
if (T.BaseType == typeof(HTMLProvider))
return true;
else
T = T.BaseType;
return false;
}
private static void InitializeClient()

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public static void SetHTMLClient<TClient>() where TClient : HTMLClient, new()
{
Client?.Dispose();
Client = (HTMLClient)Activator.CreateInstance(HTMLClientType, ServerName);
Client.WaitForConnection(MaxWaitTime);
HTMLClientType = typeof(TClient);
CloseClient();
}

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public static void SetHTMLClient(Type T)
private static void SetHTMLClient(Type T)
{
Type tmp = T.BaseType;
while (!(tmp is null) && tmp != typeof(HTMLClient))
tmp = tmp.BaseType;
HTMLClientType = tmp is null ? throw new NotSupportedException() : T;
InitializeClient();
HTMLClientType = typeof(HTMLClient).IsAssignableFrom(T) && T.GetConstructor(Type.EmptyTypes) != null ? T : throw new NotSupportedException();
CloseClient();
}

[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
public static void SetServerName(string ServerName)
{
DumpExtensions.ServerName = ServerName;
InitializeClient();
DumpExtensions.ServerName = string.IsNullOrEmpty(ServerName) ? throw new ArgumentException(nameof(ServerName)) : ServerName;
CloseClient();
}
#endregion
#endregion
}
15 changes: 9 additions & 6 deletions DumpExtensions/DumpExtensions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
<RootNamespace>VisualDump</RootNamespace>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Kir_Antipov</Authors>
<Description>This library is paired with the VisualDump extension for VisualStudio.
It allows you to view a visual dump of your objects during the debugging of the program in Visual Studio</Description>
<Description>This library is paired with the VisualDump 2.0 extension for VisualStudio.
It allows you to view a visual dump of your objects during the debugging of the program in Visual Studio 2017/2019</Description>
<Copyright>Kir_Antipov © 2019 - DateTime.Now.Year</Copyright>
<PackageTags>Visual Dump VisualDump Dumping</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/Kir-Antipov/VisualDump/master/logo.png</PackageIconUrl>
<PackageTags>Visual Dump VisualDump Dumping LINQPad</PackageTags>
<PackageIconUrl>https://raw.githubusercontent.com/Kir-Antipov/VisualDump/master/logo-64.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/Kir-Antipov/VisualDump</PackageProjectUrl>
<RepositoryUrl>https://github.com/Kir-Antipov/VisualDump</RepositoryUrl>
<Version>1.0.1</Version>
<AssemblyVersion>1.0.1.0</AssemblyVersion>
<Version>2.0.0</Version>
<AssemblyVersion>2.0.0</AssemblyVersion>
<LangVersion>7.3</LangVersion>
<PackageLicenseUrl>GPL-3.0-only</PackageLicenseUrl>
<FileVersion>2.0.0</FileVersion>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using VisualDump.HTMLProviderArgs;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class ArrayHTMLProvider : HTMLProvider
{
public override string ToHTML(object Obj, params object[] Args) => ToHTML<Array>(Obj, arr =>
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<Array>(Obj, CallStack, (arr, s) =>
{
Type arrType = Obj.GetType();
Type elType = arr.GetType().GetElementType();
Expand Down Expand Up @@ -48,9 +49,9 @@ public override string ToHTML(object Obj, params object[] Args) => ToHTML<Array>
DataTableDumpStyle.CountFields |
DataTableDumpStyle.ShowRowIndices |
DataTableDumpStyle.ShowColumnNames;
return GetProvider<DataTable>().ToHTML(table, arrType.Namespace.StartsWith("System") || elType.IsAnonymous() ? new DataTableArgs(style) : new DataTableArgs(style, fullType));
return GetProvider<DataTable>().ToHTML(table, s, arrType.Namespace.StartsWith("System") || elType.IsAnonymous() ? new DataTableArgs(style) : new DataTableArgs(style, fullType));
}
return GetProvider<IEnumerable>().ToHTML(arr, Args);
return GetProvider<IEnumerable>().ToHTML(arr, s, Args);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Reflection;
using VisualDump.Helpers;
using System.Collections.Generic;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class AssemblyHTMLProvider : HTMLProvider
{
#region Functions
public override string ToHTML(object Obj, params object[] Args) => ToHTML<Assembly>(Obj, a => GetProvider<string>().ToHTML(a.FullName));
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<Assembly>(Obj, CallStack, (a, s) => GetProvider<string>().ToHTML(a.FullName, s.CloneAndPush(a)));
#endregion
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
namespace VisualDump.HTMLProviders.DefaultProviders
using System.Collections.Generic;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class BooleanHTMLProvider : HTMLProvider
{
public override string ToHTML(object Obj, params object[] Args) => ToHTML<bool>(Obj, x => $"<div class='keyword'>{(x ? "true" : "false")}</div>");
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<bool>(Obj, CallStack, (x, s) => $"<div class='keyword'>{(x ? "true" : "false")}</div>");
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System.Web;
using System.Collections.Generic;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class CharHTMLProvider : HTMLProvider
{
public override string ToHTML(object Obj, params object[] Args) => ToHTML<char>(Obj, x => $"<div class='string'>'{HttpUtility.HtmlEncode(x)}'</div>");
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<char>(Obj, CallStack, (x, s) => $"<div class='string'>'{HttpUtility.HtmlEncode(x)}'</div>");
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using System;
using System.Text;
using VisualDump.ExtraTypes;
using System.Collections.Generic;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class CyclicalReferenceHTMLProvider : HTMLProvider
{
public override string ToHTML(object Obj, params object[] Args) => ToHTML<CyclicalReference>(Obj, reference =>
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<CyclicalReference>(Obj, CallStack, (reference, stack) =>
{
Type t = reference.CyclicalObject.GetType();
return new StringBuilder()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
using System;
using System.Data;
using System.Text;
using VisualDump.Helpers;
using System.Collections.Generic;
using VisualDump.HTMLProviderArgs;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class DataTableHTMLProvider : HTMLProvider
{
public override string ToHTML(object Obj, params object[] Args) => ToHTML<DataTable, DataTableArgs>(Obj, Args, (table, a) =>
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<DataTable, DataTableArgs>(Obj, CallStack, Args, (table, stack, a) =>
{
Stack<object> newCallStack = stack.CloneAndPush(table);
Type t = table.GetType();
bool showRowIndex = a.Style.HasFlag(DataTableDumpStyle.ShowRowIndices);
bool showColName = a.Style.HasFlag(DataTableDumpStyle.ShowColumnNames);
Expand Down Expand Up @@ -51,7 +54,7 @@ public override string ToHTML(object Obj, params object[] Args) => ToHTML<DataTa
Append("<td class='name'>").Append(i).Append("</td>");
DataRow row = table.Rows[i];
for (int j = 0; j < table.Columns.Count; ++j)
Append("<td class='value'>").Append(GetProvider(row[j]).ToHTML(row[j])).Append("</td>");
Append("<td class='value'>").Append(GetProvider(row[j]).ToHTML(row[j], newCallStack)).Append("</td>");
Append("</tr>");
}
Append("</tbody>")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System.Collections.Generic;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class DateTimeHTMLProvider : HTMLProvider
{
public override string ToHTML(object Obj, params object[] Args) => ToHTML<DateTime>(Obj, x => $"<div class='date'>{x}</div>");
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<DateTime>(Obj, CallStack, (x, s) => $"<div class='date'>{x}</div>");
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

namespace VisualDump.HTMLProviders.DefaultProviders
{
public class EnumHTMLProvider : HTMLProvider
{
public override string ToHTML(object Obj, params object[] Args) => ToHTML<Enum>(Obj, x => $"<div><span class='enum'>{x.GetType().Name}</span><span class='text'>.{x}</span> (<span class='number'>{x.GetType().GetMembers().OfType<FieldInfo>().FirstOrDefault(y => y.Name == "value__")?.GetValue(x) ?? 0}</span>)</div>");
public override string ToHTML(object Obj, Stack<object> CallStack, params object[] Args) => ToHTML<Enum>(Obj, CallStack, (x, s) => $"<div><span class='enum'>{x.GetType().Name}</span><span class='text'>.{x}</span> (<span class='number'>{x.GetType().GetMembers().OfType<FieldInfo>().FirstOrDefault(y => y.Name == "value__")?.GetValue(x) ?? 0}</span>)</div>");
}
}
Loading

0 comments on commit b81783d

Please sign in to comment.