|
1 | 1 | using System; |
| 2 | +using System.IO; |
2 | 3 | using System.Web; |
3 | 4 | using System.Data; |
4 | 5 | using System.Linq; |
|
7 | 8 | using System.Numerics; |
8 | 9 | using System.Reflection; |
9 | 10 | using System.Collections; |
| 11 | +using System.Diagnostics; |
| 12 | +using VisualDump.Helpers; |
10 | 13 | using System.ComponentModel; |
11 | 14 | using VisualDump.ExtraTypes; |
12 | 15 | using VisualDump.HTMLClients; |
13 | 16 | using VisualDump.HTMLProviders; |
14 | 17 | using System.Collections.Generic; |
15 | 18 | using System.Collections.Concurrent; |
| 19 | +using System.Text.RegularExpressions; |
| 20 | +using System.Runtime.CompilerServices; |
16 | 21 | using VisualDump.HTMLProviders.DefaultProviders; |
17 | 22 |
|
18 | 23 | public static class DumpExtensions |
19 | 24 | { |
20 | 25 | #region Var |
21 | | - public static bool Enabled { get; private set; } |
22 | | - private static ConcurrentDictionary<Type, HTMLProvider> Providers { get; } |
| 26 | + public static bool Enabled |
| 27 | + { |
| 28 | + get => _enabled; |
| 29 | + private set |
| 30 | + { |
| 31 | + if (_enabled != value) |
| 32 | + { |
| 33 | + _enabled = value; |
| 34 | + CloseClient(); |
| 35 | + } |
| 36 | + } |
| 37 | + } |
| 38 | + private static bool _enabled = true; |
| 39 | + |
| 40 | + private static HTMLClient Client |
| 41 | + { |
| 42 | + get |
| 43 | + { |
| 44 | + if (_client is null && Enabled) |
| 45 | + { |
| 46 | + if (string.IsNullOrEmpty(ServerName)) |
| 47 | + ServerName = GetDefaultServerName(); |
| 48 | + _client = (HTMLClient)Activator.CreateInstance(HTMLClientType, ServerName); |
| 49 | + _client.WaitForConnection(MaxWaitTime); |
| 50 | + } |
| 51 | + return _client; |
| 52 | + } |
| 53 | + } |
| 54 | + private static HTMLClient _client; |
| 55 | + |
| 56 | + private static Assembly EntryAssembly |
| 57 | + { |
| 58 | + get => _entryAssembly; |
| 59 | + set => _entryAssembly = _entryAssembly ?? value; |
| 60 | + } |
| 61 | + private static Assembly _entryAssembly; |
23 | 62 |
|
24 | | - private static HTMLClient Client; |
25 | 63 | private static string ServerName { get; set; } |
26 | 64 | private static Type HTMLClientType { get; set; } = typeof(PipeHTMLClient); |
27 | | - |
| 65 | + private static readonly Regex ProcessRegex = new Regex(@"(.+) - Microsoft Visual Studio", RegexOptions.IgnoreCase | RegexOptions.Compiled); |
| 66 | + private static readonly Regex ProcessInActionRegex = new Regex(@"(.+) \(.+\) - Microsoft Visual Studio", RegexOptions.IgnoreCase | RegexOptions.Compiled); |
28 | 67 | private const int MaxWaitTime = 5000; |
| 68 | + |
| 69 | + private static readonly object _sync = new object(); |
| 70 | + |
| 71 | + private static ConcurrentDictionary<Type, HTMLProvider> Providers { get; } |
29 | 72 | #endregion |
30 | 73 |
|
31 | 74 | #region Init |
@@ -63,42 +106,80 @@ static DumpExtensions() |
63 | 106 | #endif |
64 | 107 | [typeof(IEnumerable)] = new IEnumerableHTMLProvider() |
65 | 108 | }; |
66 | | - HashSet<string> defaultAssemblies = new HashSet<string> { "mscorlib", "System", "System.Core", "System.Drawing", "System.Windows.Forms" }; |
67 | | - 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))) |
| 109 | + foreach (Type p in AppDomain.CurrentDomain.GetAssemblies() |
| 110 | + .Where(x => { |
| 111 | + string name = x.GetName().Name; |
| 112 | + return !name.StartsWith("System") && !name.StartsWith("Microsoft") && name != "mscorlib" && name != "netstandard"; |
| 113 | + }) |
| 114 | + .SelectMany(x => x.GetExportedTypes() |
| 115 | + .Where(y => typeof(HTMLProvider).IsAssignableFrom(y) && y.GetConstructor(Type.EmptyTypes) != null))) |
68 | 116 | Register(p); |
69 | 117 | } |
70 | 118 | #endregion |
71 | 119 |
|
72 | 120 | #region Functions |
| 121 | + [MethodImpl(MethodImplOptions.NoInlining)] |
73 | 122 | public static void EnableDumping() |
74 | 123 | { |
| 124 | + EntryAssembly = Assembly.GetCallingAssembly(); |
75 | 125 | Enabled = true; |
76 | | - SetServerName(string.IsNullOrEmpty(ServerName) ? Assembly.GetCallingAssembly().GetName().Name : ServerName); |
77 | 126 | } |
78 | | - public static void DisableDumping() |
| 127 | + public static void DisableDumping() => Enabled = false; |
| 128 | + |
| 129 | + private static string GetDefaultServerName() |
| 130 | + { |
| 131 | + string asmName = (EntryAssembly ?? Assembly.GetEntryAssembly()).GetName().Name; |
| 132 | + Process activeDevenv = Process.GetProcesses().Where(x => x.ProcessName == "devenv").FirstOrDefault(x => x.EnumerateWindows().Select(WindowsInterop.GetWindowText).Where(title => !string.IsNullOrWhiteSpace(title)).Any(title => { |
| 133 | + Match m = ProcessInActionRegex.Match(title); |
| 134 | + m = m.Success ? m : ProcessRegex.Match(title); |
| 135 | + return m.Success && m.Groups[1].Value == asmName; |
| 136 | + })); |
| 137 | + if (activeDevenv is null) |
| 138 | + return asmName; |
| 139 | + string searchPattern = $"VisualDump-{activeDevenv.Id}"; |
| 140 | + return Directory.EnumerateFiles(@"\\.\pipe\") |
| 141 | + .Select(Path.GetFileNameWithoutExtension) |
| 142 | + .Where(x => x.StartsWith(searchPattern)) |
| 143 | + .FirstOrDefault() ?? asmName; |
| 144 | + } |
| 145 | + private static void CloseClient() |
79 | 146 | { |
80 | | - Enabled = false; |
81 | | - Client?.Dispose(); |
| 147 | + lock (_sync) |
| 148 | + { |
| 149 | + if (_client != null) |
| 150 | + { |
| 151 | + _client.Dispose(); |
| 152 | + _client = null; |
| 153 | + } |
| 154 | + } |
82 | 155 | } |
83 | 156 |
|
84 | | - public static T Dump<T>(this T Obj) => Dump(Obj, string.Empty, new object[0]); |
85 | | - public static T Dump<T>(this T Obj, string Header) => Dump(Obj, Header, new object[0]); |
86 | | - public static T Dump<T>(this T Obj, params object[] Args) => Dump(Obj, string.Empty, Args); |
87 | | - public static T Dump<T>(this T Obj, string Header, params object[] Args) |
| 157 | + [MethodImpl(MethodImplOptions.NoInlining)] |
| 158 | + public static T Dump<T>(this T Obj) => Dump(Obj, string.Empty, new object[0], Assembly.GetCallingAssembly()); |
| 159 | + [MethodImpl(MethodImplOptions.NoInlining)] |
| 160 | + public static T Dump<T>(this T Obj, string Header) => Dump(Obj, Header, new object[0], Assembly.GetCallingAssembly()); |
| 161 | + [MethodImpl(MethodImplOptions.NoInlining)] |
| 162 | + public static T Dump<T>(this T Obj, params object[] Args) => Dump(Obj, string.Empty, Args, Assembly.GetCallingAssembly()); |
| 163 | + [MethodImpl(MethodImplOptions.NoInlining)] |
| 164 | + public static T Dump<T>(this T Obj, string Header, params object[] Args) => Dump(Obj, Header, Args, Assembly.GetCallingAssembly()); |
| 165 | + private static T Dump<T>(T Obj, string Header, object[] Args, Assembly EntryAssembly) |
88 | 166 | { |
89 | 167 | if (Enabled) |
90 | 168 | { |
91 | | - string html = GetProvider(Obj?.GetType()).ToHTML(Obj, Args); |
| 169 | + string html = GetProvider(Obj?.GetType()).ToHTML(Obj, new Stack<object>(), Args); |
92 | 170 | if (!string.IsNullOrEmpty(Header)) |
93 | 171 | html = WrapWithHeader(html, Header); |
94 | | - SendHTML(WrapWithWrapper(html)); |
| 172 | + lock (_sync) |
| 173 | + { |
| 174 | + DumpExtensions.EntryAssembly = EntryAssembly; |
| 175 | + Client?.Send(WrapWithWrapper(html)); |
| 176 | + } |
95 | 177 | } |
96 | 178 | return Obj; |
97 | 179 | } |
98 | 180 |
|
99 | | - private static void SendHTML(string HTML) => Client?.Send(HTML); |
100 | | - private static string WrapWithHeader(string HTML, string Header) => $"<div class='header-box'><div class='header'><h3>{HttpUtility.HtmlEncode(Header)}</h3></div>{HTML}</div>"; |
101 | 181 | private static string WrapWithWrapper(string HTML) => $"<div class='wrapper'>{HTML}</div>"; |
| 182 | + private static string WrapWithHeader(string HTML, string Header) => $"<div class='header-box'><div class='header'><h3>{HttpUtility.HtmlEncode(Header)}</h3></div>{HTML}</div>"; |
102 | 183 |
|
103 | 184 | internal static HTMLProvider GetProvider(Type T) |
104 | 185 | { |
@@ -143,37 +224,25 @@ internal static bool Register(HTMLProvider Provider, Type T) |
143 | 224 | } |
144 | 225 | return false; |
145 | 226 | } |
146 | | - private static bool IsHTMLProvider(Type T) |
147 | | - { |
148 | | - while (T.BaseType != null) |
149 | | - if (T.BaseType == typeof(HTMLProvider)) |
150 | | - return true; |
151 | | - else |
152 | | - T = T.BaseType; |
153 | | - return false; |
154 | | - } |
155 | | - private static void InitializeClient() |
| 227 | + |
| 228 | + [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] |
| 229 | + public static void SetHTMLClient<TClient>() where TClient : HTMLClient, new() |
156 | 230 | { |
157 | | - Client?.Dispose(); |
158 | | - Client = (HTMLClient)Activator.CreateInstance(HTMLClientType, ServerName); |
159 | | - Client.WaitForConnection(MaxWaitTime); |
| 231 | + HTMLClientType = typeof(TClient); |
| 232 | + CloseClient(); |
160 | 233 | } |
161 | | - |
162 | 234 | [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] |
163 | | - public static void SetHTMLClient(Type T) |
| 235 | + private static void SetHTMLClient(Type T) |
164 | 236 | { |
165 | | - Type tmp = T.BaseType; |
166 | | - while (!(tmp is null) && tmp != typeof(HTMLClient)) |
167 | | - tmp = tmp.BaseType; |
168 | | - HTMLClientType = tmp is null ? throw new NotSupportedException() : T; |
169 | | - InitializeClient(); |
| 237 | + HTMLClientType = typeof(HTMLClient).IsAssignableFrom(T) && T.GetConstructor(Type.EmptyTypes) != null ? T : throw new NotSupportedException(); |
| 238 | + CloseClient(); |
170 | 239 | } |
171 | 240 |
|
172 | 241 | [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] |
173 | 242 | public static void SetServerName(string ServerName) |
174 | 243 | { |
175 | | - DumpExtensions.ServerName = ServerName; |
176 | | - InitializeClient(); |
| 244 | + DumpExtensions.ServerName = string.IsNullOrEmpty(ServerName) ? throw new ArgumentException(nameof(ServerName)) : ServerName; |
| 245 | + CloseClient(); |
177 | 246 | } |
178 | | -#endregion |
| 247 | + #endregion |
179 | 248 | } |
0 commit comments