mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-01-03 14:23:40 +01:00
Keep track of hooks and display them in the Plugin Stats window
This will make it easier to find plugins that are not correctly removing their hooks
This commit is contained in:
parent
f1f7803434
commit
850123aece
4 changed files with 130 additions and 6 deletions
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using EasyHook;
|
||||
|
|
@ -9,9 +10,7 @@ namespace Dalamud.Hooking {
|
|||
/// This class is basically a thin wrapper around the LocalHook type to provide helper functions.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Delegate type to represents a function prototype. This must be the same prototype as original function do.</typeparam>
|
||||
public sealed class Hook<T> : IDisposable where T : Delegate {
|
||||
private bool isDisposed;
|
||||
|
||||
public sealed class Hook<T> : IDisposable, IDalamudHook where T : Delegate {
|
||||
private readonly IntPtr address;
|
||||
|
||||
private readonly T original;
|
||||
|
|
@ -69,19 +68,20 @@ namespace Dalamud.Hooking {
|
|||
this.hookInfo = LocalHook.Create(address, detour, callbackParam); // Installs a hook here
|
||||
this.address = address;
|
||||
this.original = Marshal.GetDelegateForFunctionPointer<T>(this.hookInfo.HookBypassAddress);
|
||||
HookInfo.TrackedHooks.Add(new HookInfo() { Delegate = detour, Hook = this, Assembly = Assembly.GetCallingAssembly()});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a hook from the current process.
|
||||
/// </summary>
|
||||
public void Dispose() {
|
||||
if (this.isDisposed) {
|
||||
if (this.IsDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hookInfo.Dispose();
|
||||
|
||||
this.isDisposed = true;
|
||||
this.IsDisposed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -104,9 +104,24 @@ namespace Dalamud.Hooking {
|
|||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void CheckDisposed() {
|
||||
if (this.isDisposed) {
|
||||
if (this.IsDisposed) {
|
||||
throw new ObjectDisposedException("Hook is already disposed.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the hook is enabled.
|
||||
/// </summary>
|
||||
public bool IsEnabled {
|
||||
get {
|
||||
CheckDisposed();
|
||||
return this.hookInfo.ThreadACL.IsExclusive;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the hook has been disposed
|
||||
/// </summary>
|
||||
public bool IsDisposed { get; private set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
36
Dalamud/Hooking/HookInfo.cs
Normal file
36
Dalamud/Hooking/HookInfo.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Dalamud.Hooking {
|
||||
internal class HookInfo {
|
||||
|
||||
internal static List<HookInfo> TrackedHooks = new List<HookInfo>();
|
||||
|
||||
internal IDalamudHook Hook { get; set; }
|
||||
internal Delegate Delegate { get; set; }
|
||||
internal Assembly Assembly { get; set; }
|
||||
|
||||
private ulong? inProcessMemory = 0;
|
||||
internal ulong? InProcessMemory {
|
||||
get {
|
||||
if (Hook.IsDisposed) return 0;
|
||||
if (this.inProcessMemory == null) return null;
|
||||
if (this.inProcessMemory.Value > 0) return this.inProcessMemory.Value;
|
||||
var p = Process.GetCurrentProcess().MainModule;
|
||||
var begin = (ulong) p.BaseAddress.ToInt64();
|
||||
var end = begin + (ulong) p.ModuleMemorySize;
|
||||
var hookAddr = (ulong) Hook.Address.ToInt64();
|
||||
if (hookAddr >= begin && hookAddr <= end) {
|
||||
this.inProcessMemory = hookAddr - begin;
|
||||
return this.inProcessMemory.Value;
|
||||
} else {
|
||||
this.inProcessMemory = null;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
9
Dalamud/Hooking/IDalamudHook.cs
Normal file
9
Dalamud/Hooking/IDalamudHook.cs
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
using System;
|
||||
|
||||
namespace Dalamud.Hooking {
|
||||
internal interface IDalamudHook {
|
||||
public IntPtr Address { get; }
|
||||
public bool IsEnabled { get; }
|
||||
public bool IsDisposed { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Dalamud.Game.Internal;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin;
|
||||
using ImGuiNET;
|
||||
|
||||
|
|
@ -8,6 +10,8 @@ namespace Dalamud.Interface {
|
|||
internal class DalamudPluginStatWindow : IDisposable {
|
||||
|
||||
private readonly PluginManager pluginManager;
|
||||
private bool showDalamudHooks;
|
||||
|
||||
public DalamudPluginStatWindow(PluginManager pluginManager) {
|
||||
this.pluginManager = pluginManager;
|
||||
}
|
||||
|
|
@ -122,10 +126,70 @@ namespace Dalamud.Interface {
|
|||
ImGui.EndTabItem();
|
||||
}
|
||||
|
||||
if (ImGui.BeginTabItem("Hooks")) {
|
||||
ImGui.Columns(3);
|
||||
ImGui.SetColumnWidth(0, ImGui.GetWindowContentRegionWidth() - 280);
|
||||
ImGui.SetColumnWidth(1, 180f);
|
||||
ImGui.SetColumnWidth(2, 100f);
|
||||
ImGui.Text("Detour Method");
|
||||
ImGui.SameLine();
|
||||
ImGui.Text(" ");
|
||||
ImGui.SameLine();
|
||||
ImGui.Checkbox("Show Dalamud Hooks ###showDalamudHooksCheckbox", ref showDalamudHooks);
|
||||
ImGui.NextColumn();
|
||||
ImGui.Text("Address");
|
||||
ImGui.NextColumn();
|
||||
ImGui.Text("Status");
|
||||
ImGui.NextColumn();
|
||||
ImGui.Separator();
|
||||
ImGui.Separator();
|
||||
|
||||
foreach (var trackedHook in HookInfo.TrackedHooks) {
|
||||
try {
|
||||
if (!this.showDalamudHooks && trackedHook.Assembly == Assembly.GetExecutingAssembly()) continue;
|
||||
|
||||
|
||||
ImGui.Text($"{trackedHook.Delegate.Target} :: {trackedHook.Delegate.Method.Name}");
|
||||
ImGui.TextDisabled(trackedHook.Assembly.FullName);
|
||||
ImGui.NextColumn();
|
||||
if (!trackedHook.Hook.IsDisposed) {
|
||||
ImGui.Text($"{trackedHook.Hook.Address.ToInt64():X}");
|
||||
if (ImGui.IsItemClicked()) ImGui.SetClipboardText($"{trackedHook.Hook.Address.ToInt64():X}");
|
||||
|
||||
var processMemoryOffset = trackedHook.InProcessMemory;
|
||||
if (processMemoryOffset.HasValue) {
|
||||
ImGui.Text($"ffxiv_dx11.exe + {processMemoryOffset:X}");
|
||||
if (ImGui.IsItemClicked()) ImGui.SetClipboardText($"ffxiv_dx11.exe+{processMemoryOffset:X}");
|
||||
}
|
||||
|
||||
}
|
||||
ImGui.NextColumn();
|
||||
|
||||
if (trackedHook.Hook.IsDisposed) {
|
||||
ImGui.Text("Disposed");
|
||||
} else {
|
||||
ImGui.Text(trackedHook.Hook.IsEnabled ? "Enabled" : "Disabled");
|
||||
}
|
||||
|
||||
ImGui.NextColumn();
|
||||
} catch (Exception ex) {
|
||||
ImGui.Text(ex.Message);
|
||||
ImGui.NextColumn();
|
||||
while (ImGui.GetColumnIndex() != 0) ImGui.NextColumn();
|
||||
}
|
||||
|
||||
ImGui.Separator();
|
||||
}
|
||||
|
||||
ImGui.Columns();
|
||||
}
|
||||
|
||||
if (ImGui.IsWindowAppearing()) {
|
||||
HookInfo.TrackedHooks.RemoveAll(h => h.Hook.IsDisposed);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ImGui.EndTabBar();
|
||||
|
||||
ImGui.End();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue