diff --git a/Dalamud/Utility/DisposeSafety.cs b/Dalamud/Utility/DisposeSafety.cs
new file mode 100644
index 000000000..909c4e932
--- /dev/null
+++ b/Dalamud/Utility/DisposeSafety.cs
@@ -0,0 +1,392 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Reactive.Disposables;
+using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+
+namespace Dalamud.Utility;
+
+///
+/// Utilities for disposing stuff.
+///
+public static class DisposeSafety
+{
+ ///
+ /// Interface that marks a disposable that it can call back on dispose.
+ ///
+ public interface IDisposeCallback : IDisposable
+ {
+ ///
+ /// Event to be fired before object dispose. First parameter is the object iself.
+ ///
+ event Action? BeforeDispose;
+
+ ///
+ /// Event to be fired after object dispose. First parameter is the object iself.
+ ///
+ event Action? AfterDispose;
+ }
+
+ ///
+ /// Returns a proxy that on dispose will dispose the result of the given
+ /// .
+ /// If any exception has occurred, it will be ignored.
+ ///
+ /// The task.
+ /// A disposable type.
+ /// The proxy .
+ public static IDisposable ToDisposableIgnoreExceptions(this Task task)
+ where T : IDisposable
+ {
+ return Disposable.Create(() => task.ContinueWith(r =>
+ {
+ _ = r.Exception;
+ if (r.IsCompleted)
+ {
+ try
+ {
+ r.Dispose();
+ }
+ catch
+ {
+ // ignore
+ }
+ }
+ }));
+ }
+
+ ///
+ /// Transforms into a , disposing the content as necessary.
+ ///
+ /// The task.
+ /// Ignore all exceptions.
+ /// A disposable type.
+ /// A wrapper for the task.
+ public static Task ToContentDisposedTask(this Task task, bool ignoreAllExceptions = false)
+ where T : IDisposable => task.ContinueWith(
+ r =>
+ {
+ if (!r.IsCompletedSuccessfully)
+ return ignoreAllExceptions ? Task.CompletedTask : r;
+ try
+ {
+ r.Result.Dispose();
+ }
+ catch (Exception e)
+ {
+ if (!ignoreAllExceptions)
+ {
+ return Task.FromException(
+ new AggregateException(
+ new[] { e }.Concat(
+ (IEnumerable)r.Exception?.InnerExceptions
+ ?? new[] { new OperationCanceledException() })));
+ }
+ }
+
+ return Task.CompletedTask;
+ }).Unwrap();
+
+ ///
+ /// Returns a proxy that on dispose will dispose all the elements of the given
+ /// of s.
+ ///
+ /// The disposables.
+ /// The disposable types.
+ /// The proxy .
+ /// Error.
+ public static IDisposable AggregateToDisposable(this IEnumerable? disposables)
+ where T : IDisposable
+ {
+ if (disposables is not T[] array)
+ array = disposables?.ToArray() ?? Array.Empty();
+
+ return Disposable.Create(() =>
+ {
+ List exceptions = null;
+ foreach (var d in array)
+ {
+ try
+ {
+ d?.Dispose();
+ }
+ catch (Exception de)
+ {
+ exceptions ??= new();
+ exceptions.Add(de);
+ }
+ }
+
+ if (exceptions is not null)
+ throw new AggregateException(exceptions);
+ });
+ }
+
+ ///
+ /// Utility class for managing finalizing stuff.
+ ///
+ public class ScopedFinalizer : IDisposeCallback, IAsyncDisposable
+ {
+ private readonly List