├── .gitignore ├── README.png ├── sizer-net.ico ├── sizer-net.sln ├── UNLICENSE ├── README.md ├── sizer-net.csproj └── sizer-net.cs /.gitignore: -------------------------------------------------------------------------------- 1 | Debug/ 2 | Release/ 3 | .vs/ 4 | *.suo 5 | *.user 6 | -------------------------------------------------------------------------------- /README.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schellingb/sizer-net/HEAD/README.png -------------------------------------------------------------------------------- /sizer-net.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/schellingb/sizer-net/HEAD/sizer-net.ico -------------------------------------------------------------------------------- /sizer-net.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 2013 3 | VisualStudioVersion = 12.0.0.0 4 | MinimumVisualStudioVersion = 10.0.0.0 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sizer-net", "sizer-net.csproj", "{05D6A76D-F8F5-4E20-AB51-2CA534EA3B50}" 6 | EndProject 7 | Global 8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 9 | Debug|Any CPU = Debug|Any CPU 10 | Release35|Any CPU = Release35|Any CPU 11 | Release45|Any CPU = Release45|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {05D6A76D-F8F5-4E20-AB51-2CA534EA3B50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {05D6A76D-F8F5-4E20-AB51-2CA534EA3B50}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {05D6A76D-F8F5-4E20-AB51-2CA534EA3B50}.Release35|Any CPU.ActiveCfg = Release35|Any CPU 17 | {05D6A76D-F8F5-4E20-AB51-2CA534EA3B50}.Release35|Any CPU.Build.0 = Release35|Any CPU 18 | {05D6A76D-F8F5-4E20-AB51-2CA534EA3B50}.Release45|Any CPU.ActiveCfg = Release45|Any CPU 19 | {05D6A76D-F8F5-4E20-AB51-2CA534EA3B50}.Release45|Any CPU.Build.0 = Release45|Any CPU 20 | EndGlobalSection 21 | GlobalSection(SolutionProperties) = preSolution 22 | HideSolutionNode = FALSE 23 | EndGlobalSection 24 | EndGlobal 25 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sizer.Net 2 | This is a tool that shows the size of things (types, methods, static arrays, etc.) in a .NET assembly. 3 | 4 | ![Screenshot](https://raw.githubusercontent.com/schellingb/sizer-net/master/README.png) 5 | 6 | ## Download 7 | You can find a binary download under the [Releases page](https://github.com/schellingb/sizer-net/releases/latest). 8 | 9 | ## Usage 10 | On launch it shows a file selection dialog with which you can load any kind of .NET assembly (exe or dll). 11 | Once loaded, it shows the things stored in the assembly in a tree view with the accumulated size on the right. 12 | The usual structure is: 13 | [assembly name] → [namespace] → [class] → [thing] 14 | 15 | ## Command line 16 | If launched via command line, it takes a path to an exe or dll file as the first argument. 17 | 18 | ## Accuracy 19 | The tool is not fully accurate as it uses the simple approach of inspection via .NET's built-in reflection. 20 | While the size of actual byte instructions of functions is correct, overhead from types, fields, etc. is estimated. 21 | 22 | A more accurate method would be to use a custom .NET metadata reader library like dnlib that could read byte-accurate information. 23 | 24 | ## Unlicense 25 | Sizer.Net is available under the [Unlicense](http://unlicense.org/). 26 | -------------------------------------------------------------------------------- /sizer-net.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {05D6A76D-F8F5-4E20-AB51-2CA534EA3B50} 8 | WinExe 9 | SizerNet 10 | sizer-net 11 | 512 12 | sizer-net.ico 13 | publish\ 14 | true 15 | Disk 16 | false 17 | Foreground 18 | 7 19 | Days 20 | false 21 | false 22 | true 23 | 0 24 | 1.0.0.%2a 25 | false 26 | false 27 | false 28 | 29 | 30 | 31 | 32 | v4.5 33 | AnyCPU 34 | true 35 | full 36 | false 37 | Debug\ 38 | $(OutputPath) 39 | DEBUG;TRACE 40 | prompt 41 | 4 42 | false 43 | true 44 | false 45 | 46 | 47 | v3.5 48 | v4.5 49 | Release\35\ 50 | Release\45\ 51 | $(OutputPath) 52 | AnyCPU 53 | pdbonly 54 | true 55 | TRACE 56 | prompt 57 | 4 58 | false 59 | 60 | 61 | DOTNET35 62 | DOTNET45 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /sizer-net.cs: -------------------------------------------------------------------------------- 1 | /* 2 | Sizer.Net 3 | Written by Bernhard Schelling 4 | https://github.com/schellingb/sizer-net/ 5 | 6 | This is free and unencumbered software released into the public domain. 7 | 8 | Anyone is free to copy, modify, publish, use, compile, sell, or 9 | distribute this software, either in source code form or as a compiled 10 | binary, for any purpose, commercial or non-commercial, and by any 11 | means. 12 | 13 | In jurisdictions that recognize copyright laws, the author or authors 14 | of this software dedicate any and all copyright interest in the 15 | software to the public domain. We make this dedication for the benefit 16 | of the public at large and to the detriment of our heirs and 17 | successors. We intend this dedication to be an overt act of 18 | relinquishment in perpetuity of all present and future rights to this 19 | software under copyright law. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | OTHER DEALINGS IN THE SOFTWARE. 28 | 29 | For more information, please refer to 30 | */ 31 | 32 | using System; 33 | using System.IO; 34 | using System.Drawing; 35 | using System.Reflection; 36 | using System.Windows.Forms; 37 | 38 | [assembly: AssemblyTitle("Sizer.Net")] 39 | [assembly: AssemblyProduct("sizer-net")] 40 | [assembly: AssemblyVersion("1.1.0.0")] 41 | [assembly: AssemblyFileVersion("1.1.0.0")] 42 | [assembly: System.Runtime.InteropServices.ComVisible(false)] 43 | 44 | static class SizerNet 45 | { 46 | static Form f; 47 | static TreeView tv; 48 | static int TreeViewScrollX = 0; 49 | static string AssemblyPath; 50 | static long AssemblySize; 51 | static string[] DependencyDirs, IgnoredDependencies; 52 | 53 | [STAThread] static void Main(string[] args) 54 | { 55 | Application.EnableVisualStyles(); 56 | Application.SetCompatibleTextRenderingDefault(false); 57 | 58 | f = new Form(); 59 | f.KeyPreview = true; 60 | f.KeyUp += (object sender, KeyEventArgs e) => { if (e.KeyCode == Keys.Escape) ((Form)sender).Close(); }; 61 | f.Text = "Sizer.Net"; 62 | f.Icon = System.Drawing.Icon.ExtractAssociatedIcon(System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName); 63 | f.ClientSize = new Size(800, 700); 64 | 65 | tv = new TreeView(); 66 | tv.Location = new Point(13, 13); 67 | tv.Size = new Size(f.ClientSize.Width - 13 - 13, f.ClientSize.Height - 13 - 23 - 13 - 13); 68 | tv.DrawMode = TreeViewDrawMode.OwnerDrawText; 69 | tv.Anchor = (AnchorStyles)(AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right); 70 | tv.Resize += (object sender, EventArgs e) => { tv.Invalidate(); }; 71 | tv.DrawNode += OnTreeDrawNode; 72 | f.Controls.Add(tv); 73 | 74 | Button btnLoad = new Button(); 75 | btnLoad.Location = new Point(13, f.ClientSize.Height - 13 - 23); 76 | btnLoad.Size = new Size(500 - 13, 23); 77 | btnLoad.Anchor = (AnchorStyles)(AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right); 78 | btnLoad.Text = "Load Assembly"; 79 | btnLoad.Click += (object sender, EventArgs e) => { BrowseAssembly(); }; 80 | f.Controls.Add(btnLoad); 81 | 82 | Button btnClose = new Button(); 83 | btnClose.Location = new Point(513, f.ClientSize.Height - 13 - 23); 84 | btnClose.Size = new Size(f.ClientSize.Width - 513 - 13, 23); 85 | btnClose.Anchor = (AnchorStyles)(AnchorStyles.Bottom | AnchorStyles.Right); 86 | btnClose.Text = "Close"; 87 | btnClose.Click += (object sender, EventArgs e) => { f.Close(); }; 88 | f.Controls.Add(btnClose); 89 | 90 | if (args.Length > 0) 91 | { 92 | if (new FileInfo(args[0]).Exists == false) 93 | { 94 | MessageBox.Show("Assembly not found: " + args[0], "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 95 | return; 96 | } 97 | f.Shown += (object sender, EventArgs e) => { LoadAssembly(args[0], true); }; 98 | } 99 | else f.Shown += (object sender, EventArgs e) => { BrowseAssembly(true); }; 100 | 101 | Application.Run(f); 102 | } 103 | 104 | static void BrowseAssembly(bool InitialLoad = false) 105 | { 106 | var ofd = new OpenFileDialog(); 107 | ofd.ValidateNames = ofd.CheckFileExists = ofd.CheckPathExists = true; 108 | ofd.Filter = ".Net Assemblies (*.exe, *.dll)|*.exe;*.dll"; 109 | if (ofd.ShowDialog() != DialogResult.OK) { if (InitialLoad) f.Close(); return; } 110 | ofd.Dispose(); 111 | LoadAssembly(ofd.FileName, InitialLoad); 112 | } 113 | 114 | static void OnTreeDrawNode(object sender, DrawTreeNodeEventArgs e) 115 | { 116 | e.DrawDefault = true; 117 | if (tv.Nodes.Count == 0 || e.Bounds.Height == 0) return; 118 | float pct = (float)(long)e.Node.Tag / AssemblySize; 119 | int w = tv.ClientSize.Width / 4, x = tv.ClientSize.Width - w - 5, size = (int)(w * pct); 120 | e.Graphics.FillRectangle(Brushes.White, x, e.Bounds.Top + 1, w + 1, e.Bounds.Height - 2); 121 | e.Graphics.FillRectangle(Brushes.LightGray, x, e.Bounds.Top + 1, size + 1, e.Bounds.Height - 2); 122 | e.Graphics.DrawRectangle(Pens.DarkGray, x, e.Bounds.Top + 1, w + 1, e.Bounds.Height - 2); 123 | if (true) //ShowInKilobytes 124 | e.Graphics.DrawString(((float)(long)e.Node.Tag/1024f).ToString("0.##") + " kb", tv.Font, Brushes.DarkSlateGray, x, e.Bounds.Top + 1); 125 | //else (ShowInBytes) 126 | // e.Graphics.DrawString(((long)e.Node.Tag).ToString() + " b", tv.Font, Brushes.DarkSlateGray, x, e.Bounds.Top + 1); 127 | //else (ShowInPercent) 128 | // e.Graphics.DrawString((pct*100).ToString("0.##") + "%", tv.Font, Brushes.DarkSlateGray, x, e.Bounds.Top + 1); 129 | e.Graphics.DrawLine((e.State & TreeNodeStates.Selected) != 0 ? SystemPens.Highlight : SystemPens.ControlLight, e.Bounds.Left + 5, e.Bounds.Top + e.Bounds.Height/2, x - 5, e.Bounds.Top + e.Bounds.Height/2); 130 | if (tv.Nodes[0].Bounds.X != TreeViewScrollX) { TreeViewScrollX = tv.Nodes[0].Bounds.X; tv.Invalidate(); } 131 | } 132 | 133 | //estimated numbers for byte size of overhead introduced by various things 134 | const int Overhead_Type = 4+8*2; 135 | const int Overhead_Field = 2+2*2; 136 | const int Overhead_Method = 8+6*2; 137 | const int Overhead_LocalVariable = 4+1*2; 138 | const int Overhead_Parameter = 4+1*2; 139 | const int Overhead_InterfaceImpl = 0+2*2; 140 | const int Overhead_Event = 2+2*2; 141 | const int Overhead_Property = 2+2*2; 142 | const int Overhead_CustomAttribute = 0+3*2; 143 | 144 | static void LoadAssembly(string InAssemblyPath, bool InitialLoad = false) 145 | { 146 | AssemblyPath = InAssemblyPath; 147 | try 148 | { 149 | tv.Nodes.Clear(); 150 | AppDomain.CurrentDomain.AssemblyResolve -= ResolveExternalAssembly; 151 | AppDomain.CurrentDomain.AssemblyResolve += ResolveExternalAssembly; 152 | AssemblyPath = new FileInfo(AssemblyPath).FullName; 153 | DependencyDirs = new [] { Path.GetDirectoryName(AssemblyPath), Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) }; 154 | IgnoredDependencies = new string[0]; 155 | Assembly assembly = Assembly.LoadFile(AssemblyPath); 156 | bool IsReflectionOnly = false; 157 | AssemblySize = new FileInfo(assembly.Location).Length; 158 | if (AssemblyPath != assembly.Location && !FileContentsMatch(AssemblyPath, assembly.Location)) 159 | { 160 | MessageBox.Show("Requested assembly:\n" + AssemblyPath + "\n\nAssembly loaded by system:\n" + assembly.Location + "\n\nA different assembly was loaded because an assembly with the same name exists in the global assembly cache.\n\nResorting to loading the assembly in 'reflection only' mode which disables dependency resolving which can make certain type evaluations impossible.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 161 | assembly = Assembly.ReflectionOnlyLoadFrom(AssemblyPath); 162 | IsReflectionOnly = true; 163 | } 164 | 165 | BindingFlags all = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance; 166 | BindingFlags statics = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; 167 | 168 | TreeNode nAssembly = new TreeNode(assembly.GetName().Name); 169 | nAssembly.Tag = 0L; 170 | 171 | TreeNode nResources = nAssembly.Nodes.Add("Resources"); 172 | nResources.Tag = 0L; 173 | 174 | //Enumerate Win32 resources 175 | try 176 | { 177 | IntPtr AssemblyHandle = LoadLibraryEx(AssemblyPath, IntPtr.Zero, LoadLibraryFlags.LOAD_LIBRARY_AS_DATAFILE); 178 | if (AssemblyHandle == IntPtr.Zero) throw new Exception(); 179 | EnumResourceTypes(AssemblyHandle, new EnumResTypeProc((IntPtr hModule, IntPtr lpszType, IntPtr lParam) => 180 | { 181 | try { lpszType.ToInt32(); } catch (Exception) { return true; } 182 | EnumResourceNames(hModule, lpszType.ToInt32(), new EnumResNameProc((IntPtr hModule2, IntPtr lpszType2, IntPtr lpzName, IntPtr lParam2) => 183 | { 184 | ResType rt = unchecked((ResType)(long)lpszType2); 185 | IntPtr hResource = FindResource(hModule2, lpzName, lpszType2); 186 | long Size = SizeofResource(hModule2, hResource); 187 | 188 | string name = System.Runtime.InteropServices.Marshal.PtrToStringUni(lpzName); 189 | TreeNode nResource = nResources.Nodes.Add("Resource: " + rt.ToString() + " " + (name == null ? "#" + lpzName.ToInt64() : name)); 190 | SetNodeTag(nResource, Size); 191 | 192 | return true; 193 | }), IntPtr.Zero); 194 | return true; 195 | }), IntPtr.Zero); 196 | FreeLibrary(AssemblyHandle); 197 | } 198 | catch (Exception) { } //ignore Win32 resources 199 | 200 | //Enumerate manifest resources 201 | foreach (string mr in assembly.GetManifestResourceNames()) 202 | { 203 | ResourceLocation rl = assembly.GetManifestResourceInfo(mr).ResourceLocation; 204 | if ((rl & ResourceLocation.Embedded) == 0 || (rl & ResourceLocation.ContainedInAnotherAssembly) != 0) continue; 205 | TreeNode nResource = nResources.Nodes.Add("Manifest Resource: " + mr); 206 | Stream mrs = assembly.GetManifestResourceStream(mr); 207 | SetNodeTag(nResource, mrs.Length); 208 | mrs.Dispose(); 209 | } 210 | 211 | foreach (Module module in assembly.GetModules()) 212 | { 213 | foreach (MethodInfo mi in module.GetMethods(all)) AddMethodNode(nAssembly, mi); 214 | 215 | int lenModuleFields = 0; 216 | foreach (FieldInfo fi in module.GetFields(all)) lenModuleFields += Overhead_Field + fi.Name.Length; 217 | if (lenModuleFields != 0) { TreeNode nModuleInfo = nAssembly.Nodes.Add(module.GetFields(all).Length.ToString() + " Fields in " + module.Name + " (Overhead)"); SetNodeTag(nModuleInfo, lenModuleFields); } 218 | } 219 | 220 | Type[] AssemblyTypes; 221 | try { AssemblyTypes = assembly.GetTypes(); } 222 | catch (ReflectionTypeLoadException e) { AssemblyTypes = e.Types; } 223 | 224 | int UnresolvedTypes = 0; 225 | foreach (Type type in AssemblyTypes) 226 | { 227 | if (type == null) { UnresolvedTypes++; continue; } 228 | TreeNode nType = nAssembly; 229 | bool IsStaticArrayInitType = type.Name.Contains("StaticArrayInitTypeSize="); 230 | foreach (string NSPart in type.FullName.Split('.', '+')) 231 | { 232 | if (nType.Nodes.ContainsKey(NSPart)) nType = nType.Nodes[NSPart]; 233 | else (nType = nType.Nodes.Add(NSPart, NSPart)).Tag = 0L; 234 | } 235 | 236 | int lenType = Overhead_Type + type.FullName.Length; 237 | try { foreach (Type it in type.GetInterfaces()) lenType += Overhead_InterfaceImpl; } catch { } 238 | #if DOTNET35 239 | try { foreach (object ca in type.GetCustomAttributes(false)) lenType += Overhead_CustomAttribute; } catch { } 240 | #else 241 | try { foreach (CustomAttributeData ad in type.GetCustomAttributesData()) lenType += Overhead_CustomAttribute; } catch { } 242 | #endif 243 | SetNodeTag(nType, lenType); 244 | 245 | foreach (FieldInfo fi in type.GetFields(statics)) 246 | { 247 | try 248 | { 249 | if (fi.FieldType.ContainsGenericParameters || fi.FieldType.IsGenericType) continue; 250 | long fiSize = CalculateSize(IsReflectionOnly, fi.FieldType, fi); 251 | if (fiSize > 0) SetNodeTag(nType.Nodes.Add("Static Field: " + fi.Name), fiSize); 252 | } 253 | catch (Exception) { } 254 | } 255 | 256 | int numTypeFields = 0, numTypeProperties = 0, numTypeEvents = 0, lenTypeFields = 0, lenTypeProperties = 0, lenTypeEvents = 0; 257 | foreach (FieldInfo fi in type.GetFields(all)) { numTypeFields++; lenTypeFields += Overhead_Field + fi.Name.Length; } 258 | foreach (PropertyInfo pi in type.GetProperties(all)) { numTypeProperties++; lenTypeProperties += Overhead_Property + (pi.Name == null ? 0 : pi.Name.Length); } 259 | foreach (EventInfo ei in type.GetEvents(all)) { numTypeEvents++; lenTypeEvents += Overhead_Event + ei.Name.Length; } 260 | if (lenTypeFields != 0) SetNodeTag(nType.Nodes.Add(numTypeFields.ToString() + " Fields (Overhead)"), lenTypeFields); 261 | if (lenTypeProperties != 0) SetNodeTag(nType.Nodes.Add(numTypeProperties.ToString() + " Properties (Overhead)"), lenTypeProperties); 262 | if (lenTypeEvents != 0) SetNodeTag(nType.Nodes.Add(numTypeEvents.ToString() + " Events (Overhead)"), lenTypeEvents); 263 | 264 | foreach (ConstructorInfo ci in type.GetConstructors(all)) AddMethodNode(nType, ci); 265 | foreach (MethodInfo mi in type.GetMethods(all)) AddMethodNode(nType, mi); 266 | } 267 | 268 | SetNodeTag(nAssembly.Nodes.Add("Other Overhead"), AssemblySize - (long)nAssembly.Tag); 269 | SortByNodeByTag(nAssembly.Nodes); 270 | //FilterNodeByTag(nAssembly.Nodes, AssemblySize/100); 271 | nAssembly.Expand(); 272 | tv.Nodes.Add(nAssembly); 273 | 274 | if (UnresolvedTypes != 0) 275 | { 276 | MessageBox.Show(UnresolvedTypes.ToString() + " types could not be evaluated due to missing dependency errors.\nThese are included in the 'Other Overhead' entry.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); 277 | } 278 | } 279 | catch (Exception e) 280 | { 281 | MessageBox.Show("Assembly loading error:\n\n" + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 282 | if (InitialLoad) f.Close(); 283 | } 284 | } 285 | 286 | static void AddMethodNode(TreeNode ParentNode, MethodBase mi) 287 | { 288 | TreeNode nMethod = ParentNode.Nodes.Add(mi.Name); 289 | int lenMi = Overhead_Method + mi.Name.Length; 290 | try { var mb = mi.GetMethodBody(); lenMi += mb.GetILAsByteArray().Length; foreach (LocalVariableInfo lvi in mb.LocalVariables) lenMi += Overhead_LocalVariable; } catch { } 291 | try { foreach (ParameterInfo pi in mi.GetParameters()) lenMi += 16 + (pi.Name == null ? 0 : pi.Name.Length); } catch { } 292 | #if DOTNET35 293 | try { foreach (object ca in mi.GetCustomAttributes(false)) lenMi += Overhead_CustomAttribute; } catch { } 294 | #else 295 | try { foreach (CustomAttributeData ad in mi.GetCustomAttributesData()) lenMi += Overhead_CustomAttribute; } catch { } 296 | #endif 297 | SetNodeTag(nMethod, lenMi); 298 | } 299 | 300 | static Assembly ResolveExternalAssembly(object sender, ResolveEventArgs args) 301 | { 302 | if (System.Array.IndexOf(IgnoredDependencies, args.Name) >= 0) return null; 303 | 304 | string DllFileName = new AssemblyName(args.Name).Name + ".dll"; 305 | foreach (string dir in DependencyDirs) 306 | { 307 | string TestAssemblyPath = Path.Combine(dir, DllFileName); 308 | if (File.Exists(TestAssemblyPath)) return Assembly.LoadFile(TestAssemblyPath); 309 | } 310 | 311 | var ofd = new OpenFileDialog(); 312 | ofd.Title = "Find dependency: " + args.Name; 313 | ofd.ValidateNames = ofd.CheckFileExists = ofd.CheckPathExists = true; 314 | ofd.FileName = DllFileName; 315 | ofd.Filter = ".Net Dependencies (*.dll)|*.dll"; 316 | if (ofd.ShowDialog() != DialogResult.OK) 317 | { 318 | System.Array.Resize(ref IgnoredDependencies, IgnoredDependencies.Length + 1); 319 | IgnoredDependencies[IgnoredDependencies.Length - 1] = args.Name; 320 | return null; 321 | } 322 | System.Array.Resize(ref DependencyDirs, DependencyDirs.Length + 1); 323 | DependencyDirs[DependencyDirs.Length - 1] = Path.GetDirectoryName(ofd.FileName); 324 | return Assembly.LoadFile(ofd.FileName); 325 | } 326 | 327 | static void SetNodeTag(TreeNode n, long amount) 328 | { 329 | n.Tag = amount; 330 | for (n = n.Parent; n != null; n = n.Parent) n.Tag = ((long)n.Tag) + amount; 331 | } 332 | 333 | static void SortByNodeByTag(TreeNodeCollection nc) 334 | { 335 | foreach (TreeNode n in nc) SortByNodeByTag(n.Nodes); 336 | TreeNode[] ns = new TreeNode[nc.Count]; 337 | nc.CopyTo(ns, 0); 338 | Array.Sort(ns, (TreeNode a, TreeNode b) => { return unchecked((int)((long)b.Tag - (long)a.Tag)); }); 339 | nc.Clear(); 340 | nc.AddRange(ns); 341 | } 342 | 343 | static void FilterNodeByTag(TreeNodeCollection nc, long Threshold) 344 | { 345 | long TotalRemoved = 0; 346 | int LastRemoved = -1; 347 | for (int i = 0; i != nc.Count; i++) 348 | { 349 | if ((long)nc[i].Tag < Threshold) { TotalRemoved += (long)nc[i].Tag; nc[i].Remove(); LastRemoved = i--; continue; } 350 | FilterNodeByTag(nc[i].Nodes, Threshold); 351 | } 352 | if (TotalRemoved != 0) { nc.Add("... ...").Tag = TotalRemoved; } 353 | } 354 | 355 | static long CalculateSize(bool IsReflectionOnly, Type t, object FiOrValue = null) 356 | { 357 | if (t.IsArray) 358 | { 359 | System.Array a = (System.Array)(FiOrValue is FieldInfo ? (IsReflectionOnly ? ((FieldInfo)FiOrValue).GetRawConstantValue() : ((FieldInfo)FiOrValue).GetValue(null)) : FiOrValue); 360 | if (a == null || a.LongLength == 0) return 0; 361 | t = t.GetElementType(); 362 | if (t.IsEnum) t = Enum.GetUnderlyingType(t); 363 | if (!t.ContainsGenericParameters && !t.IsGenericType && (t.IsValueType || t.IsPointer || t.IsLayoutSequential)) return a.LongLength * System.Runtime.InteropServices.Marshal.SizeOf(t); 364 | if (!t.IsArray && t != typeof(string)) return 0; //can't measure size 365 | long res = 0; 366 | foreach (object v in a) res += CalculateSize(IsReflectionOnly, t, v); 367 | return res; 368 | } 369 | if (t == typeof(string)) 370 | { 371 | string s = (string)(FiOrValue is FieldInfo ? (IsReflectionOnly ? ((FieldInfo)FiOrValue).GetRawConstantValue() : ((FieldInfo)FiOrValue).GetValue(null)) : FiOrValue); 372 | return (s == null ? 0 : s.Length * 2); 373 | } 374 | if (t.IsEnum) t = Enum.GetUnderlyingType(t); 375 | return (!t.ContainsGenericParameters && !t.IsGenericType && (t.IsValueType || t.IsPointer || t.IsLayoutSequential) ? System.Runtime.InteropServices.Marshal.SizeOf(t) : 0); 376 | } 377 | 378 | static bool FileContentsMatch(string path1, string path2) 379 | { 380 | FileInfo fi1 = new FileInfo(path1), fi2 = new FileInfo(path2); 381 | if (!fi1.Exists || !fi2.Exists || fi1.Length != fi2.Length) return false; 382 | FileStream stream1 = fi1.Open(FileMode.Open, FileAccess.Read, FileShare.Read), stream2 = fi2.Open(FileMode.Open, FileAccess.Read, FileShare.Read); 383 | for (byte[] buf1 = new byte[4096], buf2 = new byte[4096];;) 384 | { 385 | int count = stream1.Read(buf1, 0, 4096); stream2.Read(buf2, 0, 4096); 386 | if (count == 0) return true; 387 | for (int i = 0; i < count; i += sizeof(Int64)) 388 | if (BitConverter.ToInt64(buf1, i) != BitConverter.ToInt64(buf2, i)) 389 | return false; 390 | } 391 | } 392 | 393 | //PInvoke definitions for Win32 resource enumeration 394 | enum LoadLibraryFlags : uint { LOAD_LIBRARY_AS_DATAFILE = 2 }; 395 | enum ResType { Cursor = 1, Bitmap, Icon, Menu, Dialog, String, FontDir, Font, Accelerator, RCData, MessageTable, CursorGroup, IconGroup = 14, VersionInfo = 16, DLGInclude, PlugPlay = 19, VXD, AnimatedCursor, AnimatedIcon, HTML, Manifest }; 396 | delegate bool EnumResTypeProc(IntPtr hModule, IntPtr lpszType, IntPtr lParam); 397 | delegate bool EnumResNameProc(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam); 398 | [System.Runtime.InteropServices.DllImport("kernel32.dll")] static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hReservedNull, LoadLibraryFlags dwFlags); 399 | [System.Runtime.InteropServices.DllImport("kernel32.dll")] static extern bool EnumResourceTypes(IntPtr hModule, EnumResTypeProc lpEnumFunc, IntPtr lParam); 400 | [System.Runtime.InteropServices.DllImport("kernel32.dll")] static extern bool EnumResourceNames(IntPtr hModule, int dwID, EnumResNameProc lpEnumFunc, IntPtr lParam); 401 | [System.Runtime.InteropServices.DllImport("Kernel32.dll")] static extern IntPtr FindResource(IntPtr hModule, IntPtr lpszName, IntPtr lpszType); 402 | [System.Runtime.InteropServices.DllImport("Kernel32.dll")] static extern uint SizeofResource(IntPtr hModule, IntPtr hResource); 403 | [System.Runtime.InteropServices.DllImport("kernel32.dll")] static extern bool FreeLibrary(IntPtr hModule); 404 | } 405 | --------------------------------------------------------------------------------