diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 58660ab09..4f8b7e6b4 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -143,6 +143,8 @@ namespace Dalamud { private readonly ManualResetEvent unloadSignal; + private readonly ManualResetEvent finishUnloadSignal; + private readonly string baseDirectory; #endregion @@ -162,13 +164,14 @@ namespace Dalamud { /// internal DirectoryInfo AssetDirectory => new DirectoryInfo(this.StartInfo.AssetDirectory); - public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch) { + public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal) { this.StartInfo = info; this.LogLevelSwitch = loggingLevelSwitch; this.baseDirectory = info.WorkingDirectory; this.unloadSignal = new ManualResetEvent(false); + this.finishUnloadSignal = finishSignal; this.Configuration = DalamudConfiguration.Load(info.ConfigurationPath); @@ -277,6 +280,10 @@ namespace Dalamud { this.unloadSignal.WaitOne(); } + public void WaitForUnloadFinish() { + this.finishUnloadSignal.WaitOne(); + } + public void Dispose() { // this must be done before unloading plugins, or it can cause a race condition // due to rendering happening on another thread, where a plugin might receive @@ -305,6 +312,8 @@ namespace Dalamud { this.Data.Dispose(); this.AntiDebug?.Dispose(); + + Log.Debug("Dalamud::Dispose OK!"); } internal void ReplaceExceptionHandler() { diff --git a/Dalamud/EntryPoint.cs b/Dalamud/EntryPoint.cs index dfeb8f866..a64430644 100644 --- a/Dalamud/EntryPoint.cs +++ b/Dalamud/EntryPoint.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Net; +using System.Threading; using System.Threading.Tasks; using Dalamud.Interface; using EasyHook; @@ -19,6 +20,8 @@ namespace Dalamud { var (logger, levelSwitch) = NewLogger(info.WorkingDirectory); Log.Logger = logger; + var finishSignal = new ManualResetEvent(false); + try { Log.Information(new string('-', 200)); Log.Information("Initializing a session.."); @@ -31,7 +34,7 @@ namespace Dalamud { AppDomain.CurrentDomain.UnhandledException += OnUnhandledException; TaskScheduler.UnobservedTaskException += OnUnobservedTaskException; - using var dalamud = new Dalamud(info, levelSwitch); + using var dalamud = new Dalamud(info, levelSwitch, finishSignal); Log.Information("Starting a session.."); // Run session @@ -44,6 +47,8 @@ namespace Dalamud { Log.Information("Session has ended."); Log.CloseAndFlush(); + + finishSignal.Set(); } } diff --git a/Dalamud/Game/Internal/Framework.cs b/Dalamud/Game/Internal/Framework.cs index e55f502ee..819b7945d 100644 --- a/Dalamud/Game/Internal/Framework.cs +++ b/Dalamud/Game/Internal/Framework.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; using Dalamud.Game.Internal.Gui; using Dalamud.Game.Internal.Libc; using Dalamud.Game.Internal.Network; @@ -14,18 +15,25 @@ namespace Dalamud.Game.Internal { /// This class represents the Framework of the native game client and grants access to various subsystems. /// public sealed class Framework : IDisposable { + private readonly Dalamud dalamud; + [UnmanagedFunctionPointer(CallingConvention.ThisCall)] private delegate bool OnUpdateDetour(IntPtr framework); + private delegate IntPtr OnDestroyDetour(); + public delegate void OnUpdateDelegate(Framework framework); + public delegate IntPtr OnDestroyDelegate(); + /// /// Event that gets fired every time the game framework updates. /// public event OnUpdateDelegate OnUpdateEvent; private Hook updateHook; - + + private Hook destroyHook; /// /// A raw pointer to the instance of Client::Framework @@ -56,6 +64,7 @@ namespace Dalamud.Game.Internal { #endregion public Framework(SigScanner scanner, Dalamud dalamud) { + this.dalamud = dalamud; Address = new FrameworkAddressResolver(); Address.Setup(scanner); @@ -74,7 +83,8 @@ namespace Dalamud.Game.Internal { Network = new GameNetwork(scanner); - //Resource = new ResourceManager(dalamud, scanner); + this.destroyHook = + new Hook(Address.OnDestroy, new OnDestroyDelegate(HandleFrameworkDestroy), this); } private void HookVTable() { @@ -93,9 +103,9 @@ namespace Dalamud.Game.Internal { public void Enable() { Gui.Enable(); Network.Enable(); - //Resource.Enable(); this.updateHook.Enable(); + this.destroyHook.Enable(); } public void Dispose() { @@ -150,5 +160,14 @@ namespace Dalamud.Game.Internal { return this.updateHook.Original(framework); } + + private IntPtr HandleFrameworkDestroy() { + Log.Information("Framework::OnDestroy!"); + this.dalamud.Unload(); + + this.dalamud.WaitForUnloadFinish(); + + return this.destroyHook.Original(); + } } } diff --git a/Dalamud/Game/Internal/FrameworkAddressResolver.cs b/Dalamud/Game/Internal/FrameworkAddressResolver.cs index 6d6b11263..6d130beac 100644 --- a/Dalamud/Game/Internal/FrameworkAddressResolver.cs +++ b/Dalamud/Game/Internal/FrameworkAddressResolver.cs @@ -8,6 +8,8 @@ namespace Dalamud.Game.Internal { public IntPtr GuiManager { get; private set; } public IntPtr ScriptManager { get; private set; } + + public IntPtr OnDestroy { get; private set; } protected override void Setup64Bit(SigScanner sig) { @@ -19,6 +21,8 @@ namespace Dalamud.Game.Internal { // Called from Framework::Init ScriptManager = BaseAddress + 0x2C68; // note that no deref here + + OnDestroy = sig.ScanText("48 83 EC 48 48 8B 0D ?? ?? ?? ?? 48 85 C9"); } private void SetupFramework(SigScanner scanner) {