using System; using Dalamud.Game; using Dalamud.Hooking; using Dalamud.Memory; using Serilog; namespace Dalamud.Fixes; /// /// This fix is for the following issue: /// Null reference in the game's WndProc function when certain window messages arrive /// before an object on the game's input manager is initialized. /// internal class WndProcNullRefFix : IGameFix, IDisposable { private Hook? wndProcHook; private IntPtr object1Address; private IntPtr object2Address; private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); /// public void Apply() { var sigScanner = Service.Get(); if (!sigScanner.TryScanText("40 55 53 41 54 41 56 48 8D 6C 24 ??", out var patchAddress)) { Log.Error("Failed to find WndProc address"); return; } if (!sigScanner.TryGetStaticAddressFromSig("74 1F E8 ?? ?? ?? ?? 48 83 38 00 ", out this.object1Address)) { Log.Error("Failed to find object1 address"); return; } if (!sigScanner.TryGetStaticAddressFromSig("E8 ?? ?? ?? ?? 48 83 38 00 74 14", out this.object2Address, 0x7)) { Log.Error("Failed to find object2 address"); return; } Log.Information($"Applying WndProcNullRefFix at {patchAddress:X} with o1 {this.object1Address:X}, o2 {this.object2Address:X}"); this.wndProcHook = new Hook(patchAddress, this.WndProcDetour, true); Log.Information("Set up hook"); this.wndProcHook.Enable(); Log.Information("Enabled hook"); } /// public void Dispose() { this.wndProcHook?.Dispose(); } private IntPtr WndProcDetour(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam) { if (msg == 0x219 && wParam.ToInt64() == 7 && (MemoryHelper.Read(this.object1Address) == IntPtr.Zero || MemoryHelper.Read(this.object2Address) == IntPtr.Zero)) { Log.Information("Filtered WM_DEVICE_CHANGE message"); return IntPtr.Zero; } return this.wndProcHook!.Original(hWnd, msg, wParam, lParam); } }