├── .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 | 
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 |
--------------------------------------------------------------------------------