diff --git a/src/Avalonia.MicroCom/MicroComProxyBase.cs b/src/Avalonia.MicroCom/MicroComProxyBase.cs index 140af3e4ef..b707b50643 100644 --- a/src/Avalonia.MicroCom/MicroComProxyBase.cs +++ b/src/Avalonia.MicroCom/MicroComProxyBase.cs @@ -61,6 +61,22 @@ namespace Avalonia.MicroCom throw new COMException("QueryInterface failed", rv); } + public bool TryQueryInterface(out T result) where T : IUnknown + { + var guid = MicroComRuntime.GetGuidFor(typeof(T)); + var rv = QueryInterface(guid, out var ppv); + if (rv == 0) + { + result = (T)MicroComRuntime.CreateProxyFor(typeof(T), ppv, true); + return true; + } + else + { + result = default; + return false; + } + } + public bool IsDisposed => _nativePointer == IntPtr.Zero; protected virtual void Dispose(bool disposing) diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs index adf0ca64bb..7cf8b14bed 100644 --- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs +++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs @@ -83,7 +83,7 @@ namespace Avalonia.Win32 while (true) { - var ptr = MicroCom.MicroComRuntime.GetNativeIntPtr(wrapper, true); + var ptr = MicroCom.MicroComRuntime.GetNativeIntPtr(wrapper); var hr = UnmanagedMethods.OleSetClipboard(ptr); if (hr == 0) @@ -107,8 +107,8 @@ namespace Avalonia.Win32 if (hr == 0) { - using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, false); - var wrapper = new OleDataObject(proxy); + using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, true); + using var wrapper = new OleDataObject(proxy); var formats = wrapper.GetDataFormats().ToArray(); return formats; } @@ -131,8 +131,8 @@ namespace Avalonia.Win32 if (hr == 0) { - using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, false); - var wrapper = new OleDataObject(proxy); + using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, true); + using var wrapper = new OleDataObject(proxy); var rv = wrapper.Get(format); return rv; } diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index a62f75f5b8..ac603f60eb 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -16,33 +16,43 @@ using IDataObject = Avalonia.Input.IDataObject; namespace Avalonia.Win32 { + public interface IDisposableDataObject : IDataObject, IDisposable { } + internal static class DataObjectEx { - public static unsafe IDataObject GetAvaloniaObject(this Win32Com.IDataObject pDataObj) + public static unsafe IDisposableDataObject GetAvaloniaObjectFromCOM(this Win32Com.IDataObject pDataObj) { - switch (pDataObj) + if (pDataObj is null) + { + throw new ArgumentNullException(nameof(pDataObj)); + } + if (pDataObj is IDisposableDataObject disposableDataObject) + { + return disposableDataObject; + } + + // If DataObject was created on the our side (drag'n'drop initiated by Avalonia), + // then pDataObj will implement IAvnDataObject interface as well. So we can safely case it back to DataObject. + if (((MicroComProxyBase)pDataObj).TryQueryInterface(out var avnInterface)) + { + using (avnInterface) + { + var ppv = MicroComRuntime.GetNativeIntPtr(avnInterface); + return MicroComRuntime.GetObjectFromCcw(ppv) as DataObject; + } + } + // Otherwise wrap pDataObj into OleDataObject. + else { - case null: - throw new ArgumentNullException(nameof(pDataObj)); - case DataObject avnObj: - return avnObj; - case OleDataObject oleAvnObj: - return oleAvnObj; - default: - var guid = MicroComRuntime.GetGuidFor(typeof(Win32Com.IAvnDataObject)); - var rv = ((MicroComProxyBase)pDataObj).QueryInterface(guid, out var ppv); - return rv == 0 - ? MicroComRuntime.GetObjectFromCcw(ppv) as DataObject - : new OleDataObject(pDataObj); + return new OleDataObject(pDataObj); } } } - internal class DataObject : IDataObject, Win32Com.IAvnDataObject, IMicroComShadowContainer + internal class DataObject : IDisposableDataObject, Win32Com.IAvnDataObject, IMicroComShadowContainer { // Compatibility with WinForms + WPF... internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray(); - private static Dictionary _avnDataObject = new Dictionary(); class FormatEnumerator : Win32Com.IEnumFORMATETC, IMicroComShadowContainer { @@ -143,7 +153,7 @@ namespace Avalonia.Win32 public DataObject(IDataObject wrapped) { - if (_wrapped is DataObject) + if (_wrapped is DataObject || _wrapped is OleDataObject) { throw new InvalidOperationException(); } diff --git a/src/Windows/Avalonia.Win32/DragSource.cs b/src/Windows/Avalonia.Win32/DragSource.cs index 379efcb64c..7eb23cfa13 100644 --- a/src/Windows/Avalonia.Win32/DragSource.cs +++ b/src/Windows/Avalonia.Win32/DragSource.cs @@ -15,12 +15,12 @@ namespace Avalonia.Win32 triggerEvent.Pointer.Capture(null); - var dataObject = new DataObject(data); - var src = new OleDragSource(); + using var dataObject = new DataObject(data); + using var src = new OleDragSource(); var allowed = OleDropTarget.ConvertDropEffect(allowedEffects); - var objPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(dataObject, true); - var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(src, true); + var objPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(dataObject); + var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(src); UnmanagedMethods.DoDragDrop(objPtr, srcPtr, (int)allowed, out var finalEffect); return Task.FromResult(OleDropTarget.ConvertDropEffect((Win32Com.DropEffect)finalEffect)); diff --git a/src/Windows/Avalonia.Win32/OleContext.cs b/src/Windows/Avalonia.Win32/OleContext.cs index a3641264ec..c025d06fe7 100644 --- a/src/Windows/Avalonia.Win32/OleContext.cs +++ b/src/Windows/Avalonia.Win32/OleContext.cs @@ -47,7 +47,7 @@ namespace Avalonia.Win32 return false; } - var trgPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(target, false); + var trgPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(target); return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, trgPtr) == UnmanagedMethods.HRESULT.S_OK; } diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs index 5626a0a675..81058b8229 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -8,17 +8,18 @@ using System.Runtime.InteropServices.ComTypes; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using Avalonia.Input; +using Avalonia.MicroCom; using Avalonia.Win32.Interop; namespace Avalonia.Win32 { - internal class OleDataObject : Avalonia.Input.IDataObject + internal class OleDataObject : IDisposableDataObject { private readonly Win32Com.IDataObject _wrapped; public OleDataObject(Win32Com.IDataObject wrapped) { - _wrapped = wrapped; + _wrapped = wrapped.CloneReference(); } public bool Contains(string dataFormat) @@ -183,5 +184,10 @@ namespace Avalonia.Win32 } return formatsList; } + + public void Dispose() + { + _wrapped.Dispose(); + } } } diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index 3767dbd50c..e20877e87c 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -3,7 +3,6 @@ using Avalonia.Input.Raw; using Avalonia.MicroCom; using Avalonia.Platform; using Avalonia.Win32.Interop; -using IDataObject = Avalonia.Input.IDataObject; using DropEffect = Avalonia.Win32.Win32Com.DropEffect; namespace Avalonia.Win32 @@ -14,7 +13,7 @@ namespace Avalonia.Win32 private readonly ITopLevelImpl _tl; private readonly IDragDropDevice _dragDevice; - private IDataObject _currentDrag = null; + private IDisposableDataObject _currentDrag = null; public MicroComShadow Shadow { get; set; } @@ -77,7 +76,12 @@ namespace Avalonia.Win32 *pdwEffect= (int)DropEffect.None; } - _currentDrag = pDataObj.GetAvaloniaObject(); + var newDrag = pDataObj.GetAvaloniaObjectFromCOM(); + if (_currentDrag != newDrag) + { + _currentDrag?.Dispose(); + _currentDrag = newDrag; + } var args = new RawDragEvent( _dragDevice, @@ -85,7 +89,7 @@ namespace Avalonia.Win32 _target, GetDragLocation(pt), _currentDrag, - ConvertDropEffect((*pdwEffect)), + ConvertDropEffect(*pdwEffect), ConvertKeyState(grfKeyState) ); dispatch(args); @@ -129,6 +133,7 @@ namespace Avalonia.Win32 } finally { + _currentDrag?.Dispose(); _currentDrag = null; } } @@ -143,7 +148,12 @@ namespace Avalonia.Win32 *pdwEffect = (int)DropEffect.None; } - _currentDrag = pDataObj.GetAvaloniaObject(); + var newDrag = pDataObj.GetAvaloniaObjectFromCOM(); + if (_currentDrag != newDrag) + { + _currentDrag?.Dispose(); + _currentDrag = newDrag; + } var args = new RawDragEvent( _dragDevice, @@ -159,6 +169,7 @@ namespace Avalonia.Win32 } finally { + _currentDrag?.Dispose(); _currentDrag = null; } } @@ -171,6 +182,7 @@ namespace Avalonia.Win32 public void Dispose() { + _currentDrag?.Dispose(); } public void OnReferencedFromNative() diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index e4f5268285..dae8da8f8f 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -529,6 +529,7 @@ namespace Avalonia.Win32 if (_dropTarget != null) { OleContext.Current?.UnregisterDragDrop(Handle); + _dropTarget.Dispose(); _dropTarget = null; }