source: trunk/eraser6/Eraser.Util/BlackBox.cs @ 1823

Revision 1823, 23.3 KB checked in by lowjoel, 4 years ago (diff)

Fixed ArgumentOutOfRangeException? when uploading crash reports.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25
26using System.Windows.Forms;
27using System.IO;
28using System.Runtime.InteropServices;
29using System.Diagnostics;
30using System.Reflection;
31using Microsoft.Win32.SafeHandles;
32
33using System.Drawing;
34using System.Drawing.Imaging;
35using System.Collections.ObjectModel;
36using System.Globalization;
37
38using ICSharpCode.SharpZipLib.Tar;
39using ICSharpCode.SharpZipLib.BZip2;
40using System.Net;
41using System.Xml;
42
43namespace Eraser.Util
44{
45    /// <summary>
46    /// Handles application exceptions, stores minidumps and uploads them to the
47    /// Eraser server.
48    /// </summary>
49    public class BlackBox
50    {
51        /// <summary>
52        /// Initialises the BlackBox handler. Call this initialiser once throughout
53        /// the lifespan of the application.
54        /// </summary>
55        /// <returns>The global BlackBox instance.</returns>
56        public static BlackBox Get()
57        {
58            if (Instance == null)
59                Instance = new BlackBox();
60            return Instance;
61        }
62
63        /// <summary>
64        /// Creates a new BlackBox report based on the exception provided.
65        /// </summary>
66        /// <param name="e">The exception which triggered this dump.</param>
67        public void CreateReport(Exception e)
68        {
69            if (e == null)
70                throw new ArgumentNullException("e");
71
72            //Generate a unique identifier for this report.
73            string crashName = DateTime.Now.ToUniversalTime().ToString(
74                CrashReportName, CultureInfo.InvariantCulture);
75            string currentCrashReport = Path.Combine(CrashReportsPath, crashName);
76            Directory.CreateDirectory(currentCrashReport);
77
78            //Store the steps which we have completed.
79            int currentStep = 0;
80
81            try
82            {
83                //First, write a user-readable summary
84                WriteDebugLog(currentCrashReport, e);
85                ++currentStep;
86
87                //Take a screenshot
88                WriteScreenshot(currentCrashReport);
89                ++currentStep;
90
91                //Write a memory dump to the folder
92                WriteMemoryDump(currentCrashReport, e);
93                ++currentStep;
94            }
95            catch
96            {
97                //If an exception was caught while creating the report, we should just
98                //abort as that may cause a cascade. However, we need to remove the
99                //report folder if the crash report is empty.
100                if (currentStep == 0)
101                    Directory.Delete(currentCrashReport);
102            }
103        }
104
105        /// <summary>
106        /// Enumerates the list of crash dumps waiting for upload.
107        /// </summary>
108        /// <returns>A string array containing the list of dumps waiting for upload.</returns>
109        public BlackBoxReport[] GetDumps()
110        {
111            DirectoryInfo dirInfo = new DirectoryInfo(CrashReportsPath);
112            List<BlackBoxReport> result = new List<BlackBoxReport>();
113            if (dirInfo.Exists)
114                foreach (DirectoryInfo subDir in dirInfo.GetDirectories())
115                    try
116                    {
117                        result.Add(new BlackBoxReport(Path.Combine(CrashReportsPath, subDir.Name)));
118                    }
119                    catch (InvalidDataException)
120                    {
121                        //Do nothing: invalid reports are automatically deleted.
122                    }
123
124            return result.ToArray();
125        }
126
127        /// <summary>
128        /// Constructor. Use the <see cref="Initialise"/> function to use this class.
129        /// </summary>
130        private BlackBox()
131        {
132            //If we have a debugger attached we shouldn't bother with exceptions.
133            if (Debugger.IsAttached)
134                return;
135
136            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
137            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
138        }
139
140        /// <summary>
141        /// Called when an unhandled exception is raised in the application.
142        /// </summary>
143        private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
144        {
145            CreateReport(e.ExceptionObject as Exception);
146        }
147
148        /// <summary>
149        /// Dumps the contents of memory to a dumpfile.
150        /// </summary>
151        /// <param name="dumpFolder">Path to the folder to store the dump file.</param>
152        /// <param name="e">The exception which is being handled.</param>
153        private void WriteMemoryDump(string dumpFolder, Exception e)
154        {
155            //Open a file stream
156            using (FileStream stream = new FileStream(Path.Combine(dumpFolder, MemoryDumpFileName),
157                FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
158            {
159                //Store the exception information
160                NativeMethods.MiniDumpExceptionInfo exception =
161                    new NativeMethods.MiniDumpExceptionInfo();
162                exception.ClientPointers = false;
163                exception.ExceptionPointers = Marshal.GetExceptionPointers();
164                exception.ThreadId = (uint)AppDomain.GetCurrentThreadId();
165
166                NativeMethods.MiniDumpWriteDump(Process.GetCurrentProcess().Handle,
167                    (uint)Process.GetCurrentProcess().Id, stream.SafeFileHandle,
168                    NativeMethods.MiniDumpType.MiniDumpWithFullMemory,
169                    ref exception, IntPtr.Zero, IntPtr.Zero);
170            }
171        }
172
173        /// <summary>
174        /// Writes a debug log to the given directory.
175        /// </summary>
176        /// <param name="screenshotPath">The path to store the screenshot into.</param>
177        /// <param name="exception">The exception to log about.</param>
178        private void WriteDebugLog(string dumpFolder, Exception exception)
179        {
180            using (FileStream file = new FileStream(Path.Combine(dumpFolder, DebugLogFileName),
181                FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
182            using (StreamWriter stream = new StreamWriter(file))
183            {
184                //Application information
185                string separator = new string('-', 76);
186                string lineFormat = "{0,15}: {1}";
187                stream.WriteLine("Application Information");
188                stream.WriteLine(separator);
189                stream.WriteLine(string.Format(lineFormat, "Version",
190                    Assembly.GetEntryAssembly().GetName().Version));
191                StringBuilder commandLine = new StringBuilder();
192                foreach (string param in Environment.GetCommandLineArgs())
193                {
194                    commandLine.Append(param);
195                    commandLine.Append(' ');
196                }
197                stream.WriteLine(string.Format(lineFormat, "Command Line",
198                    commandLine.ToString().Trim()));
199
200                //Exception Information
201                stream.WriteLine();
202                stream.WriteLine("Exception Information (Outermost to innermost)");
203                stream.WriteLine(separator);
204
205                //Open a stream to the Stack Trace Log file. We want to separate the stack
206                //trace do we can check against the server to see if the crash is a new one
207                using (StreamWriter stackTraceLog = new StreamWriter(
208                    Path.Combine(dumpFolder, BlackBoxReport.StackTraceFileName)))
209                {
210                    Exception currentException = exception;
211                    for (uint i = 1; currentException != null; ++i)
212                    {
213                        stream.WriteLine(string.Format("Exception {0}:", i));
214                        stream.WriteLine(string.Format(lineFormat, "Message", currentException.Message));
215                        stream.WriteLine(string.Format(lineFormat, "Exception Type",
216                            currentException.GetType().FullName));
217                        stackTraceLog.WriteLine(string.Format("Exception {0}: {1}", i,
218                            currentException.GetType().FullName));
219
220                        //Parse the stack trace
221                        string[] stackTrace = currentException.StackTrace.Split(new char[] { '\n' });
222                        for (uint j = 0; j < stackTrace.Length; ++j)
223                        {
224                            stream.WriteLine(string.Format(lineFormat,
225                                string.Format("Stack Trace [{0}]", j), stackTrace[j].Trim()));
226                            stackTraceLog.WriteLine(string.Format("{0}", stackTrace[j].Trim()));
227                        }
228
229                        uint k = 0;
230                        foreach (System.Collections.DictionaryEntry value in currentException.Data)
231                            stream.WriteLine(string.Format(lineFormat, string.Format("Data[{0}]", ++k),
232                                string.Format("{0} {1}", value.Key.ToString(), value.Value.ToString())));
233
234                        //End the exception and get the inner exception.
235                        stream.WriteLine();
236                        currentException = currentException.InnerException;
237                    }
238                }
239            }
240        }
241
242        /// <summary>
243        /// Writes a screenshot to the given directory
244        /// </summary>
245        /// <param name="dumpFolder">The path to save the screenshot to.</param>
246        private void WriteScreenshot(string dumpFolder)
247        {
248            //Get the size of the screen
249            Rectangle rect = new Rectangle(int.MaxValue, int.MaxValue, int.MinValue, int.MinValue);
250            foreach (Screen screen in Screen.AllScreens)
251                rect = Rectangle.Union(rect, screen.Bounds);
252
253            //Copy a screen DC to the screenshot bitmap
254            Bitmap screenShot = new Bitmap(rect.Width, rect.Height);
255            Graphics bitmap = Graphics.FromImage(screenShot);
256            bitmap.CopyFromScreen(0, 0, 0, 0, rect.Size, CopyPixelOperation.SourceCopy);
257
258            //Save the bitmap to disk
259            screenShot.Save(Path.Combine(dumpFolder, ScreenshotFileName), ImageFormat.Png);
260        }
261
262        /// <summary>
263        /// The global BlackBox instance.
264        /// </summary>
265        private static BlackBox Instance;
266
267        /// <summary>
268        /// The path to all Eraser crash reports.
269        /// </summary>
270        private static readonly string CrashReportsPath = Path.Combine(Environment.GetFolderPath(
271            Environment.SpecialFolder.LocalApplicationData), @"Eraser 6\Crash Reports");
272
273        /// <summary>
274        /// The report name format.
275        /// </summary>
276        internal static readonly string CrashReportName = "yyyyMMdd HHmmss.FFF";
277
278        /// <summary>
279        /// The file name of the memory dump.
280        /// </summary>
281        ///
282        internal static readonly string MemoryDumpFileName = "Memory.dmp";
283
284        /// <summary>
285        /// The file name of the debug log.
286        /// </summary>
287        internal static readonly string DebugLogFileName = "Debug.log";
288
289        /// <summary>
290        /// The file name of the screenshot.
291        /// </summary>
292        internal static readonly string ScreenshotFileName = "Screenshot.png";
293    }
294
295    /// <summary>
296    /// Represents one BlackBox crash report.
297    /// </summary>
298    public class BlackBoxReport
299    {
300        /// <summary>
301        /// Constructor.
302        /// </summary>
303        /// <param name="path">Path to the folder containing the memory dump, screenshot and
304        /// debug log.</param>
305        internal BlackBoxReport(string path)
306        {
307            Path = path;
308
309            string stackTracePath = System.IO.Path.Combine(Path, StackTraceFileName);
310            if (!File.Exists(stackTracePath))
311            {
312                Delete();
313                throw new InvalidDataException("The BlackBox report is corrupt.");
314            }
315
316            string[] stackTrace = null;
317            using (StreamReader reader = new StreamReader(stackTracePath))
318                stackTrace = reader.ReadToEnd().Split(new char[] { '\n' });
319
320            //Parse the lines in the file.
321            StackTraceCache = new List<BlackBoxExceptionEntry>();
322            List<string> currentException = new List<string>();
323            string exceptionType = null;
324            foreach (string str in stackTrace)
325            {
326                if (str.StartsWith("Exception "))
327                {
328                    //Add the current exception to the list of exceptions.
329                    if (currentException.Count != 0)
330                    {
331                        StackTraceCache.Add(new BlackBoxExceptionEntry(exceptionType,
332                            new List<string>(currentException)));
333                        currentException.Clear();
334                    }
335
336                    //Set the exception type for the next exception.
337                    exceptionType = str.Substring(str.IndexOf(':') + 1).Trim();
338                }
339                else if (!string.IsNullOrEmpty(str.Trim()))
340                {
341                    currentException.Add(str.Trim());
342                }
343            }
344
345            if (currentException.Count != 0)
346                StackTraceCache.Add(new BlackBoxExceptionEntry(exceptionType, currentException));
347        }
348
349        /// <summary>
350        /// Deletes the report and its contents.
351        /// </summary>
352        public void Delete()
353        {
354            Directory.Delete(Path, true);
355        }
356
357        /// <summary>
358        /// The name of the report.
359        /// </summary>
360        public string Name
361        {
362            get
363            {
364                return System.IO.Path.GetFileName(Path);
365            }
366        }
367
368        /// <summary>
369        /// The timestamp of the report.
370        /// </summary>
371        public DateTime Timestamp
372        {
373            get
374            {
375                return DateTime.ParseExact(Name, BlackBox.CrashReportName,
376                    CultureInfo.InvariantCulture).ToLocalTime();
377            }
378        }
379
380        /// <summary>
381        /// The path to the folder containing the report.
382        /// </summary>
383        public string Path
384        {
385            get;
386            private set;
387        }
388
389        /// <summary>
390        /// The files which comprise the error report.
391        /// </summary>
392        public ReadOnlyCollection<FileInfo> Files
393        {
394            get
395            {
396                List<FileInfo> result = new List<FileInfo>();
397                DirectoryInfo directory = new DirectoryInfo(Path);
398                foreach (FileInfo file in directory.GetFiles())
399                    if (!InternalFiles.Contains(file.Name))
400                        result.Add(file);
401
402                return result.AsReadOnly();
403            }
404        }
405
406        /// <summary>
407        /// Gets a read-only stream which reads the Debug log.
408        /// </summary>
409        public Stream DebugLog
410        {
411            get
412            {
413                return new FileStream(System.IO.Path.Combine(Path, BlackBox.DebugLogFileName),
414                    FileMode.Open, FileAccess.Read, FileShare.Read);
415            }
416        }
417
418        /// <summary>
419        /// Gets the stack trace for this crash report.
420        /// </summary>
421        public ReadOnlyCollection<BlackBoxExceptionEntry> StackTrace
422        {
423            get
424            {
425                return StackTraceCache.AsReadOnly();
426            }
427        }
428
429        /// <summary>
430        /// Gets or sets whether the given report has been uploaded to the server.
431        /// </summary>
432        public bool Submitted
433        {
434            get
435            {
436                byte[] buffer = new byte[1];
437                using (FileStream stream = new FileStream(System.IO.Path.Combine(Path, StatusFileName),
438                    FileMode.OpenOrCreate, FileAccess.Read, FileShare.Read))
439                {
440                    stream.Read(buffer, 0, buffer.Length);
441                }
442
443                return buffer[0] == 1;
444            }
445
446            set
447            {
448                byte[] buffer = { Convert.ToByte(value) };
449                using (FileStream stream = new FileStream(System.IO.Path.Combine(Path, StatusFileName),
450                    FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read))
451                {
452                    stream.Write(buffer, 0, buffer.Length);
453                }
454            }
455        }
456
457        public override string ToString()
458        {
459            return Name;
460        }
461
462        /// <summary>
463        /// The backing variable for the <see cref="StackTrace"/> field.
464        /// </summary>
465        private List<BlackBoxExceptionEntry> StackTraceCache;
466
467        /// <summary>
468        /// The file name for the status file.
469        /// </summary>
470        private static readonly string StatusFileName = "Status.txt";
471
472        /// <summary>
473        /// The file name of the stack trace.
474        /// </summary>
475        internal static readonly string StackTraceFileName = "Stack Trace.log";
476
477        /// <summary>
478        /// The list of files internal to the report.
479        /// </summary>
480        private static readonly List<string> InternalFiles = new List<string>(
481            new string[] {
482                 StackTraceFileName,
483                 StatusFileName
484            }
485        );
486    }
487
488    /// <summary>
489    /// Represents one exception which can be chained <see cref="InnerException"/>
490    /// to represent the exception handled by BlackBox
491    /// </summary>
492    public class BlackBoxExceptionEntry
493    {
494        /// <summary>
495        /// Constructor.
496        /// </summary>
497        /// <param name="exceptionType">The type of the exception.</param>
498        /// <param name="stackTrace">The stack trace for this exception.</param>
499        internal BlackBoxExceptionEntry(string exceptionType, List<string> stackTrace)
500        {
501            ExceptionType = exceptionType;
502            StackTraceCache = stackTrace;
503        }
504
505        /// <summary>
506        /// The type of the exception.
507        /// </summary>
508        public string ExceptionType
509        {
510            get;
511            private set;
512        }
513
514        /// <summary>
515        /// The stack trace for this exception.
516        /// </summary>
517        public ReadOnlyCollection<string> StackTrace
518        {
519            get
520            {
521                return StackTraceCache.AsReadOnly();
522            }
523        }
524
525        /// <summary>
526        /// The backing variable for the <see cref="StackTrace"/> property.
527        /// </summary>
528        private List<string> StackTraceCache;
529    }
530
531    /// <summary>
532    /// Uploads <see cref="BlackBoxReport"/>s to the Eraser server.
533    /// </summary>
534    public class BlackBoxReportUploader
535    {
536        /// <summary>
537        /// Constructor.
538        /// </summary>
539        /// <param name="report">The report to upload.</param>
540        public BlackBoxReportUploader(BlackBoxReport report)
541        {
542            Report = report;
543            if (!Directory.Exists(UploadTempDir))
544                Directory.CreateDirectory(UploadTempDir);
545
546            ReportBaseName = Path.Combine(UploadTempDir, Report.Name);
547        }
548
549        /// <summary>
550        /// Gets from the server based on the stack trace whether this report is
551        /// new.
552        /// </summary>
553        public bool IsNew
554        {
555            get
556            {
557                PostDataBuilder builder = new PostDataBuilder();
558                builder.AddPart(new PostDataField("action", "status"));
559                AddStackTraceToRequest(Report.StackTrace, builder);
560
561                WebRequest reportRequest = HttpWebRequest.Create(BlackBoxServer);
562                reportRequest.ContentType = builder.ContentType;
563                reportRequest.Method = "POST";
564                using (Stream formStream = builder.Stream)
565                {
566                    reportRequest.ContentLength = formStream.Length;
567                    using (Stream requestStream = reportRequest.GetRequestStream())
568                    {
569                        int lastRead = 0;
570                        byte[] buffer = new byte[32768];
571                        while ((lastRead = formStream.Read(buffer, 0, buffer.Length)) != 0)
572                            requestStream.Write(buffer, 0, lastRead);
573                    }
574                }
575
576                try
577                {
578                    HttpWebResponse response = reportRequest.GetResponse() as HttpWebResponse;
579                    using (Stream responseStream = response.GetResponseStream())
580                    {
581                        XmlReader reader = XmlReader.Create(responseStream);
582                        reader.ReadToFollowing("crashReport");
583                        string reportStatus = reader.GetAttribute("status");
584                        switch (reportStatus)
585                        {
586                            case "exists":
587                                Report.Submitted = true;
588                                return false;
589
590                            case "new":
591                                return true;
592
593                            default:
594                                throw new InvalidDataException(
595                                    "Unknown crash report server response.");
596                        }
597                    }
598                }
599                catch (WebException e)
600                {
601                    using (Stream responseStream = e.Response.GetResponseStream())
602                    {
603                        try
604                        {
605                            XmlReader reader = XmlReader.Create(responseStream);
606                            reader.ReadToFollowing("error");
607                            throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture,
608                                "The server encountered a problem while processing the request: {0}",
609                                reader.ReadString()));
610                        }
611                        catch (XmlException)
612                        {
613                        }
614                    }
615
616                    throw new InvalidDataException(((HttpWebResponse)e.Response).StatusDescription);
617                }
618            }
619        }
620
621        /// <summary>
622        /// Compresses the report for uploading.
623        /// </summary>
624        /// <param name="progress">The <see cref="ProgressManager"/> instance that the
625        /// Upload function is using.</param>
626        /// <param name="progressChanged">The progress changed event handler that should
627        /// be called for upload progress updates.</param>
628        private void Compress(SteppedProgressManager progress,
629            ProgressChangedEventHandler progressChanged)
630        {
631            using (FileStream archiveStream = new FileStream(ReportBaseName + ".tar",
632                    FileMode.Create, FileAccess.Write))
633            {
634                //Add the report into a tar file
635                TarArchive archive = TarArchive.CreateOutputTarArchive(archiveStream);
636                foreach (FileInfo file in Report.Files)
637                {
638                    TarEntry entry = TarEntry.CreateEntryFromFile(file.FullName);
639                    entry.Name = Path.GetFileName(entry.Name);
640                    archive.WriteEntry(entry, false);
641                }
642                archive.Close();
643            }
644
645            ProgressManager step = new ProgressManager();
646            progress.Steps.Add(new SteppedProgressManagerStep(step, 0.5f, "Compressing"));
647            using (FileStream bzipFile = new FileStream(ReportBaseName + ".tbz",
648                FileMode.Create))
649            using (FileStream tarStream = new FileStream(ReportBaseName + ".tar",
650                FileMode.Open, FileAccess.Read, FileShare.Read, 262144, FileOptions.DeleteOnClose))
651            using (BZip2OutputStream bzipStream = new BZip2OutputStream(bzipFile, 262144))
652            {
653                //Compress the tar file
654                int lastRead = 0;
655                byte[] buffer = new byte[524288];
656                while ((lastRead = tarStream.Read(buffer, 0, buffer.Length)) != 0)
657                {
658                    bzipStream.Write(buffer, 0, lastRead);
659                    step.Total = tarStream.Length;
660                    step.Completed = tarStream.Position;
661
662                    if (progressChanged != null)
663                        progressChanged(this, new ProgressChangedEventArgs(progress, null));
664                }
665            }
666        }
667
668        /// <summary>
669        /// Compresses the report, then uploads it to the server.
670        /// </summary>
671        /// <param name="progressChanged">The progress changed event handler that should
672        /// be called for upload progress updates.</param>
673        public void Submit(ProgressChangedEventHandler progressChanged)
674        {
675            SteppedProgressManager overallProgress = new SteppedProgressManager();
676            Compress(overallProgress, progressChanged);
677
678            using (FileStream bzipFile = new FileStream(ReportBaseName + ".tbz",
679                FileMode.Open, FileAccess.Read, FileShare.Read, 131072, FileOptions.DeleteOnClose))
680            using (Stream logFile = Report.DebugLog)
681            {
682                //Build the POST request
683                PostDataBuilder builder = new PostDataBuilder();
684                builder.AddPart(new PostDataField("action", "upload"));
685                builder.AddPart(new PostDataFileField("crashReport", "Report.tbz", bzipFile));
686                AddStackTraceToRequest(Report.StackTrace, builder);
687
688                //Upload the POST request
689                WebRequest reportRequest = HttpWebRequest.Create(BlackBoxServer);
690                reportRequest.ContentType = builder.ContentType;
691                reportRequest.Method = "POST";
692                reportRequest.Timeout = int.MaxValue;
693                using (Stream formStream = builder.Stream)
694                {
695                    ProgressManager progress = new ProgressManager();
696                    overallProgress.Steps.Add(new SteppedProgressManagerStep(
697                        progress, 0.5f, "Uploading"));
698                    reportRequest.ContentLength = formStream.Length;
699
700                    using (Stream requestStream = reportRequest.GetRequestStream())
701                    {
702                        int lastRead = 0;
703                        byte[] buffer = new byte[32768];
704                        while ((lastRead = formStream.Read(buffer, 0, buffer.Length)) != 0)
705                        {
706                            requestStream.Write(buffer, 0, lastRead);
707
708                            progress.Total = formStream.Length;
709                            progress.Completed = formStream.Position;
710                            progressChanged(this, new ProgressChangedEventArgs(overallProgress, null));
711                        }
712                    }
713                }
714
715                try
716                {
717                    reportRequest.GetResponse();
718                    Report.Submitted = true;
719                }
720                catch (WebException e)
721                {
722                    using (Stream responseStream = e.Response.GetResponseStream())
723                    {
724                        try
725                        {
726                            XmlReader reader = XmlReader.Create(responseStream);
727                            reader.ReadToFollowing("error");
728                            throw new InvalidDataException(string.Format(CultureInfo.CurrentCulture,
729                                "The server encountered a problem while processing the request: {0}",
730                                reader.ReadString()));
731                        }
732                        catch (XmlException)
733                        {
734                        }
735                    }
736
737                    throw new InvalidDataException(((HttpWebResponse)e.Response).StatusDescription);
738                }
739            }
740        }
741
742        /// <summary>
743        /// Adds the stack trace to the given form request.
744        /// </summary>
745        /// <param name="stackTrace">The stack trace to add.</param>
746        /// <param name="builder">The Form request builder to add the stack trace to.</param>
747        private static void AddStackTraceToRequest(IList<BlackBoxExceptionEntry> stackTrace,
748            PostDataBuilder builder)
749        {
750            int exceptionIndex = 0;
751            foreach (BlackBoxExceptionEntry exceptionStack in stackTrace)
752            {
753                foreach (string stackFrame in exceptionStack.StackTrace)
754                    builder.AddPart(new PostDataField(
755                        string.Format("stackTrace[{0}][]", exceptionIndex), stackFrame));
756                builder.AddPart(new PostDataField(string.Format(
757                    "stackTrace[{0}][exception]", exceptionIndex), exceptionStack.ExceptionType));
758                ++exceptionIndex;
759            }
760        }
761
762        /// <summary>
763        /// The path to where the temporary files are stored before uploading.
764        /// </summary>
765        private static readonly string UploadTempDir =
766            Path.Combine(Path.GetTempPath(), "Eraser Crash Reports");
767
768        /// <summary>
769        /// The URI to the BlackBox server.
770        /// </summary>
771        private static readonly Uri BlackBoxServer =
772            new Uri("http://eraser.heidi.ie/scripts/blackbox/upload.php");
773
774        /// <summary>
775        /// The report being uploaded.
776        /// </summary>
777        private BlackBoxReport Report;
778
779        /// <summary>
780        /// The base name of the report.
781        /// </summary>
782        private readonly string ReportBaseName;
783    }
784}
Note: See TracBrowser for help on using the repository browser.