BugSplat for .NET
Report crashes, hangs, and handled exceptions from .NET Framework and .NET 10 applications on Windows with the BugSplat NuGet package.
Overview 👀
BugSplat for .NET adds crash reporting to .NET Framework 4.7.2+ and .NET 10+ applications on Windows. It's installed from one NuGet package, BugSplat, whose library, BugSplatDotNet.dll, is built for both runtimes from the same source, with the same API. It's built on the BugSplat native SDK: every report is a minidump that BugSplat symbolicates from the symbols you upload, so call stacks show function names, file names, and line numbers for both managed (C#) and native (C++) frames, without shipping .pdb files with your application.
The package contains both builds, and NuGet picks the one for your target framework. Everything else in this guide (the native runtime you ship, initialization, handled exceptions, Windows Error Reporting, and symbols) is the same for both. Where a runtime behaves differently, a note says so.
BugSplat captures crashes in two ways, and a complete integration needs both:
The application exception handler. Creating a
BugSplatinstance installs it in your process. It catches unhandled managed exceptions on any thread, including the WPF dispatcher, and crashes in native code your application calls, such as access violations.Windows Error Reporting (WER). Some crashes end the process without running any of the application's exception handlers, and Windows hands them to WER instead. That includes heap corruption,
__fastfail,/GSfailures, fail-fasts from the .NET 10 runtime, and every unhandled exception in a WinUI 3 app. BugSplat captures these with its WER helper,BugSplatWer.dll, which Windows loads only after your installer registers it.
Either way, BugSplatMonitor.exe writes the minidump from outside your process and uploads it. BugSplat reports:
Unhandled managed exceptions, on any thread (including the WPF dispatcher)
Application exception handler → minidump
Crashes in native code called from managed code (P/Invoke), including access violations
Application exception handler → minidump, with one mixed C#/C++ call stack
Handled exceptions you choose to report
BugSplat.Post(exception) → minidump; your app keeps running
Application hangs (apps with a window)
Hang detection → minidump of the hung process
Fail-fast crashes: heap corruption, __fastfail, /GS failures, and on .NET 10 runtime fail-fasts and unhandled exceptions in WinUI 3 apps
Windows Error Reporting → BugSplatWer.dll → minidump (requires a registry entry; see Windows Error Reporting)
User feedback
BugSplat.PostFeedback(...)
Crashes and handled exceptions alike are reported as minidumps with BugSplat's .NET crash type, so the managed frames are named from your symbols. To rebrand or reword the crash dialog, see Customize the Crash Dialog.
Supported Versions
.NET Framework 4.7.2 and later
Supported, with the net472 build.
.NET 10 and later
Supported, with the net10.0 build.
.NET 5 through 9, and .NET Core
Not supported.
BugSplat for .NET runs on Windows only; Linux and macOS aren't supported. It loads the native BugSplat.dll, which must match the architecture your application runs as. The NuGet package includes the native runtime for x64, x86, and ARM64 and copies the matching one; the SDK download includes x64 only.
Getting Started 🚦
Install the BugSplat NuGet package:
or search for BugSplat in Visual Studio's NuGet Package Manager. The same package works for .NET Framework 4.7.2+ and .NET 10+ projects.
To get a feel for BugSplat before integrating it, clone my-dotnet-crasher and try one of the samples, which install BugSplat from NuGet: MyDotNetFrameworkWpfCrasher for .NET Framework, or MyDotNetWinUI3Crasher and MyDotNetCrasher for .NET 10. The samples are also in the SDK download.
Integration 🏗️
Add the
BugSplatNuGet package, as shown in Getting Started above. It referencesBugSplatDotNet.dllfor your target framework and brings BugSplat's native runtime with it.Ship the native runtime next to your executable.
BugSplat.dllstartsBugSplatMonitor.exefrom your application's directory to capture crashes, and the monitor startsBugSplatReporter.exeto show the crash dialog and upload the report, soBugSplat.dll,BugSplatMonitor.exe,BugSplatReporter.exe, andBugSplatWer.dllmust be installed alongside your.exe. The package copies them to your build anddotnet publishoutput as content files; make sure your installer includes them.Register
BugSplatWer.dllwith Windows Error Reporting from your installer. Crashes that go through WER aren't reported without it, and for a WinUI 3 application that's every crash. See Windows Error Reporting below for the registry entry.Initialize BugSplat once, as early as possible (at the top of
MainorProgram.cs, or in yourAppconstructor), and keep the instance for the life of the process:The database is created on the Manage Database page in Settings. Creating the instance installs the application exception handler and hang detection, so you don't need a handler of your own for unhandled exceptions, including WPF dispatcher exceptions and exceptions on background threads. Crashes that bypass the application's exception handlers are captured through the WER registration from step 3.
Report handled exceptions by calling
Postfrom inside thecatchblock:Postwrites a minidump of the exception being handled and uploads it; your application keeps running. Call it inside thecatch, while the frames of the code that threw are still on the stack, so the report shows where the exception came from. Called anywhere else, there is no exception in flight andPostreturnsfalsewithout reporting anything. It blocks while the report is written and uploaded.Upload symbols for every build you ship, so call stacks show function names, file names, and line numbers. See Symbols below.
Test your integration by forcing a crash with the application running outside the Visual Studio debugger (Ctrl+F5, or
dotnet run); the debugger intercepts the exceptions BugSplat would report. Verify that symbols were uploaded on the Versions page and that the crash appears on the Crashes page with a symbolicated call stack.BugSplat.IsWerEnabledtells you whether the WER registration from step 3 is in place.
Using the SDK Download Instead of NuGet
If you can't use NuGet, you can add BugSplat from the SDK download instead. Its sample projects show how to wire everything up by hand. In place of steps 1 and 2 above:
Reference
BugSplatDotNet.dllfromBugSplat\dotnet\Release\net472(.NET Framework) ornet10.0(.NET 10).Build your application for x64, since the download's native runtime is x64 only.
Copy
BugSplat.dll,BugSplatMonitor.exe,BugSplatReporter.exe, andBugSplatWer.dllfromBugSplat\x64\Release\binnext to your executable, and include them in your installer. Copy theBugSplatThemefolder too if you customize the crash dialog.
Customize the Crash Dialog
The crash dialog's colors, fonts, layout, logo, and wording come from a theme folder that BugSplatReporter.exe reads at run time. To ship your own, add a folder named BugSplatTheme next to your project file, holding theme.json, strings.en-US.json, and any logo or translations, and write only the values you're changing:
The package copies the folder's contents to BugSplatTheme\ in your build output and dotnet publish folder, next to BugSplatReporter.exe, and keeps it a loose folder in a single-file publish. Without a BugSplatTheme folder, the dialog uses its built-in defaults.
To keep the theme elsewhere, set
<BugSplatThemeDirectory>to its path, relative to the project file. If that folder doesn't exist, the build fails withBSTHEME000.Every build on Windows checks the theme with
BugSplatReporter.exe --check-themeand reports each value the dialog would ignore as a warning,BSTHEME001toBSTHEME005. Add a code to<NoWarn>to silence it, or set<BugSplatCheckTheme>false</BugSplatCheckTheme>to skip the check.A library that sets
<BugSplatCopyNativeFiles>true</BugSplatCopyNativeFiles>gets the theme in its own output'sBugSplatTheme\folder; deploy it next to the host's executable with the native files. If you copy BugSplat's files to a host yourself, copy your theme folder there asBugSplatTheme\too.
Links in the dialog's text work only for domains you allow in code:
See Crash Dialog Branding for every setting, how to preview a theme, and the rules for links.
Symbols
BugSplat symbolicates your crashes from the symbol files you upload, which is why your application doesn't need to ship its .pdb files. After each build, upload every .exe, .dll, and .pdb your application ships with symbol-upload, using the same database, application name, and version you pass to new BugSplat(...). That includes BugSplatDotNet.pdb, BugSplat's native PDBs, and the native PDBs of any C++ libraries you call, so mixed C#/C++ call stacks are symbolicated on both sides.
symbol-upload authenticates with a Client ID and Client Secret, which you create on the Integrations page. The samples run it after every build with a Scripts\SymbolUpload.ps1 post-build step, which reads the credentials from Scripts\env.ps1 or the BUGSPLAT_CLIENT_ID and BUGSPLAT_CLIENT_SECRET environment variables. Building with /p:BugSplatSymbolUpload=false skips it, and in the .NET 10 samples a failed upload fails the build. Copy the script into your own project, or run symbol-upload from your build pipeline.
Windows Error Reporting
Some crashes end the process without running any of the application's exception handlers: Windows fail-fasts the process straight through Windows Error Reporting (WER). This happens for heap corruption (for example, a native library freeing the same memory twice), __fastfail, and /GS stack-cookie failures, as well as the runtime fail-fasts and WinUI 3 crashes described below. WER is the only way BugSplat can capture these crashes. It does so through its WER helper, BugSplatWer.dll, which Windows loads only when its full path is listed in the registry. Your installer must create the entry, with administrator rights:
BugSplat.IsWerEnabled tells you at run time whether the entry is in place. See the upgrade guide for more about the registry entry.
Mixed C#/C++ Crashes
When managed code calls native code through P/Invoke and the native code crashes, BugSplat reports one call stack that interleaves the C# and C++ frames, symbolicated from both the managed and the native PDBs you upload. This covers native code that faults when called from C#, native code that calls back into C# code that throws, and crashes on native threads with no managed frames at all. The MyDotNetCrasher sample's native-* and cpp-throw modes demonstrate each of these, and the Mixed-Mode buttons of the WPF and WinUI 3 samples show the simplest case.
Limitations
Windows only. The NuGet package supports x64, x86, and ARM64; the SDK download supports x64 only.
Start your application with its
.exe.dotnet YourApp.dllruns your application insidedotnet.exe, and BugSplat looks forBugSplatMonitor.exenext todotnet.exeinstead of your application.Crashes that go through WER aren't reported without the registry entry. That includes heap corruption,
__fastfail,/GSfailures, .NET 10 runtime fail-fasts, and every WinUI 3 crash (see Windows Error Reporting).Unobserved task exceptions aren't reported. They don't crash the process, so there's no crash to capture. Observe your tasks and report failures with
Postfrom acatch..NET Framework: stack overflows in managed code aren't reported. The .NET Framework ends the process on a stack overflow without running any application exception handler, and reports it through its own
CLR20r3WER event, which doesn't callBugSplatWer.dlleven when it's registered..NET 10: uncaught C++ exceptions thrown from native code called through P/Invoke are reported from the point where the .NET runtime re-raises them, rather than from the C++
throw.
Native Applications That Host .NET
If your application is a native C++ program that loads the .NET Framework and calls into managed code, integrate the native SDK, BugSplat for Windows (C++), in the host program instead of the BugSplat package, and call SetCrashType(8) so BugSplat resolves the managed frames in its minidumps. Crashes in the managed code are captured by the native SDK's handlers, and the call stack shows the managed frames on top of your native ones. The MyDotNetFrameworkHostCrasher sample demonstrates the setup.
API Reference 📖
BugSplat(string database, string application, string version)
Initializes crash reporting. Create one instance, early, and keep it for the life of the process.
bool Post(Exception exception)
Reports a handled exception as a minidump and uploads it. Call it inside the catch block. Returns whether the report was uploaded.
FeedbackResult PostFeedback(string title, string? description = null, string[]? attachments = null)
Posts user feedback. The result's Success, CrashId, and InfoUrl describe the report. It blocks while the report is uploaded, so call it off the UI thread.
string User, Email, Key, Description, Notes
Default values stamped on every report.
void SetAttribute(string name, string value)
Adds a custom attribute to every report.
bool AddAttachment(string filePath)
Attaches a file to every report.
bool QuietMode
Set to true to suppress the crash dialog; reports are still uploaded.
string[] CrashDialogLinkDomains
The domains, and their subdomains, that links in the crash dialog's text may open, for example new[] { "example.com" }. Only https links work. By default no domain is allowed, and links are shown as plain text. See Links in the Dialog.
MiniDumpType MiniDumpType
The minidump type for crash reports and Post. The default, MiniDumpType.Normal, is enough for BugSplat to name the managed frames. Larger types, such as WithPrivateReadWriteMemory or WithFullMemory, are written only when full memory dumps are enabled for your database; otherwise BugSplat writes a normal minidump.
bool IsWerEnabled
Whether BugSplatWer.dll is registered with Windows Error Reporting.
bool IsInitialized
Whether crash reporting is installed for this process.
Upgrading From the Legacy .NET Framework SDK
The previous .NET Framework SDK (BugSplat.CrashReporter) has been replaced by BugSplat for .NET. To upgrade:
BugSplat.CrashReporter.Init(database, app, version)
new BugSplat(database, app, version)
Subscribing AppDomainUnhandledExceptionHandler, DispatcherUnhandledExceptionHandler, and TaskSchedulerUnobservedTaskExceptionHandler
Nothing: unhandled exceptions are captured by the constructor
CrashReporter.createReport(exception) for handled exceptions
bugsplat.Post(exception), inside the catch
Referencing BugSplatDotNet.dll from the SDK download
The BugSplat NuGet package
Shipping BsSndRpt.exe, BugSplatDotNet.dll, and BugSplatRc.dll
Shipping BugSplatDotNet.dll plus BugSplat.dll, BugSplatMonitor.exe, BugSplatReporter.exe, and BugSplatWer.dll, which the NuGet package copies to your output
Editing BugSplatRc.dll to change the crash dialog
A BugSplatTheme folder in your project
Any CPU
Any architecture with the NuGet package, which copies the matching native runtime; x64 with the SDK download
SendPdbs
Sample Apps
.NET Framework 4.7.2, hosted by C++
A native C++ program that crashes in the C# code it calls
Last updated
Was this helpful?
