-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathDawnMemAssert.cs
109 lines (94 loc) · 3.72 KB
/
DawnMemAssert.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// -----------------------------------------------------------------------
// <copyright file="DawnMemAssert.cs" company="PTV AG"></copyright>
//
// DawnMemAssert is a helper-class to check for memory-leaks in
// UI-applications. You can call DawnMemAssert.SetNullAndAssertDead()
// for a reference to an object which should be removable from memory.
// If the assert-box pops up, this indicates a memory-leak, i.e.
// There are still references to this object.
//
// The typical scenario are "lapsed event-listeners", references to a
// long-living object by attaching events to it. In this case the
// receiver of the event will not be freed, as long as the sender lives.
// -----------------------------------------------------------------------
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace Ptvag.Dawn.Tools
{
/// <summary>
/// The mode for the Assert box
/// </summary>
public enum AssertMode
{
/// <summary>
/// Always assert
/// </summary>
Always,
/// <summary>
/// Never assert
/// </summary>
Never,
/// <summary>
/// Assert only if the debugger is attached
/// </summary>
IfDebuggerIsAttached
}
public class DawnMemAssert<T> where T : class
{
public static bool SetNullAndAssertDead(ref T o)
{
// default is if debugger is attached
var mode = AssertMode.IfDebuggerIsAttached;
// try to read the assertion value from the app.config
var configStr = System.Configuration.ConfigurationManager.AppSettings["DawnMemAssert"];
if (string.Equals(configStr, "Always", StringComparison.InvariantCultureIgnoreCase))
mode = AssertMode.Always;
else if (string.Equals(configStr, "Never", StringComparison.InvariantCultureIgnoreCase))
mode = AssertMode.Never;
// check if assertion should be executed
var doAssert =
mode == AssertMode.Always ||
mode == AssertMode.IfDebuggerIsAttached && Debugger.IsAttached;
if (doAssert)
new DawnMemAssert<T>(new WeakReference(o));
// set the object reference to null
o = null;
// returns true if the assertion will be executed
return doAssert;
}
/// <summary>
/// Holds the weak reference to the object
/// </summary>
private readonly WeakReference weakReference;
/// <summary>
/// Creates an instance of the assert object
/// </summary>
/// <param name="wr">The weak reference to the object</param>
private DawnMemAssert(WeakReference wr)
{
// set the weak reference
weakReference = wr;
// attach to the idle-event, so we'll leave the call-stack
Application.Idle += Application_Idle;
}
private void Application_Idle(object sender, EventArgs e)
{
// Invoke the garbage collector
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// At this point the weak reference shouldn't be alive anymore
if (weakReference.IsAlive)
{
var result = MessageBox.Show(weakReference.Target +
" is still alive! Maybe a thread still has a reference to it. Press Retry to test again.",
"DawnMemAssert", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning);
if (result == DialogResult.Retry)
return;
}
// detach from idle event
Application.Idle -= Application_Idle;
}
}
}