mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-31 04:43:40 +01:00
- Use a Weak Concurrent Collection to track scoped hooks - Make `Hook`s remove themselves from the Tracked Hook list.
107 lines
4.2 KiB
C#
107 lines
4.2 KiB
C#
using System.Diagnostics;
|
|
using System.Linq;
|
|
|
|
using Dalamud.Game;
|
|
using Dalamud.IoC;
|
|
using Dalamud.IoC.Internal;
|
|
using Dalamud.Plugin.Internal.Types;
|
|
using Dalamud.Plugin.Services;
|
|
using Dalamud.Utility;
|
|
using Dalamud.Utility.Signatures;
|
|
using Serilog;
|
|
|
|
namespace Dalamud.Hooking.Internal;
|
|
|
|
/// <summary>
|
|
/// Plugin-scoped version of service used to create hooks.
|
|
/// </summary>
|
|
[PluginInterface]
|
|
[ServiceManager.ScopedService]
|
|
#pragma warning disable SA1015
|
|
[ResolveVia<IGameInteropProvider>]
|
|
#pragma warning restore SA1015
|
|
internal class GameInteropProviderPluginScoped : IGameInteropProvider, IInternalDisposableService
|
|
{
|
|
private readonly LocalPlugin plugin;
|
|
private readonly SigScanner scanner;
|
|
|
|
private readonly WeakConcurrentCollection<IDalamudHook> trackedHooks = new();
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="GameInteropProviderPluginScoped"/> class.
|
|
/// </summary>
|
|
/// <param name="plugin">Plugin this instance belongs to.</param>
|
|
/// <param name="scanner">SigScanner instance for target module.</param>
|
|
public GameInteropProviderPluginScoped(LocalPlugin plugin, TargetSigScanner scanner)
|
|
{
|
|
this.plugin = plugin;
|
|
this.scanner = scanner;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public void InitializeFromAttributes(object self)
|
|
{
|
|
foreach (var hook in SignatureHelper.Initialize(self))
|
|
this.trackedHooks.Add(hook);
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public Hook<T> HookFromFunctionPointerVariable<T>(nint address, T detour) where T : Delegate
|
|
{
|
|
var hook = Hook<T>.FromFunctionPointerVariable(address, detour);
|
|
this.trackedHooks.Add(hook);
|
|
return hook;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public Hook<T> HookFromImport<T>(ProcessModule? module, string moduleName, string functionName, uint hintOrOrdinal, T detour) where T : Delegate
|
|
{
|
|
var hook = Hook<T>.FromImport(module, moduleName, functionName, hintOrOrdinal, detour);
|
|
this.trackedHooks.Add(hook);
|
|
return hook;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public Hook<T> HookFromSymbol<T>(string moduleName, string exportName, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
|
{
|
|
var hook = Hook<T>.FromSymbol(moduleName, exportName, detour, backend == IGameInteropProvider.HookBackend.MinHook);
|
|
this.trackedHooks.Add(hook);
|
|
return hook;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public Hook<T> HookFromAddress<T>(nint procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
|
{
|
|
var hook = Hook<T>.FromAddress(procAddress, detour, backend == IGameInteropProvider.HookBackend.MinHook);
|
|
this.trackedHooks.Add(hook);
|
|
return hook;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public Hook<T> HookFromAddress<T>(UIntPtr procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
|
=> this.HookFromAddress((nint)procAddress, detour, backend);
|
|
|
|
/// <inheritdoc/>
|
|
public unsafe Hook<T> HookFromAddress<T>(void* procAddress, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
|
=> this.HookFromAddress((nint)procAddress, detour, backend);
|
|
|
|
/// <inheritdoc/>
|
|
public Hook<T> HookFromSignature<T>(string signature, T detour, IGameInteropProvider.HookBackend backend = IGameInteropProvider.HookBackend.Automatic) where T : Delegate
|
|
=> this.HookFromAddress(this.scanner.ScanText(signature), detour, backend);
|
|
|
|
/// <inheritdoc/>
|
|
void IInternalDisposableService.DisposeService()
|
|
{
|
|
var notDisposed = this.trackedHooks.Where(x => !x.IsDisposed).ToArray();
|
|
if (notDisposed.Length != 0)
|
|
Log.Warning("{PluginName} is leaking {Num} hooks! Make sure that all of them are disposed properly.", this.plugin.InternalName, notDisposed.Length);
|
|
|
|
foreach (var hook in notDisposed)
|
|
{
|
|
Log.Warning("\t\t\tLeaked hook at +0x{Address:X}", hook.Address.ToInt64() - this.scanner.Module.BaseAddress.ToInt64());
|
|
hook.Dispose();
|
|
}
|
|
|
|
this.trackedHooks.Clear();
|
|
}
|
|
}
|