Why Automatic Crash Reporting is Essential for Modern Applications
When developers ship a new release, they usually perform dozens of test cycles, run static analysis, and rely on automated unit and integration tests to catch obvious problems. Yet, even after the code passes every test and satisfies customers, hidden bugs can surface in the wild. Real‑world usage patterns, unexpected input, and rare hardware conditions can trigger failures that are difficult to reproduce in a lab environment. In these moments, the information a user provides about the crash is often too vague to lead to a quick fix. Users may describe a “crash” or “error message” without knowing what exception type was thrown, which module was executing, or what data caused the fault. Consequently, developers waste time guessing, digging through logs that were not captured, or asking the user for additional details that may no longer be available because the application is closed.
Automatic bug report collection solves this problem by capturing the exact state of the program at the moment of failure and transmitting it directly to a support server. The collected data can include the stack trace, exception type, application version, operating system details, user configuration, and even memory dumps. With this level of detail, the support team can reproduce the scenario locally, identify the root cause, and verify the fix before sending an update back to the customer. Many large software vendors, such as Microsoft, have built such systems into their operating systems and product suites. The Windows Error Reporting feature, for example, sends crash data to Microsoft automatically, allowing them to fix regressions quickly.
There are two main classes of exceptions to consider. Continuable exceptions are errors that the program can recover from - think of a file-not-found error that can be handled by prompting the user to select a different file. Non‑continuable exceptions, such as access violations or stack overflows, are fatal; the program cannot safely continue and must terminate. For continuable errors, it is important to keep the user’s workflow uninterrupted. In these cases, the application should report the issue in the background while allowing the user to keep working. For non‑continuable errors, the user must be informed that the program is shutting down, and the crash report should be sent immediately to avoid losing data.
Designing an automatic reporting mechanism involves three key decisions: when to capture a crash, what data to send, and how to transmit it. The first decision is typically handled by intercepting the global exception handler. Most GUI frameworks expose a central exception hook that can be overridden; when an exception escapes the application’s code, this hook receives it and can decide how to process it. The second decision is about payload size and privacy. Sending a full memory dump may reveal sensitive data, so developers often choose a more lightweight approach - stack traces, environment variables, and user settings. The third decision is about the transport protocol. FTP has been a long‑standing choice for file uploads, but security concerns and corporate firewalls have pushed many developers toward HTTP or HTTPS. HTTP is more firewall‑friendly, and HTTPS adds encryption. In either case, the upload should be asynchronous to avoid blocking the UI thread, except when the application is about to terminate.
Because automatic reporting reduces support costs and improves the quality of the software, it has become a standard feature in many professional applications. The next section walks through a concrete implementation in Delphi, illustrating how to hook the global exception handler, package crash data, and upload it to a remote server without compromising the user experience.
Implementing Automatic Bug Reporting in Delphi: A Practical Guide
Delphi’s VCL framework exposes the Application.OnException event, which is called whenever an unhandled exception bubbles up to the main message loop. By assigning a custom handler to this event we can intercept all exceptions, collect diagnostic data, and start an upload to our support server. It is best practice to preserve the original handler so that default behavior - such as displaying a dialog or terminating the application - remains available if the developer chooses not to send a report.
The custom handler MyExceptionHandler receives the exception object. The first step is to decide whether the exception is continuable. In Delphi, the most common non‑continuable exception is EAccessViolation, but other low‑level errors (like stack overflows) are usually represented by EIntError or system exceptions. A simple predicate works for many cases:
Once we have this flag, we can build a diagnostic message. For continuable errors, we note that the application can keep running; for fatal errors, we mark the program as about to terminate. The message typically contains:
- Exception type and message
- Stack trace (available via
ExceptAddrandExceptFrame) - Application version and build number
- Operating system and architecture
- Any relevant user data (e.g., the current document name)
- A prompt asking the user if they wish to send the report
To bundle this data into a file that can be uploaded, we write it to a temporary file using a
TStringList. The Clever Components suite provides theTclUploadercomponent, which abstracts file transfer over FTP, HTTP, or HTTPS. The component exposes anUploadmethod that can run asynchronously when a continuable exception occurs. When the program is about to exit, the upload runs synchronously to guarantee that the report is transmitted before the process terminates.<strong>procedure TForm.MyExceptionHandler(Sender: TObject; E: Exception);</p> <p>var</p> <p> Msg: TStringList;</p> <p> IsCont: Boolean;</p> <p>begin</p> <p> IsCont := IsContinuable(E);</p> <p> Msg := TStringList.Create;</p> <p> try</p> <p> if IsCont then</p> <p> Msg.Add('Continuable exception: the application can continue.')</p> <p> else</p> <p> Msg.Add('Fatal exception: the application will terminate.');</p> <p> Msg.Add('Exception type: ' + E.ClassName);</p> <p> Msg.Add('Message: ' + E.Message);</p> <p> Msg.Add('Stack trace:');</p> <p> Msg.Add(ExceptAddr); // simplified; real code would build full trace</p> <p> Msg.Add('Application version: ' + Application.Title);</p> <p> Msg.Add('OS: ' + GetOSVersion); // placeholder function</p> <p> Msg.Add('Send this report? (Yes/No)');</p> <p> if MessageDlg(Msg.Text, mtInformation, [mbYes, mbNo], 0) = mrYes then</p> <p> begin</p> <p> Msg.SaveToFile(clUploader.LocalFile);</p> <p> clUploader.Start(IsCont);</p> <p> end;</p> <p> finally</p> <p> Msg.Free;</p> <p> end;</p> <p> if not IsCont then</p> <p> Halt;</p> <p>end;</strong>The
clUploadercomponent exposes an eventOnDataItemProceedthat can be used to give the user visual feedback on upload progress. Assigning a handler updates the form caption to show how many bytes have been sent. Because the component can handle both synchronous and asynchronous transfers, the same handler works for both scenarios.<strong>procedure TForm.clUploaderOnProgress(Sender: TObject; ResourceInfo: TclResourceInfo;</p> <p> StateItem: TclResourceStateItem; CurrentData: PChar; CurrentSize: Integer);</p> <p>begin</p> <p> Caption := Format('Uploading bug report: %d/%d bytes',</p> <p> [StateItem.BytesProceed, ResourceInfo.Size]);</p> <p>end;</strong>Testing the automatic reporter is straightforward. You can trigger a continuable exception by calling
raise Exception.Create('Test error');from a button click. For a non‑continuable error, deliberately access memory at address zero. In Delphi, you can emulate an access violation with inline assembly or by calling an invalid API. The following example uses inline assembly to read from address zero, which raisesEAccessViolationon Windows:<strong>procedure TForm.btnTestContinuableClick(Sender: TObject);</p> <p>begin</p> <p> raise Exception.Create('Test continuable exception');</p> <p>end;</p> <p>procedure TForm.btnTestFatalClick(Sender: TObject);</p> <p>begin</p> <p> asm</p> <p> xor eax, eax</p> <p> mov eax, [eax]</p> <p> end;</p> <p>end;</strong>When deploying the application, configure the
TclUploadercomponent’sURLproperty to point to your server. The component automatically detects whether the URL usesftp://,http://, orhttps://and sets up the appropriate protocol. If you opt for HTTP or HTTPS, ensure that your web server accepts file uploads and that the user’s credentials have permission to write to the target directory. For FTP, you need a user account with write access; for HTTPS, you may use an authenticated endpoint that accepts POST requests containing the file data.To help you get started, a ready‑to‑run example that demonstrates all the steps described above is available for download. The source code is built with Delphi 10.4 Sydney and includes the full implementation of the exception handler, the
TclUploadercomponent, and the test UI. Download the example here: Clever Internet Suite, which contains the necessary networking components for file transfer. Feel free to tweak the upload destination, add additional diagnostics, or integrate the reporting system into your own application architecture. By automating the collection and transmission of crash data, you give your users a smoother experience and empower your support team with the information they need to fix problems faster.





No comments yet. Be the first to comment!