diff --git a/Core/InteropServices/SafeIDispatch.cs b/Core/InteropServices/SafeIDispatch.cs
new file mode 100644
index 00000000..9c288cd9
--- /dev/null
+++ b/Core/InteropServices/SafeIDispatch.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Vanara.InteropServices
+{
+ /// Provide a IDispatch-based (e.g. late-bound) access to a COM object. Use to work with the object.
+ public class SafeIDispatch : ComReleaser
+ {
+ /// Initializes a new instance of the class.
+ /// The target object which must be a raw COM object.
+ public SafeIDispatch(object target) : base(target)
+ {
+ if (!Marshal.IsComObject(target))
+ throw new ArgumentException("The target object must be a COM object");
+ RawPointer = GetRawPointer(target);
+ }
+
+ /// Gets the pointer to the IDispatch instance.
+ /// A pointer to the IDispatch instance.
+ public IntPtr RawPointer { get; }
+
+ /// Determines whether the specified , is equal to this instance.
+ /// The to compare with this instance.
+ /// if the specified is equal to this instance; otherwise, .
+ public bool Equals(ComReleaser other) => other?.Item is null || Item is null ? false : (bool)(RawPointer == GetRawPointer(other.Item));
+
+ /// Determines whether the specified , is equal to this instance.
+ /// The to compare with this instance.
+ /// if the specified is equal to this instance; otherwise, .
+ public override bool Equals(object obj) => obj is ComReleaser o ? Equals(o) : base.Equals(obj);
+
+ /// Returns a hash code for this instance.
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ public override int GetHashCode() => RawPointer.ToInt32();
+
+ ///
+ /// Method to encapsulate operations against the late-bound COM object. The caller must handle any exceptions that may result as part
+ /// of the operation.
+ ///
+ /// A method that performs work against the late-bound COM object.
+ public void Invoke(Action action) => action.Invoke(Item);
+
+ private static IntPtr GetRawPointer(object target)
+ {
+ var result = IntPtr.Zero;
+ try
+ {
+ result = Marshal.GetIDispatchForObject(target);
+ }
+ finally
+ {
+ // Decrement reference count added by call to GetIDispatchForObject
+ if (result != IntPtr.Zero)
+ Marshal.Release(result);
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file