Merge pull request #3386 from alexanderkyte/nunit_lite_return_status
[mono.git] / mcs / class / referencesource / System.Web / Util / Debug.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="Debug.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 namespace System.Web.Util {
8     using Microsoft.Win32;
9     using Microsoft.Win32.SafeHandles;
10     using System;
11     using System.Collections;
12     using System.Collections.Generic;
13     using System.Collections.ObjectModel;
14     using System.Globalization;
15     using System.Reflection;
16     using System.Runtime.ConstrainedExecution;
17     using System.Runtime.InteropServices;
18     using System.Security;
19     using System.Security.Permissions;
20     using System.Threading;
21     using System.Runtime.Versioning;
22
23     internal static class Debug {
24
25         internal const string   TAG_INTERNAL = "Internal";
26         internal const string   TAG_EXTERNAL = "External";
27         internal const string   TAG_ALL      = "*";
28
29         internal const string   DATE_FORMAT = @"yyyy/MM/dd HH:mm:ss.ffff";
30         internal const string   TIME_FORMAT = @"HH:mm:ss:ffff";
31
32         // Some of these APIs must be always available, not #ifdefed away.
33         [SuppressUnmanagedCodeSecurity]
34         private static partial class NativeMethods {
35             [DllImport("kernel32.dll")]
36             internal extern static bool IsDebuggerPresent();
37         }
38
39 #if DBG
40         private static partial class NativeMethods {
41             [DllImport("kernel32.dll")]
42             internal extern static void DebugBreak();
43
44             [DllImport("kernel32.dll")]
45             internal extern static int GetCurrentProcessId();
46
47             [DllImport("kernel32.dll")]
48             internal extern static int GetCurrentThreadId();
49
50             [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
51             internal extern static IntPtr GetCurrentProcess();
52
53             [DllImport("kernel32.dll", SetLastError=true)]
54             internal extern static bool TerminateProcess(HandleRef processHandle, int exitCode);
55
56             [DllImport("kernel32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
57             internal extern static void OutputDebugString(string message);
58
59             internal const int PM_NOREMOVE = 0x0000;
60             internal const int PM_REMOVE = 0x0001;
61
62             [StructLayout(LayoutKind.Sequential)]
63             internal struct MSG {
64                 internal IntPtr   hwnd;
65                 internal int      message;
66                 internal IntPtr   wParam;
67                 internal IntPtr   lParam;
68                 internal int      time;
69                 internal int      pt_x;
70                 internal int      pt_y;
71             }
72
73             [DllImport("user32.dll", CharSet=System.Runtime.InteropServices.CharSet.Auto)]
74             internal extern static bool PeekMessage([In, Out] ref MSG msg, HandleRef hwnd, int msgMin, int msgMax, int remove);
75
76             internal const int 
77                 MB_OK = 0x00000000,
78                 MB_OKCANCEL = 0x00000001,
79                 MB_ABORTRETRYIGNORE = 0x00000002,
80                 MB_YESNOCANCEL = 0x00000003,
81                 MB_YESNO = 0x00000004,
82                 MB_RETRYCANCEL = 0x00000005,
83                 MB_ICONHAND = 0x00000010,
84                 MB_ICONQUESTION = 0x00000020,
85                 MB_ICONEXCLAMATION = 0x00000030,
86                 MB_ICONASTERISK = 0x00000040,
87                 MB_USERICON = 0x00000080,
88                 MB_ICONWARNING = 0x00000030,
89                 MB_ICONERROR = 0x00000010,
90                 MB_ICONINFORMATION = 0x00000040,
91                 MB_DEFBUTTON1 = 0x00000000,
92                 MB_DEFBUTTON2 = 0x00000100,
93                 MB_DEFBUTTON3 = 0x00000200,
94                 MB_DEFBUTTON4 = 0x00000300,
95                 MB_APPLMODAL = 0x00000000,
96                 MB_SYSTEMMODAL = 0x00001000,
97                 MB_TASKMODAL = 0x00002000,
98                 MB_HELP = 0x00004000,
99                 MB_NOFOCUS = 0x00008000,
100                 MB_SETFOREGROUND = 0x00010000,
101                 MB_DEFAULT_DESKTOP_ONLY = 0x00020000,
102                 MB_TOPMOST = 0x00040000,
103                 MB_RIGHT = 0x00080000,
104                 MB_RTLREADING = 0x00100000,
105                 MB_SERVICE_NOTIFICATION = 0x00200000,
106                 MB_SERVICE_NOTIFICATION_NT3X = 0x00040000,
107                 MB_TYPEMASK = 0x0000000F,
108                 MB_ICONMASK = 0x000000F0,
109                 MB_DEFMASK = 0x00000F00,
110                 MB_MODEMASK = 0x00003000,
111                 MB_MISCMASK = 0x0000C000;
112
113             internal const int
114                 IDOK = 1,
115                 IDCANCEL = 2,
116                 IDABORT = 3,
117                 IDRETRY = 4,
118                 IDIGNORE = 5,
119                 IDYES = 6,
120                 IDNO = 7,
121                 IDCLOSE = 8,
122                 IDHELP = 9;
123
124
125             [DllImport("user32.dll", CharSet=CharSet.Auto, BestFitMapping=false)]
126             internal extern static int MessageBox(HandleRef hWnd, string text, string caption, int type);
127
128             internal static readonly IntPtr HKEY_LOCAL_MACHINE = unchecked((IntPtr)(int)0x80000002);
129
130             internal const int READ_CONTROL           = 0x00020000;
131             internal const int STANDARD_RIGHTS_READ   = READ_CONTROL;
132
133             internal const int SYNCHRONIZE            = 0x00100000;
134
135             internal const int KEY_QUERY_VALUE        = 0x0001;
136             internal const int KEY_ENUMERATE_SUB_KEYS = 0x0008;
137             internal const int KEY_NOTIFY             = 0x0010;
138
139
140             internal const int KEY_READ               = ((STANDARD_RIGHTS_READ |
141                                                                KEY_QUERY_VALUE |
142                                                                KEY_ENUMERATE_SUB_KEYS |
143                                                                KEY_NOTIFY)
144                                                               &
145                                                               (~SYNCHRONIZE));
146
147             internal const int REG_NOTIFY_CHANGE_NAME       = 1;
148             internal const int REG_NOTIFY_CHANGE_LAST_SET   = 4;
149
150
151             [DllImport("advapi32.dll", CharSet=CharSet.Auto, BestFitMapping=false, SetLastError=true)]
152             internal extern static int RegOpenKeyEx(IntPtr hKey, string lpSubKey, int ulOptions, int samDesired, out SafeRegistryHandle hkResult);
153
154             [DllImport("advapi32.dll", ExactSpelling=true, SetLastError=true)]
155             internal extern static int RegNotifyChangeKeyValue(SafeRegistryHandle hKey, bool watchSubTree, uint notifyFilter, SafeWaitHandle regEvent, bool async);
156         }
157
158         private class SafeRegistryHandle : SafeHandleZeroOrMinusOneIsInvalid {
159
160             // Note: Officially -1 is the recommended invalid handle value for
161             // registry keys, but we'll also get back 0 as an invalid handle from
162             // RegOpenKeyEx.
163
164             [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode=true)]
165             [ResourceExposure(ResourceScope.Machine)]
166             internal SafeRegistryHandle() : base(true) {}
167
168             [DllImport("advapi32.dll"),
169              SuppressUnmanagedCodeSecurity,
170              ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
171             private static extern int RegCloseKey(IntPtr hKey);
172
173             override protected bool ReleaseHandle()
174             {
175                 // Returns a Win32 error code, 0 for success
176                 int r = RegCloseKey(handle);
177                 return r == 0;
178             }
179         }
180
181         private enum TagValue {
182             Disabled = 0,
183             Enabled = 1,
184             Break = 2,
185
186             Min = Disabled,
187             Max = Break,
188         }
189
190         private const string            TAG_ASSERT = "Assert";
191         private const string            TAG_ASSERT_BREAK = "AssertBreak";
192
193         private const string            TAG_DEBUG_VERBOSE = "DebugVerbose";
194         private const string            TAG_DEBUG_MONITOR = "DebugMonitor";
195         private const string            TAG_DEBUG_PREFIX = "DebugPrefix";
196         private const string            TAG_DEBUG_THREAD_PREFIX = "DebugThreadPrefix";
197
198         private const string            PRODUCT = "Microsoft .NET Framework";
199         private const string            COMPONENT = "System.Web";
200
201         private static string           s_regKeyName = @"Software\Microsoft\ASP.NET\Debug";
202         private static string           s_listenKeyName = @"Software\Microsoft";
203
204         private static bool             s_assert;
205         private static bool             s_assertBreak;
206
207         private static bool             s_includePrefix;
208         private static bool             s_includeThreadPrefix;
209         private static bool             s_monitor;
210
211         private static object           s_lock;
212         private static volatile bool    s_inited;
213         private static ReadOnlyCollection<Tag>  s_tagDefaults;
214         private static List<Tag>        s_tags;
215
216         private static AutoResetEvent       s_notifyEvent;
217         private static RegisteredWaitHandle s_waitHandle;
218         private static SafeRegistryHandle   s_regHandle;
219         private static bool                 s_stopMonitoring;
220
221         private static Hashtable        s_tableAlwaysValidate;
222         private static Type[]           s_DumpArgs;
223         private static Type[]           s_ValidateArgs;
224
225         private class Tag {
226             string      _name;
227             TagValue    _value;
228             int         _prefixLength;
229
230             internal Tag(string name, TagValue value) {
231                 _name = name;
232                 _value = value;
233
234                 if (_name[_name.Length - 1] == '*') {
235                     _prefixLength = _name.Length - 1;
236                 }
237                 else {
238                     _prefixLength = -1;
239                 }
240             }
241
242             internal string Name {
243                 get {return _name;}
244             }
245
246             internal TagValue Value {
247                 get {return _value;}
248             }
249
250             internal int PrefixLength {
251                 get {return _prefixLength;}
252             }
253         }
254
255         static Debug() {
256             s_lock = new object();
257         }
258
259         private static void EnsureInit() {
260             bool continueInit = false;
261
262             if (!s_inited) {
263                 lock (s_lock) {
264                     if (!s_inited) {
265                         s_tableAlwaysValidate = new Hashtable();
266                         s_DumpArgs = new Type[1] {typeof(string)}; 
267                         s_ValidateArgs = new Type[0];              
268
269                         List<Tag> tagDefaults = new List<Tag>();
270                         tagDefaults.Add(new Tag(TAG_ALL, TagValue.Disabled));
271                         tagDefaults.Add(new Tag(TAG_INTERNAL, TagValue.Enabled));
272                         tagDefaults.Add(new Tag(TAG_EXTERNAL, TagValue.Enabled));
273                         tagDefaults.Add(new Tag(TAG_ASSERT, TagValue.Break));
274                         tagDefaults.Add(new Tag(TAG_ASSERT_BREAK, TagValue.Disabled));
275                         tagDefaults.Add(new Tag(TAG_DEBUG_VERBOSE, TagValue.Enabled));
276                         tagDefaults.Add(new Tag(TAG_DEBUG_MONITOR, TagValue.Enabled));
277                         tagDefaults.Add(new Tag(TAG_DEBUG_PREFIX, TagValue.Enabled));
278                         tagDefaults.Add(new Tag(TAG_DEBUG_THREAD_PREFIX, TagValue.Enabled));
279
280                         s_tagDefaults = tagDefaults.AsReadOnly();
281                         s_tags = new List<Tag>(s_tagDefaults);
282                         GetBuiltinTagValues();
283
284                         continueInit = true;
285                         s_inited = true;
286                     }
287                 }
288             }
289
290             // Work to do outside the init lock.
291             if (continueInit) {
292                 ReadTagsFromRegistry();
293                 Trace(TAG_DEBUG_VERBOSE, "Debugging package initialized");
294
295                 // Need to read tags before starting to monitor in order to get TAG_DEBUG_MONITOR
296                 StartRegistryMonitor();
297             }
298         }
299
300         private static bool StringEqualsIgnoreCase(string s1, string s2) {
301             return StringComparer.OrdinalIgnoreCase.Equals(s1, s2);
302         }
303
304         [RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
305         private static void WriteTagsToRegistry() {
306             try {
307                 using (RegistryKey key = Registry.LocalMachine.CreateSubKey(s_regKeyName)) {
308                     List<Tag> tags = s_tags;
309                     foreach (Tag tag in tags) {
310                         key.SetValue(tag.Name, tag.Value, RegistryValueKind.DWord);
311                     }
312                 }
313             }
314             catch {
315             }
316         }
317
318         private static void GetBuiltinTagValues() {
319             // Use GetTagValue instead of IsTagEnabled because it does not call EnsureInit
320             // and potentially recurse.
321             s_assert              = (GetTagValue(TAG_ASSERT) != TagValue.Disabled);
322             s_assertBreak         = (GetTagValue(TAG_ASSERT_BREAK) != TagValue.Disabled);
323             s_includePrefix       = (GetTagValue(TAG_DEBUG_PREFIX) != TagValue.Disabled);
324             s_includeThreadPrefix = (GetTagValue(TAG_DEBUG_THREAD_PREFIX) != TagValue.Disabled);
325             s_monitor             = (GetTagValue(TAG_DEBUG_MONITOR) != TagValue.Disabled);
326         }
327
328         [RegistryPermission(SecurityAction.Assert, Unrestricted=true)]
329         private static void ReadTagsFromRegistry() {
330             lock (s_lock) {
331                 try {
332                     List<Tag> tags = new List<Tag>(s_tagDefaults);
333                     string[] names = null;
334
335                     bool writeTags = false;
336                     using (RegistryKey key = Registry.LocalMachine.OpenSubKey(s_regKeyName, false)) {
337                         if (key != null) {
338                             names = key.GetValueNames();
339                             foreach (string name in names) {
340                                 TagValue value = TagValue.Disabled;
341                                 try {
342                                     TagValue keyvalue = (TagValue) key.GetValue(name);
343                                     if (TagValue.Min <= keyvalue && keyvalue <= TagValue.Max) {
344                                         value = keyvalue;
345                                     }
346                                     else {
347                                         writeTags = true;
348                                     }
349                                 }
350                                 catch {
351                                     writeTags = true;
352                                 }
353
354                                 // Add tag to list, making sure it is unique.
355                                 Tag tag = new Tag(name, (TagValue) value);
356                                 bool found = false;
357                                 for (int i = 0; i < s_tagDefaults.Count; i++) {
358                                     if (StringEqualsIgnoreCase(name, tags[i].Name)) {
359                                         found = true;
360                                         tags[i] = tag;
361                                         break;
362                                     }
363                                 }
364
365                                 if (!found) {
366                                     tags.Add(tag);
367                                 }
368                             }
369                         }
370                     }
371
372                     s_tags = tags;
373                     GetBuiltinTagValues();
374
375                     // Write tags out if there was an invalid value or 
376                     // not all default tags are present.
377                     if (writeTags || (names != null && names.Length < tags.Count)) {
378                         WriteTagsToRegistry();
379                     }
380                 }
381                 catch {
382                     s_tags = new List<Tag>(s_tagDefaults);
383                 }
384             }
385         }
386
387         private static void StartRegistryMonitor() {
388             if (!s_monitor) {
389                 Trace(TAG_DEBUG_VERBOSE, "WARNING: Registry monitoring disabled, changes during process execution will not be recognized."); 
390                 return;
391             }
392
393             Trace(TAG_DEBUG_VERBOSE, "Monitoring registry key " + s_listenKeyName + " for changes.");
394
395             // Event used to notify of changes.
396             s_notifyEvent = new AutoResetEvent(false);
397
398             // Register a wait on the event.
399             s_waitHandle = ThreadPool.RegisterWaitForSingleObject(s_notifyEvent, OnRegChangeKeyValue, null, -1, false);
400
401             // Monitor the registry.
402             MonitorRegistryForOneChange();
403         }
404
405         private static void StopRegistryMonitor() {
406             // Cleanup allocated handles
407             s_stopMonitoring = true;
408
409             if (s_regHandle != null) {
410                 s_regHandle.Close();
411                 s_regHandle = null;
412             }
413
414             if (s_waitHandle != null) {
415                 s_waitHandle.Unregister(s_notifyEvent);
416                 s_waitHandle = null;
417             }
418
419             if (s_notifyEvent != null) {
420                 s_notifyEvent.Close();
421                 s_notifyEvent = null;
422             }
423
424             Trace(TAG_DEBUG_VERBOSE, "Registry monitoring stopped."); 
425         }
426
427         public static void OnRegChangeKeyValue(object state, bool timedOut) {
428             if (!s_stopMonitoring) {
429                 if (timedOut) {
430                     StopRegistryMonitor();
431                 }
432                 else {
433                     // Monitor again
434                     MonitorRegistryForOneChange();
435
436                     // Once we're monitoring, read the changes to the registry.
437                     // We have to do this after we start monitoring in order
438                     // to catch all changes to the registry.
439                     ReadTagsFromRegistry();
440                 }
441             }
442         }
443
444         private static void MonitorRegistryForOneChange() {
445             // Close the open reg handle
446             if (s_regHandle != null) {
447                 s_regHandle.Close();
448                 s_regHandle = null;
449             }
450
451             // Open the reg key
452             int result = NativeMethods.RegOpenKeyEx(NativeMethods.HKEY_LOCAL_MACHINE, s_listenKeyName, 0, NativeMethods.KEY_READ, out s_regHandle);
453             if (result != 0) {
454                 StopRegistryMonitor();
455                 return;
456             }
457
458             // Listen for changes.
459             result = NativeMethods.RegNotifyChangeKeyValue(
460                     s_regHandle, 
461                     true, 
462                     NativeMethods.REG_NOTIFY_CHANGE_NAME | NativeMethods.REG_NOTIFY_CHANGE_LAST_SET,
463                     s_notifyEvent.SafeWaitHandle,
464                     true);
465
466             if (result != 0) {
467                 StopRegistryMonitor();
468             }
469         }
470
471         private static Tag FindMatchingTag(string name, bool exact) {
472             List<Tag> tags = s_tags;
473
474             // Look for exact match first
475             foreach (Tag tag in tags) {
476                 if (StringEqualsIgnoreCase(name, tag.Name)) {
477                     return tag;
478                 }
479             }
480
481             if (exact) {
482                 return null;
483             }
484
485             Tag longestTag = null;
486             int longestPrefix = -1;
487             foreach (Tag tag in tags) {
488                 if (    tag.PrefixLength > longestPrefix && 
489                         0 == string.Compare(name, 0, tag.Name, 0, tag.PrefixLength, StringComparison.OrdinalIgnoreCase)) {
490
491                     longestTag = tag;
492                     longestPrefix = tag.PrefixLength;
493                 }
494             }
495
496             return longestTag;
497         }
498
499         private static TagValue GetTagValue(string name) {
500             Tag tag = FindMatchingTag(name, false);
501             if (tag != null) {
502                 return tag.Value;
503             }
504             else {
505                 return TagValue.Disabled;
506             }
507         }
508
509         [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
510         private static bool TraceBreak(string tagName, string message, Exception e, bool includePrefix) {
511             EnsureInit();
512
513             TagValue tagValue = GetTagValue(tagName);
514             if (tagValue == TagValue.Disabled) {
515                 return false;
516             }
517
518             bool isAssert = object.ReferenceEquals(tagName, TAG_ASSERT);
519             if (isAssert) {
520                 tagName = "";
521             }
522
523             string exceptionMessage = null;
524             if (e != null) {
525                 string httpCode = null;
526                 string errorCode = null;
527
528                 if (e is HttpException) {
529                     httpCode = " _httpCode=" + ((HttpException)e).GetHttpCode().ToString(CultureInfo.InvariantCulture) + " ";
530                 }
531
532                 if (e is ExternalException) {
533                     // note that HttpExceptions are ExternalExceptions
534                     errorCode = "_hr=0x" + ((ExternalException)e).ErrorCode.ToString("x", CultureInfo.InvariantCulture);
535                 }
536
537                 // Use e.ToString() in order to get inner exception
538                 if (errorCode != null) {
539                     exceptionMessage = "Exception " + e.ToString() + "\n" + httpCode + errorCode;
540                 }
541                 else {
542                     exceptionMessage = "Exception " + e.ToString();
543                 }
544             }
545
546             if (string.IsNullOrEmpty(message) & exceptionMessage != null) {
547                 message = exceptionMessage;
548                 exceptionMessage = null;
549             }
550
551             string traceFormat;
552             int idThread = 0;
553             int idProcess = 0;
554
555             if (!includePrefix || !s_includePrefix) {
556                 traceFormat = "{4}\n{5}";
557             }
558             else {
559                 if (s_includeThreadPrefix) {
560                     idThread = NativeMethods.GetCurrentThreadId();
561                     idProcess = NativeMethods.GetCurrentProcessId();
562                     traceFormat = "[0x{0:x}.{1:x} {2} {3}] {4}\n{5}";
563                 }
564                 else {
565                     traceFormat = "[{2} {3}] {4}\n{5}";
566                 }
567             }
568
569             string suffix = "";
570             if (exceptionMessage != null) {
571                 suffix += exceptionMessage + "\n";
572             }
573
574             bool doBreak = (tagValue == TagValue.Break);
575             if (doBreak && !isAssert) {
576                 suffix += "Breaking into debugger...\n";
577             }
578
579             string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, idProcess, idThread, COMPONENT, tagName, message, suffix);
580
581             NativeMethods.OutputDebugString(traceMessage);
582
583             return doBreak;
584         }
585
586         private class MBResult {
587             internal int Result;
588         }
589
590         [ResourceExposure(ResourceScope.None)]
591         static bool DoAssert(string message) {
592             if (!s_assert) {
593                 return false;
594             }
595
596             // Skip 2 frames - one for this function, one for
597             // the public Assert function that called this function.
598             System.Diagnostics.StackFrame frame = new System.Diagnostics.StackFrame(2, true);
599             System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(2, true);
600
601             string fileName = frame.GetFileName();
602             int lineNumber = frame.GetFileLineNumber();
603
604             string traceFormat;
605             if (!string.IsNullOrEmpty(fileName)) {
606                 traceFormat = "ASSERTION FAILED: {0}\nFile: {1}:{2}\nStack trace:\n{3}";
607             }
608             else {
609                 traceFormat = "ASSERTION FAILED: {0}\nStack trace:\n{3}";
610             }
611
612             string traceMessage = string.Format(CultureInfo.InvariantCulture, traceFormat, message, fileName, lineNumber, trace.ToString());
613
614             if (!TraceBreak(TAG_ASSERT, traceMessage, null, true)) {
615                 // If the value of "Assert" is not TagValue.Break, then don't even ask user.
616                 return false;
617             }
618
619             if (s_assertBreak) {
620                 // If "AssertBreak" is enabled, then always break.
621                 return true;
622             }
623
624             string dialogFormat;
625             if (!string.IsNullOrEmpty(fileName)) {
626                 dialogFormat = 
627 @"Failed expression: {0}
628 File: {1}:{2}
629 Component: {3}
630 PID={4} TID={5}
631 Stack trace:
632 {6}
633
634 A=Exit process R=Debug I=Continue";
635             }
636             else {
637                 dialogFormat = 
638 @"Failed expression: {0}
639 (no file information available)
640 Component: {3}
641 PID={4} TID={5}
642 Stack trace:
643 {6}
644
645 A=Exit process R=Debug I=Continue";
646             }
647
648             string dialogMessage = string.Format(
649                 CultureInfo.InvariantCulture,
650                 dialogFormat,
651                 message,
652                 fileName, lineNumber,
653                 COMPONENT,
654                 NativeMethods.GetCurrentProcessId(), NativeMethods.GetCurrentThreadId(),
655                 trace.ToString());
656
657             MBResult mbResult = new MBResult();
658
659             Thread thread = new Thread(
660                 delegate() {
661                     for (int i = 0; i < 100; i++) {
662                         NativeMethods.MSG msg = new NativeMethods.MSG();
663                         NativeMethods.PeekMessage(ref msg, new HandleRef(mbResult, IntPtr.Zero), 0, 0, NativeMethods.PM_REMOVE);
664                     }
665
666                     mbResult.Result = NativeMethods.MessageBox(new HandleRef(mbResult, IntPtr.Zero), dialogMessage, PRODUCT + " Assertion",                
667                         NativeMethods.MB_SERVICE_NOTIFICATION | 
668                         NativeMethods.MB_TOPMOST |
669                         NativeMethods.MB_ABORTRETRYIGNORE | 
670                         NativeMethods.MB_ICONEXCLAMATION);
671                 }
672             );
673
674             thread.Start();
675             thread.Join();
676
677             if (mbResult.Result == NativeMethods.IDABORT) {
678                 IntPtr currentProcess = NativeMethods.GetCurrentProcess();
679                 NativeMethods.TerminateProcess(new HandleRef(mbResult, currentProcess), 1);
680             }
681
682             return mbResult.Result == NativeMethods.IDRETRY;
683         }
684 #endif
685
686         //
687         // Sends the message to the debugger if the tag is enabled.
688         // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
689         //
690         [System.Diagnostics.Conditional("DBG")]
691         internal static void Trace(string tagName, string message) {
692 #if DBG
693             if (TraceBreak(tagName, message, null, true)) {
694                 Break();
695             }
696 #endif
697         }
698
699         //
700         // Sends the message to the debugger if the tag is enabled.
701         // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
702         //
703         [System.Diagnostics.Conditional("DBG")]
704         internal static void Trace(string tagName, string message, bool includePrefix) {
705 #if DBG
706             if (TraceBreak(tagName, message, null, includePrefix)) {
707                 Break();
708             }
709 #endif
710         }
711
712         //
713         // Sends the message to the debugger if the tag is enabled.
714         // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
715         //
716         [System.Diagnostics.Conditional("DBG")]
717         internal static void Trace(string tagName, string message, Exception e) {
718 #if DBG
719             if (TraceBreak(tagName, message, e, true)) {
720                 Break();
721             }
722 #endif
723         }
724
725         //
726         // Sends the message to the debugger if the tag is enabled.
727         // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
728         //
729         [System.Diagnostics.Conditional("DBG")]
730         internal static void Trace(string tagName, Exception e) {
731 #if DBG
732             if (TraceBreak(tagName, null, e, true)) {
733                 Break();
734             }
735 #endif
736         }
737
738         //
739         // Sends the message to the debugger if the tag is enabled.
740         // Also breaks into the debugger the value of the tag is 2 (TagValue.Break).
741         //
742         [System.Diagnostics.Conditional("DBG")]
743         internal static void Trace(string tagName, string message, Exception e, bool includePrefix) {
744 #if DBG
745             if (TraceBreak(tagName, message, e, includePrefix)) {
746                 Break();
747             }
748 #endif
749         }
750
751 #if DBG
752 #endif
753
754         [System.Diagnostics.Conditional("DBG")]
755         public static void TraceException(String tagName, Exception e) {
756 #if DBG
757             if (TraceBreak(tagName, null, e, true)) {
758                 Break();
759             }
760 #endif
761         }
762
763
764         //
765         // If the assertion is false and the 'Assert' tag is enabled:
766         //      * Send a message to the debugger.
767         //      * If the 'AssertBreak' tag is enabled, immediately break into the debugger
768         //      * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
769         //
770         [System.Diagnostics.Conditional("DBG")]
771         internal static void Assert(bool assertion, string message) {
772 #if DBG
773             EnsureInit();
774             if (assertion == false) {
775                 if (DoAssert(message)) {
776                     Break();
777                 }
778             }
779 #endif
780         }
781
782
783         //
784         // If the assertion is false and the 'Assert' tag is enabled:
785         //      * Send a message to the debugger.
786         //      * If the 'AssertBreak' tag is enabled, immediately break into the debugger
787         //      * Else display a dialog box asking the user to Abort, Retry (break), or Ignore
788         //
789         [System.Diagnostics.Conditional("DBG")]
790         [ResourceExposure(ResourceScope.None)]
791         internal static void Assert(bool assertion) {
792 #if DBG
793             EnsureInit();
794             if (assertion == false) {
795                 if (DoAssert(null)) {
796                     Break();
797                 }
798             }
799 #endif
800         }
801
802         //
803         // Like Assert, but the assertion is always considered to be false.
804         //
805         [System.Diagnostics.Conditional("DBG")]
806         [ResourceExposure(ResourceScope.None)]
807         internal static void Fail(string message) {
808 #if DBG
809             Assert(false, message);
810 #endif
811         }
812
813         //
814         // Returns true if the tag is enabled, false otherwise.
815         // Note that the tag needn't be an exact match.
816         //
817         [ResourceExposure(ResourceScope.None)]
818         internal static bool IsTagEnabled(string tagName) {
819 #if DBG
820             EnsureInit();
821             return GetTagValue(tagName) != TagValue.Disabled;
822 #else
823             return false;
824 #endif
825         }
826
827         //
828         // Returns true if the tag present. 
829         // This function chekcs for an exact match.
830         //
831         [ResourceExposure(ResourceScope.None)]
832         internal static bool IsTagPresent(string tagName) {
833 #if DBG
834             EnsureInit();
835             return FindMatchingTag(tagName, true) != null;
836 #else
837             return false;
838 #endif
839         }
840
841         // Returns a value indicating whether a debugger is attached to the process.
842         // We don't #ifdef this away since we might need to change behavior based
843         // on whether one is attached.
844         internal static bool IsDebuggerPresent() {
845             return (NativeMethods.IsDebuggerPresent() || System.Diagnostics.Debugger.IsAttached);
846         }
847
848         //
849         // Breaks into the debugger, or launches one if not yet attached.
850         //
851         [System.Diagnostics.Conditional("DBG")]
852         [ResourceExposure(ResourceScope.None)]
853         internal static void Break() {
854 #if DBG
855             if (NativeMethods.IsDebuggerPresent()) {
856                 NativeMethods.DebugBreak();
857             }
858             else if (!System.Diagnostics.Debugger.IsAttached) {
859                 System.Diagnostics.Debugger.Launch();
860             }
861             else {
862                 System.Diagnostics.Debugger.Break();            
863             }
864 #endif
865         }
866
867
868         //
869         // Tells the debug system to always validate calls for a
870         // particular tag. This is useful for temporarily enabling
871         // validation in stress tests or other situations where you
872         // may not have control over the debug tags that are enabled
873         // on a particular machine.
874         // 
875         [System.Diagnostics.Conditional("DBG")]
876         internal static void AlwaysValidate(string tagName) {
877 #if DBG
878             EnsureInit();
879             s_tableAlwaysValidate[tagName] = tagName;
880 #endif
881         }
882
883
884         //
885         // Throws an exception if the assertion is not valid.
886         // Use this function from a DebugValidate method where
887         // you would otherwise use Assert.
888         //
889         [System.Diagnostics.Conditional("DBG")]
890         internal static void CheckValid(bool assertion, string message) {
891 #if DBG
892             if (!assertion) {
893                 throw new Exception(message);
894             }
895 #endif
896         }
897
898         //
899         // Calls DebugValidate on an object if such a method exists.
900         //
901         // This method should be used from implementations of DebugValidate
902         // where it is unknown whether an object has a DebugValidate method.
903         // For example, the DoubleLink class uses it to validate the
904         // item of type Object which it points to.
905         //
906         // This method should NOT be used when code wants to conditionally
907         // validate an object and have a failed validation caught in an assert.
908         // Use Debug.Validate(tagName, obj) for that purpose.
909         //
910         [System.Diagnostics.Conditional("DBG")]
911         [ResourceExposure(ResourceScope.None)]
912         internal static void Validate(Object obj) {
913 #if DBG
914             Type        type;
915             MethodInfo  mi;
916
917             if (obj != null) {
918                 type = obj.GetType();
919
920                 mi = type.GetMethod(
921                         "DebugValidate", 
922                         BindingFlags.NonPublic | BindingFlags.Instance,
923                         null,
924                         s_ValidateArgs,
925                         null);
926
927                 if (mi != null) {
928                     object[] tempIndex = null;
929                     mi.Invoke(obj, tempIndex);
930                 }
931             }
932 #endif
933         }
934
935         // Ensures that the bounds provided to an array-consuming method are correct.
936         // Because this method is on the Debug type, it should only be used in debugging
937         // code and should not be depended on for user input sanitization (since the
938         // compiler won't emit calls to it.)
939         [System.Diagnostics.Conditional("DBG")]
940         internal static void ValidateArrayBounds<T>(T[] array, int offset, int count) {
941 #if DBG
942             // these are the same checks as in the ArraySegment<T> ctor
943             Assert(array != null, "Array cannot be null.");
944             Assert(offset >= 0, "Offset must be non-negative.");
945             Assert(count >= 0, "Count must be non-negative.");
946             Assert(array.Length - offset >= count, "Invalid offset.");
947 #endif
948         }
949
950         //
951         // Validates an object is the "Validate" tag is enabled, or when
952         // the "Validate" tag is not disabled and the given 'tag' is enabled.
953         // An Assertion is made if the validation fails.
954         //
955         [System.Diagnostics.Conditional("DBG")]
956         [ResourceExposure(ResourceScope.None)]
957         internal static void Validate(string tagName, Object obj) {
958 #if DBG
959             EnsureInit();
960
961             if (    obj != null 
962                     && (    IsTagEnabled("Validate")
963                             ||  (   !IsTagPresent("Validate") 
964                                     && (   s_tableAlwaysValidate[tagName] != null 
965                                            ||  IsTagEnabled(tagName))))) {
966                 try {
967                     Debug.Validate(obj);
968                 }
969                 catch (Exception e) {
970                     Debug.Assert(false, "Validate failed: " + e.InnerException.Message);
971                 }
972 #pragma warning disable 1058
973                 catch {
974                     Debug.Assert(false, "Validate failed.  Non-CLS compliant exception caught.");
975                 }
976 #pragma warning restore 1058
977             }
978 #endif
979         }
980
981 #if DBG
982
983         //
984         // Calls DebugDescription on an object to get its description.
985         //
986         // This method should only be used in implementations of DebugDescription
987         // where it is not known whether a nested objects has an implementation
988         // of DebugDescription. For example, the double linked list class uses
989         // GetDescription to get the description of the item it points to.
990         //
991         // This method should NOT be used when you want to conditionally
992         // dump an object. Use Debug.Dump instead.
993         //
994         // @param obj      The object to call DebugDescription on. May be null.
995         // @param indent   A prefix for each line in the description. This is used
996         //                 to allow the nested display of objects within other objects.
997         //                 The indent is usually a multiple of four spaces.
998         //
999         // @return         The description.
1000         //
1001         [ReflectionPermission(SecurityAction.Assert, Unrestricted=true)]
1002         internal static string GetDescription(Object obj, string indent) {
1003             string      description;
1004             Type        type;
1005             MethodInfo  mi;
1006             Object[]   parameters;
1007
1008             if (obj == null) {
1009                 description = "\n";
1010             }
1011             else {
1012                 type = obj.GetType();
1013                 mi = type.GetMethod(
1014                         "DebugDescription", 
1015                         BindingFlags.NonPublic | BindingFlags.Instance,
1016                         null,
1017                         s_DumpArgs,
1018                         null);
1019                         
1020                 if (mi == null || mi.ReturnType != typeof(string)) {
1021                     description = indent + obj.ToString();
1022                 }
1023                 else {
1024                     parameters = new Object[1] {(Object) indent};
1025                     description = (string) mi.Invoke(obj, parameters);
1026                 }
1027             }
1028
1029             return description;
1030         }
1031 #endif
1032
1033
1034         // 
1035         // Dumps an object to the debugger if the "Dump" tag is enabled,
1036         // or if the "Dump" tag is not present and the 'tag' is enabled.
1037         // 
1038         // @param tagName  The tag to Dump with.
1039         // @param obj  The object to dump.
1040         // 
1041         [System.Diagnostics.Conditional("DBG")]
1042         internal static void Dump(string tagName, Object obj) {
1043 #if DBG
1044             EnsureInit();
1045
1046             string  description;
1047             string  traceTag = null;
1048             bool    dumpEnabled, dumpPresent;
1049
1050             if (obj != null) {
1051                 dumpEnabled = IsTagEnabled("Dump");
1052                 dumpPresent = IsTagPresent("Dump");
1053                 if (dumpEnabled || !dumpPresent) {
1054                     if (IsTagEnabled(tagName)) {
1055                         traceTag = tagName;
1056                     }
1057                     else if (dumpEnabled) {
1058                         traceTag = "Dump";
1059                     }
1060
1061                     if (traceTag != null) {
1062                         description = GetDescription(obj, string.Empty);
1063                         Debug.Trace(traceTag, "Dump\n" + description);
1064                     }
1065                 }
1066             }
1067 #endif
1068         }
1069
1070 #if DBG
1071         static internal string ToStringMaybeNull(object o) {
1072             if (o != null) {
1073                 return o.ToString();
1074             }
1075
1076             return "<null>";
1077         }
1078 #endif
1079
1080         static internal string FormatUtcDate(DateTime utcTime) {
1081 #if DBG
1082             DateTime localTime = DateTimeUtil.ConvertToLocalTime(utcTime);
1083             return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
1084 #else
1085             return string.Empty;
1086 #endif
1087         }
1088
1089         static internal string FormatLocalDate(DateTime localTime) {
1090 #if DBG
1091             return localTime.ToString(DATE_FORMAT, CultureInfo.InvariantCulture);
1092 #else
1093             return string.Empty;
1094 #endif
1095         }
1096     }
1097 }