ApplicationAssert.cs
9.9 KB
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
namespace SysFramework
{
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
/// <summary>
/// A class to help with error checking and automatic logging
/// of asserts and conditional checks.
/// <remarks>
/// This class works with displays system assert dialogs
/// as well as writing to the application log with the
/// ApplicationLog class. There is no instance data associated
/// with this class.
/// </remarks>
/// </summary>
public class ApplicationAssert
{
#if !DEBUG
/// <value>
/// A LineNumber constant to be used when not in a debug build
/// so that ApplicationAssert. LineNumber is always a valid expression.
/// <remarks>
/// This allows us to pass ApplicationAssert.LineNumber with good debug
/// functionality and minimal runtime overhead.
/// </remarks>
/// </value>
public const int LineNumber = 0;
#else
/// <value>Property LineNumber is used to get the current line number in the calling function.</value>
/// <remarks>
/// This should be called in a parameter list to get accurate
/// information about the line number before the Check* functions
/// are called. If we wait until the Check* functions themselves
/// to retrieve this information, then the stack trace indicates
/// the next executable line, which is only marginally useful
/// information. This function is compiled out in debug builds in
/// favor of the LineNumber constant.
/// Returns LineNumber, or 0 on failure.
/// </remarks>
public static int LineNumber
{
get
{
try
{
//
// Get the trace information with file information by skipping
// this function and then reading the top stack frame.
//
return (new StackTrace(1, true)).GetFrame(0).GetFileLineNumber();
}
catch
{
}
return 0;
}
}
#endif
/// <summary>
/// Check the given condition and show an assert dialog when the
/// desktop is interactive.
/// <remarks>
/// Log the assertion at a warning level in case the desktop is not
/// interactive. The text will always contain full stack trace
/// information and will show the location of the error condition if
/// the source code is available.
/// </remarks>
/// <param name="condition">An expression to be tested for True</param>
/// <param name="errorText">The message to display</param>
/// <param name="lineNumber">
/// The line of the current error in the function. See
/// GenerateStackTrace for more information.
/// </param>
/// </summary>
[ConditionalAttribute("DEBUG")]
public static void Check(bool condition, String errorText, int lineNumber)
{
if ( !condition )
{
String detailMessage = String.Empty;
StringBuilder strBuilder;
GenerateStackTrace(lineNumber, out detailMessage);
strBuilder = new StringBuilder();
strBuilder.Append("Assert: ").Append("\r\n").Append(errorText).Append("\r\n").Append(detailMessage);
ApplicationLog.WriteWarning(strBuilder.ToString());
System.Diagnostics.Debug.Fail(errorText, detailMessage);
}
}
/// <summary>
/// Verify that a required condition holds.
/// <remarks>
/// Show an assert dialog in a DEBUG build before throwing an
/// ApplicationException. It is assumed that the exception will be
/// handled or logged, so this does not log a warning for the assertion
/// like the Check function, which does not actually throw.
/// </remarks>
/// <param name="condition">An expression to be tested for True</param>
/// <param name="errorText">The message to display</param>
/// <param name="lineNumber">
/// The line of the current error in the function. See
/// GenerateStackTrace for more information.
/// </param>
/// <exception class="System.ApplicationException">
/// The checked condition failed.
/// </exception>
/// </summary>
public static void CheckCondition(bool condition, String errorText, int lineNumber)
{
//Test the condition
if ( !condition )
{
//Assert and throw if the condition is not met
String detailMessage;
GenerateStackTrace(lineNumber, out detailMessage);
Debug.Fail(errorText, detailMessage);
throw new ApplicationException(errorText);
}
}
/// <summary>
/// Generate a stack trace to display/log with the assertion text.
/// <remarks>
/// The trace information includes file and line number information
/// if its available, as well as a copy of the line of text if
/// the source code is available. This function is only included in
/// DEBUG builds of the application.
/// </remarks>
/// <param name="lineNumber">
/// The line of the current error in the function. This
/// value should be retrieved by call Application.LineNumber
/// in the parameter list of any of the Check* functions. If
/// LineNumber is not provided,then the next executable line is used.
/// </param>
/// <param name="currentTrace">Returns the generated stack trace.</param>
/// </summary>
//marked by xue.haitao 2007.7.19
//[ConditionalAttribute("DEBUG")]
private static void GenerateStackTrace(int lineNumber, out String currentTrace)
{
currentTrace = String.Empty;
#if DEBUG
StringBuilder message; //Used for smart string concatenation
String fileName; //The source file name
int currentLine; //The line to process in the source file
String sourceLine; //The line from the source file
StreamReader fileStream = null; //The reader used to scan the source file
bool openedFile = false;
StackTrace curTrace;
StackFrame curFrame;
message = new StringBuilder();
//New StackTrace should never fail, but Try/Catch to be rock solid.
try
{
//Get a new stack trace with line information. Skip the first function
// and second functions (this one, and the calling Check* function)
curTrace = new StackTrace(2, true);
try
{
//
// Get the first retrieved stack frame and attempt to get
// file information from the trace, then open the file
// and find the specified line. Display as much information
// as possible if this is not supported.
//
curFrame = curTrace.GetFrame(0);
//Retrieve and add File/Line information. Note that we only
//proceed if both of these are available.
if ((String.Empty != (fileName = curFrame.GetFileName())) &&
(0 <= (currentLine = (lineNumber != 0) ? lineNumber : curFrame.GetFileLineNumber())))
{
//Append File name and line number
message.Append(fileName).Append(", Line: ").Append(currentLine);
//Append the actual code if we can find the source file
fileStream = new StreamReader(fileName);
openedFile = true;
do
{
sourceLine = fileStream.ReadLine();
--currentLine;
} while (currentLine != 0);
message.Append("\r\n");
if (lineNumber != 0)
{
message.Append("Current executable line:");
}
else
{
message.Append("\r\n").Append("Next executable line:");
}
message.Append("\r\n").Append(sourceLine.Trim());
}
}
catch
{
//Ignore errors, just show as much as we can
}
finally
{
//Always close the file
if (openedFile) fileStream.Close();
}
//Retrieve the final string
currentTrace = message.ToString();
}
catch
{
//Nothing to do, just get out of here with the default (empty) return value
}
#endif
}
} // class ApplicationAssert
} // namespace T1.SystemFramework