Dalamud/Dalamud/Hooking/Internal/GameInteropProviderPluginScoped.cs
Haselnussbomber c93f04f0e4
Code cleanup (#2439)
* Use new Lock objects

* Fix CA1513: Use ObjectDisposedException.ThrowIf

* Fix CA1860: Avoid using 'Enumerable.Any()' extension method

* Fix IDE0028: Use collection initializers or expressions

* Fix CA2263: Prefer generic overload when type is known

* Fix CA1862: Use the 'StringComparison' method overloads to perform case-insensitive string comparisons

* Fix IDE0270: Null check can be simplified

* Fix IDE0280: Use 'nameof'

* Fix IDE0009: Add '.this'

* Fix IDE0007: Use 'var' instead of explicit type

* Fix IDE0062: Make local function static

* Fix CA1859: Use concrete types when possible for improved performance

* Fix IDE0066: Use switch expression

Only applied to where it doesn't look horrendous.

* Use is over switch

* Fix CA1847: Use String.Contains(char) instead of String.Contains(string) with single characters

* Fix SYSLIB1045: Use 'GeneratedRegexAttribute' to generate the regular expression implementation at compile-time.

* Fix CA1866: Use 'string.EndsWith(char)' instead of 'string.EndsWith(string)' when you have a string with a single char

* Fix IDE0057: Substring can be simplified

* Fix IDE0059: Remove unnecessary value assignment

* Fix CA1510: Use ArgumentNullException throw helper

* Fix IDE0300: Use collection expression for array

* Fix IDE0250: Struct can be made 'readonly'

* Fix IDE0018: Inline variable declaration

* Fix CA1850: Prefer static HashData method over ComputeHash

* Fi CA1872: Prefer 'Convert.ToHexString' and 'Convert.ToHexStringLower' over call chains based on 'BitConverter.ToString'

* Update ModuleLog instantiations

* Organize usings
2026-01-06 08:36:55 -08:00

108 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 = [];
/// <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();
}
}