diff --git a/Dalamud/Hooking/AsmHook.cs b/Dalamud/Hooking/AsmHook.cs index 4ee96bb154..f1ed7fd117 100644 --- a/Dalamud/Hooking/AsmHook.cs +++ b/Dalamud/Hooking/AsmHook.cs @@ -20,6 +20,8 @@ public sealed class AsmHook : IDisposable, IDalamudHook private bool isEnabled = false; private DynamicMethod statsMethod; + + private Guid hookId = Guid.NewGuid(); /// /// Initializes a new instance of the class. @@ -44,7 +46,7 @@ public AsmHook(IntPtr address, byte[] assembly, string name, AsmHookBehaviour as this.statsMethod.GetILGenerator().Emit(OpCodes.Ret); var dele = this.statsMethod.CreateDelegate(typeof(Action)); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, dele, Assembly.GetCallingAssembly())); + HookManager.TrackedHooks.TryAdd(this.hookId, new HookInfo(this, dele, Assembly.GetCallingAssembly())); } /// @@ -70,7 +72,7 @@ public AsmHook(IntPtr address, string[] assembly, string name, AsmHookBehaviour this.statsMethod.GetILGenerator().Emit(OpCodes.Ret); var dele = this.statsMethod.CreateDelegate(typeof(Action)); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, dele, Assembly.GetCallingAssembly())); + HookManager.TrackedHooks.TryAdd(this.hookId, new HookInfo(this, dele, Assembly.GetCallingAssembly())); } /// @@ -116,6 +118,8 @@ public void Dispose() this.IsDisposed = true; + HookManager.TrackedHooks.TryRemove(this.hookId, out _); + if (this.isEnabled) { this.isEnabled = false; diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs index da65fedc78..20796bbf06 100644 --- a/Dalamud/Hooking/Hook.cs +++ b/Dalamud/Hooking/Hook.cs @@ -71,6 +71,11 @@ public T OriginalDisposeSafe /// public virtual string BackendName => throw new NotImplementedException(); + /// + /// Gets the unique GUID for this hook. + /// + protected Guid HookId { get; } = Guid.NewGuid(); + /// /// Remove a hook from the current process. /// diff --git a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs index 7a177206f3..40a33fc1b5 100644 --- a/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs +++ b/Dalamud/Hooking/Internal/FunctionPointerVariableHook.cs @@ -100,7 +100,7 @@ internal FunctionPointerVariableHook(IntPtr address, T detour, Assembly callingA unhooker.TrimAfterHook(); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly)); + HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly)); } } @@ -137,6 +137,8 @@ public override void Dispose() this.Disable(); + HookManager.TrackedHooks.TryRemove(this.HookId, out _); + var index = HookManager.MultiHookTracker[this.Address].IndexOf(this); HookManager.MultiHookTracker[this.Address][index] = null; diff --git a/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs index 1138d4e070..5bf7551c3d 100644 --- a/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs +++ b/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.Diagnostics; +using System.Diagnostics; using System.Linq; using Dalamud.Game; @@ -7,6 +6,7 @@ using Dalamud.IoC.Internal; using Dalamud.Plugin.Internal.Types; using Dalamud.Plugin.Services; +using Dalamud.Utility; using Dalamud.Utility.Signatures; using Serilog; @@ -26,7 +26,7 @@ internal class GameInteropProviderPluginScoped : IGameInteropProvider, IInternal private readonly LocalPlugin plugin; private readonly SigScanner scanner; - private readonly ConcurrentBag trackedHooks = new(); + private readonly WeakConcurrentCollection trackedHooks = new(); /// /// Initializes a new instance of the class. diff --git a/Dalamud/Hooking/Internal/MinHookHook.cs b/Dalamud/Hooking/Internal/MinHookHook.cs index 4cebca0d0e..0305f3c84b 100644 --- a/Dalamud/Hooking/Internal/MinHookHook.cs +++ b/Dalamud/Hooking/Internal/MinHookHook.cs @@ -35,7 +35,7 @@ internal MinHookHook(IntPtr address, T detour, Assembly callingAssembly) unhooker.TrimAfterHook(); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly)); + HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly)); } } @@ -76,6 +76,8 @@ public override void Dispose() HookManager.MultiHookTracker[this.Address][index] = null; } + HookManager.TrackedHooks.TryRemove(this.HookId, out _); + base.Dispose(); } diff --git a/Dalamud/Hooking/Internal/ReloadedHook.cs b/Dalamud/Hooking/Internal/ReloadedHook.cs index bf441a86da..2b0a4e9ce3 100644 --- a/Dalamud/Hooking/Internal/ReloadedHook.cs +++ b/Dalamud/Hooking/Internal/ReloadedHook.cs @@ -30,7 +30,7 @@ internal ReloadedHook(IntPtr address, T detour, Assembly callingAssembly) unhooker.TrimAfterHook(); - HookManager.TrackedHooks.TryAdd(Guid.NewGuid(), new HookInfo(this, detour, callingAssembly)); + HookManager.TrackedHooks.TryAdd(this.HookId, new HookInfo(this, detour, callingAssembly)); } } @@ -63,6 +63,8 @@ public override void Dispose() if (this.IsDisposed) return; + HookManager.TrackedHooks.TryRemove(this.HookId, out _); + this.Disable(); base.Dispose(); diff --git a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs index eac3b18628..e896b4599f 100644 --- a/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs +++ b/Dalamud/Interface/Internal/Windows/PluginStatWindow.cs @@ -258,8 +258,6 @@ public override void Draw() ImGui.EndTabItem(); } - var toRemove = new List(); - if (ImGui.BeginTabItem("Hooks")) { ImGui.Checkbox("Show Dalamud Hooks", ref this.showDalamudHooks); @@ -291,9 +289,6 @@ public override void Draw() { try { - if (trackedHook.Hook.IsDisposed) - toRemove.Add(guid); - if (!this.showDalamudHooks && trackedHook.Assembly == Assembly.GetExecutingAssembly()) continue; @@ -355,14 +350,6 @@ public override void Draw() } } - if (ImGui.IsWindowAppearing()) - { - foreach (var guid in toRemove) - { - HookManager.TrackedHooks.TryRemove(guid, out _); - } - } - ImGui.EndTabBar(); } } diff --git a/Dalamud/Utility/WeakConcurrentCollection.cs b/Dalamud/Utility/WeakConcurrentCollection.cs new file mode 100644 index 0000000000..69da125833 --- /dev/null +++ b/Dalamud/Utility/WeakConcurrentCollection.cs @@ -0,0 +1,44 @@ +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace Dalamud.Utility; + +/// +/// An implementation of a weak concurrent set based on a . +/// +/// The type of object that we're tracking. +public class WeakConcurrentCollection : ICollection where T : class +{ + private readonly ConditionalWeakTable cwt = new(); + + /// + public int Count => this.cwt.Count(); + + /// + public bool IsReadOnly => false; + + private HashSet Keys => this.cwt.Select(pair => pair.Key).ToHashSet(); + + /// + public IEnumerator GetEnumerator() => this.cwt.Select(pair => pair.Key).GetEnumerator(); + + /// + IEnumerator IEnumerable.GetEnumerator() => this.cwt.Select(pair => pair.Key).GetEnumerator(); + + /// + public void Add(T item) => this.cwt.AddOrUpdate(item, null); + + /// + public void Clear() => this.cwt.Clear(); + + /// + public bool Contains(T item) => this.Keys.Contains(item); + + /// + public void CopyTo(T[] array, int arrayIndex) => this.Keys.CopyTo(array, arrayIndex); + + /// + public bool Remove(T item) => this.cwt.Remove(item); +}