From 0c53741b1de2bebfd3b928251f17148711d446d8 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 25 May 2023 21:16:52 +0200 Subject: [PATCH] Remove OLE interop dependency. --- Dalamud/Dalamud.cs | 4 ++ Dalamud/Dalamud.csproj | 1 - Dalamud/Interface/DragDrop/DragDropInterop.cs | 27 ++++++++++- Dalamud/Interface/DragDrop/DragDropManager.cs | 30 +++++++----- Dalamud/Interface/DragDrop/DragDropTarget.cs | 48 +++++++++++-------- Dalamud/Interface/UiBuilder.cs | 5 +- 6 files changed, 76 insertions(+), 39 deletions(-) diff --git a/Dalamud/Dalamud.cs b/Dalamud/Dalamud.cs index 73914d2a7..142f653ef 100644 --- a/Dalamud/Dalamud.cs +++ b/Dalamud/Dalamud.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Dalamud.Configuration.Internal; using Dalamud.Game; using Dalamud.Game.Gui.Internal; +using Dalamud.Interface.DragDrop; using Dalamud.Interface.Internal; using Dalamud.Plugin.Internal; using Dalamud.Utility; @@ -135,6 +136,9 @@ internal sealed class Dalamud : IServiceType // will not receive any windows messages Service.GetNullable()?.Dispose(); + // this must be done before unloading interface manager, since it relies on the window handle members. + Service.GetNullable()?.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 // a render call after it has been disposed, which can crash if it attempts to diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj index 0f259ee31..61730b5ca 100644 --- a/Dalamud/Dalamud.csproj +++ b/Dalamud/Dalamud.csproj @@ -82,7 +82,6 @@ - diff --git a/Dalamud/Interface/DragDrop/DragDropInterop.cs b/Dalamud/Interface/DragDrop/DragDropInterop.cs index b3befac07..4a14d5e37 100644 --- a/Dalamud/Interface/DragDrop/DragDropInterop.cs +++ b/Dalamud/Interface/DragDrop/DragDropInterop.cs @@ -1,9 +1,9 @@ using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; using System.Text; -using Microsoft.VisualStudio.OLE.Interop; - // ReSharper disable UnusedMember.Local // ReSharper disable IdentifierTypo // ReSharper disable InconsistentNaming @@ -12,6 +12,29 @@ namespace Dalamud.Interface.DragDrop; /// Implements interop enums and function calls to interact with external drag and drop. internal partial class DragDropManager { + internal struct POINTL + { + [ComAliasName("Microsoft.VisualStudio.OLE.Interop.LONG")] + public int x; + [ComAliasName("Microsoft.VisualStudio.OLE.Interop.LONG")] + public int y; + } + + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("00000122-0000-0000-C000-000000000046")] + [ComImport] + public interface IDropTarget + { + [MethodImpl(MethodImplOptions.InternalCall)] + void DragEnter([MarshalAs(UnmanagedType.Interface), In] IDataObject pDataObj, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD"), In] uint grfKeyState, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.POINTL"), In] POINTL pt, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD"), In, Out] ref uint pdwEffect); + [MethodImpl(MethodImplOptions.InternalCall)] + void DragOver([ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD"), In] uint grfKeyState, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.POINTL"), In] POINTL pt, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD"), In, Out] ref uint pdwEffect); + [MethodImpl(MethodImplOptions.InternalCall)] + void DragLeave(); + [MethodImpl(MethodImplOptions.InternalCall)] + void Drop([MarshalAs(UnmanagedType.Interface), In] IDataObject pDataObj, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD"), In] uint grfKeyState, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.POINTL"), In] POINTL pt, [ComAliasName("Microsoft.VisualStudio.OLE.Interop.DWORD"), In, Out] ref uint pdwEffect); + } + private static class DragDropInterop { [Flags] diff --git a/Dalamud/Interface/DragDrop/DragDropManager.cs b/Dalamud/Interface/DragDrop/DragDropManager.cs index c27149153..2b1f79ed1 100644 --- a/Dalamud/Interface/DragDrop/DragDropManager.cs +++ b/Dalamud/Interface/DragDrop/DragDropManager.cs @@ -1,7 +1,8 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; - +using System.Threading.Tasks; +using Dalamud.Interface.Internal; using ImGuiNET; using Serilog; @@ -11,17 +12,22 @@ namespace Dalamud.Interface.DragDrop; /// A manager that keeps state of external windows drag and drop events, /// and can be used to create ImGui drag and drop sources and targets for those external events. /// -internal partial class DragDropManager : IDisposable, IDragDropManager +[ServiceManager.EarlyLoadedService] +internal partial class DragDropManager : IDisposable, IDragDropManager, IServiceType { - private readonly UiBuilder uiBuilder; - + private InterfaceManager? interfaceManager; private int lastDropFrame = -2; private int lastTooltipFrame = -1; - /// Initializes a new instance of the class. - /// The parent instance. - public DragDropManager(UiBuilder uiBuilder) - => this.uiBuilder = uiBuilder; + [ServiceManager.ServiceConstructor] + private DragDropManager() + { + Service.GetAsync().ContinueWith(task => + { + this.interfaceManager = task.Result.Manager; + this.Enable(); + }); + } /// Gets a value indicating whether external drag and drop is available at all. public bool ServiceAvailable { get; private set; } @@ -44,14 +50,15 @@ internal partial class DragDropManager : IDisposable, IDragDropManager /// Enable external drag and drop. public void Enable() { - if (this.ServiceAvailable) + if (this.ServiceAvailable || this.interfaceManager == null) { return; } try { - var ret2 = DragDropInterop.RegisterDragDrop(this.uiBuilder.WindowHandlePtr, this); + var ret2 = DragDropInterop.RegisterDragDrop(this.interfaceManager.WindowHandlePtr, this); + Log.Information($"[DragDrop] Registered window {this.interfaceManager.WindowHandlePtr} for external drag and drop operations. ({ret2})"); Marshal.ThrowExceptionForHR(ret2); this.ServiceAvailable = true; } @@ -71,7 +78,8 @@ internal partial class DragDropManager : IDisposable, IDragDropManager try { - DragDropInterop.RevokeDragDrop(this.uiBuilder.WindowHandlePtr); + DragDropInterop.RevokeDragDrop(this.interfaceManager!.WindowHandlePtr); + Log.Information($"[DragDrop] Disabled external drag and drop operations for window {this.interfaceManager.WindowHandlePtr}."); } catch (Exception ex) { diff --git a/Dalamud/Interface/DragDrop/DragDropTarget.cs b/Dalamud/Interface/DragDrop/DragDropTarget.cs index 79a20cff0..a592991dd 100644 --- a/Dalamud/Interface/DragDrop/DragDropTarget.cs +++ b/Dalamud/Interface/DragDrop/DragDropTarget.cs @@ -2,30 +2,30 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.InteropServices.ComTypes; using System.Text; using Dalamud.Utility; using ImGuiNET; -using Microsoft.VisualStudio.OLE.Interop; using Serilog; namespace Dalamud.Interface.DragDrop; /// Implements the IDropTarget interface to interact with external drag and dropping. -internal partial class DragDropManager : IDropTarget +internal partial class DragDropManager : DragDropManager.IDropTarget { + private int lastUpdateFrame = -1; + /// Create the drag and drop formats we accept. - private static readonly FORMATETC[] FormatEtc = - { + private static FORMATETC FormatEtc = new() { - cfFormat = (ushort)DragDropInterop.ClipboardFormat.CF_HDROP, + cfFormat = (short)DragDropInterop.ClipboardFormat.CF_HDROP, ptd = nint.Zero, - dwAspect = (uint)DragDropInterop.DVAspect.DVASPECT_CONTENT, + dwAspect = DVASPECT.DVASPECT_CONTENT, lindex = -1, - tymed = (uint)DragDropInterop.TYMED.TYMED_HGLOBAL, - }, - }; + tymed = TYMED.TYMED_HGLOBAL, + }; /// /// Invoked whenever a drag and drop process drags files into any FFXIV-related viewport. @@ -39,7 +39,7 @@ internal partial class DragDropManager : IDropTarget this.IsDragging = true; UpdateIo((DragDropInterop.ModifierKeys)grfKeyState, true); - if (pDataObj.QueryGetData(FormatEtc) != 0) + if (pDataObj.QueryGetData(ref FormatEtc) != 0) { pdwEffect = 0; } @@ -50,17 +50,24 @@ internal partial class DragDropManager : IDropTarget this.HasPaths = this.Files.Count + this.Directories.Count > 0; this.Extensions = this.Files.Select(Path.GetExtension).Where(p => !p.IsNullOrEmpty()).Distinct().ToHashSet(); } + Log.Debug("[DragDrop] Entering external Drag and Drop with {KeyState} at {PtX}, {PtY} and with {N} files.", (DragDropInterop.ModifierKeys)grfKeyState, pt.x, pt.y, this.Files.Count + this.Directories.Count); } /// Invoked every windows update-frame as long as the drag and drop process keeps hovering over an FFXIV-related viewport. /// The mouse button used to drag as well as key modifiers. /// The global cursor position. /// Effects that can be used with this drag and drop process. - /// Can be invoked more often than once a XIV frame, can also be less often (?). + /// Can be invoked more often than once a XIV frame, so we are keeping track of frames to skip unnecessary updates. public void DragOver(uint grfKeyState, POINTL pt, ref uint pdwEffect) { - UpdateIo((DragDropInterop.ModifierKeys)grfKeyState, false); - pdwEffect &= (uint)DragDropInterop.DropEffects.Copy; + var frame = ImGui.GetFrameCount(); + if (frame != this.lastUpdateFrame) + { + this.lastUpdateFrame = frame; + UpdateIo((DragDropInterop.ModifierKeys)grfKeyState, false); + pdwEffect &= (uint)DragDropInterop.DropEffects.Copy; + Log.Verbose("[DragDrop] External Drag and Drop with {KeyState} at {PtX}, {PtY}.", (DragDropInterop.ModifierKeys)grfKeyState, pt.x, pt.y); + } } /// Invoked whenever a drag and drop process that hovered over any FFXIV-related viewport leaves all FFXIV-related viewports. @@ -70,6 +77,7 @@ internal partial class DragDropManager : IDropTarget this.Files = Array.Empty(); this.Directories = Array.Empty(); this.Extensions = new HashSet(); + Log.Debug("[DragDrop] Leaving external Drag and Drop."); } /// Invoked whenever a drag process ends by dropping over any FFXIV-related viewport. @@ -90,6 +98,8 @@ internal partial class DragDropManager : IDropTarget { pdwEffect = 0; } + + Log.Debug("[DragDrop] Dropping {N} files with {KeyState} at {PtX}, {PtY}.", this.Files.Count + this.Directories.Count, (DragDropInterop.ModifierKeys)grfKeyState, pt.x, pt.y); } private static void UpdateIo(DragDropInterop.ModifierKeys keys, bool entering) @@ -189,12 +199,8 @@ internal partial class DragDropManager : IDropTarget try { - var stgMedium = new STGMEDIUM[] - { - default, - }; - data.GetData(FormatEtc, stgMedium); - var numFiles = DragDropInterop.DragQueryFile(stgMedium[0].unionmember, uint.MaxValue, new StringBuilder(), 0); + data.GetData(ref FormatEtc, out var stgMedium); + var numFiles = DragDropInterop.DragQueryFile(stgMedium.unionmember, uint.MaxValue, new StringBuilder(), 0); var files = new string[numFiles]; var sb = new StringBuilder(1024); var directoryCount = 0; @@ -202,11 +208,11 @@ internal partial class DragDropManager : IDropTarget for (var i = 0u; i < numFiles; ++i) { sb.Clear(); - var ret = DragDropInterop.DragQueryFile(stgMedium[0].unionmember, i, sb, sb.Capacity); + var ret = DragDropInterop.DragQueryFile(stgMedium.unionmember, i, sb, sb.Capacity); if (ret >= sb.Capacity) { sb.Capacity = ret + 1; - ret = DragDropInterop.DragQueryFile(stgMedium[0].unionmember, i, sb, sb.Capacity); + ret = DragDropInterop.DragQueryFile(stgMedium.unionmember, i, sb, sb.Capacity); } if (ret > 0 && ret < sb.Capacity) diff --git a/Dalamud/Interface/UiBuilder.cs b/Dalamud/Interface/UiBuilder.cs index a421d17ba..5bd317af5 100644 --- a/Dalamud/Interface/UiBuilder.cs +++ b/Dalamud/Interface/UiBuilder.cs @@ -31,7 +31,7 @@ public sealed class UiBuilder : IDisposable private readonly string namespaceName; private readonly InterfaceManager interfaceManager = Service.Get(); private readonly GameFontManager gameFontManager = Service.Get(); - private readonly DragDropManager dragDropManager; + private readonly DragDropManager dragDropManager = Service.Get(); private bool hasErrorWindow = false; private bool lastFrameUiHideState = false; @@ -49,8 +49,6 @@ public sealed class UiBuilder : IDisposable this.stopwatch = new Stopwatch(); this.hitchDetector = new HitchDetector($"UiBuilder({namespaceName})", this.configuration.UiBuilderHitch); this.namespaceName = namespaceName; - this.dragDropManager = new DragDropManager(this); - this.dragDropManager.Enable(); this.interfaceManager.Draw += this.OnDraw; this.interfaceManager.BuildFonts += this.OnBuildFonts; @@ -406,7 +404,6 @@ public sealed class UiBuilder : IDisposable /// void IDisposable.Dispose() { - this.dragDropManager.Dispose(); this.interfaceManager.Draw -= this.OnDraw; this.interfaceManager.BuildFonts -= this.OnBuildFonts; this.interfaceManager.ResizeBuffers -= this.OnResizeBuffers;