diff --git a/samples/ControlCatalog/MainView.xaml b/samples/ControlCatalog/MainView.xaml
index 7beb496ab5..facce2aa82 100644
--- a/samples/ControlCatalog/MainView.xaml
+++ b/samples/ControlCatalog/MainView.xaml
@@ -40,6 +40,9 @@
+
+
+
diff --git a/samples/ControlCatalog/Pages/ClipboardPage.xaml b/samples/ControlCatalog/Pages/ClipboardPage.xaml
new file mode 100644
index 0000000000..4199a9780f
--- /dev/null
+++ b/samples/ControlCatalog/Pages/ClipboardPage.xaml
@@ -0,0 +1,23 @@
+
+
+ Example of ClipboardPage capabilities
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/ControlCatalog/Pages/ClipboardPage.xaml.cs b/samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
new file mode 100644
index 0000000000..eed46265ff
--- /dev/null
+++ b/samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Avalonia.Markup.Xaml;
+
+namespace ControlCatalog.Pages
+{
+ public partial class ClipboardPage : UserControl
+ {
+ public ClipboardPage()
+ {
+ InitializeComponent();
+ }
+
+ private TextBox ClipboardContent => this.Get("ClipboardContent");
+
+ private void InitializeComponent()
+ {
+ AvaloniaXamlLoader.Load(this);
+ }
+
+ private async void CopyText(object sender, RoutedEventArgs args)
+ {
+ await Application.Current.Clipboard.SetTextAsync(ClipboardContent.Text);
+ }
+
+ private async void PasteText(object sender, RoutedEventArgs args)
+ {
+ ClipboardContent.Text = await Application.Current.Clipboard.GetTextAsync();
+ }
+
+ private async void CopyTextDataObject(object sender, RoutedEventArgs args)
+ {
+ var dataObject = new DataObject();
+ dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
+ await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
+ }
+
+ private async void PasteTextDataObject(object sender, RoutedEventArgs args)
+ {
+ ClipboardContent.Text = await Application.Current.Clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
+ }
+
+ private async void CopyFilesDataObject(object sender, RoutedEventArgs args)
+ {
+ var files = ClipboardContent.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
+ if (files.Length == 0)
+ {
+ return;
+ }
+ var dataObject = new DataObject();
+ dataObject.Set(DataFormats.FileNames, files);
+ await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
+ }
+
+ private async void PasteFilesDataObject(object sender, RoutedEventArgs args)
+ {
+ var fiels = await Application.Current.Clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable;
+ ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
+ }
+
+ private async void GetFormats(object sender, RoutedEventArgs args)
+ {
+ var formats = await Application.Current.Clipboard.GetFormatsAsync();
+ ClipboardContent.Text = string.Join(Environment.NewLine, formats);
+ }
+
+ private async void Clear(object sender, RoutedEventArgs args)
+ {
+ await Application.Current.Clipboard.ClearAsync();
+ }
+ }
+}
diff --git a/samples/ControlCatalog/Pages/DragAndDropPage.xaml b/samples/ControlCatalog/Pages/DragAndDropPage.xaml
index 1bf8bfa99b..3f8a023060 100644
--- a/samples/ControlCatalog/Pages/DragAndDropPage.xaml
+++ b/samples/ControlCatalog/Pages/DragAndDropPage.xaml
@@ -11,7 +11,13 @@
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
- Drag Me
+ Drag Me (text)
+
+
+ Drag Me (files)
d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
+ SetupDnd("Files", d => d.Set(DataFormats.FileNames, new[] { Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }), DragDropEffects.Copy);
}
void SetupDnd(string suffix, Action factory, DragDropEffects effects)
diff --git a/src/Avalonia.MicroCom/CallbackBase.cs b/src/Avalonia.MicroCom/CallbackBase.cs
new file mode 100644
index 0000000000..6783ebe3dc
--- /dev/null
+++ b/src/Avalonia.MicroCom/CallbackBase.cs
@@ -0,0 +1,53 @@
+namespace Avalonia.MicroCom
+{
+ public abstract class CallbackBase : IUnknown, IMicroComShadowContainer
+ {
+ private readonly object _lock = new object();
+ private bool _referencedFromManaged = true;
+ private bool _referencedFromNative = false;
+ private bool _destroyed;
+
+ public bool IsDestroyed => _destroyed;
+
+ protected virtual void Destroyed()
+ {
+
+ }
+
+ public void Dispose()
+ {
+ lock (_lock)
+ {
+ _referencedFromManaged = false;
+ DestroyIfNeeded();
+ }
+ }
+
+ void DestroyIfNeeded()
+ {
+ if(_destroyed)
+ return;
+ if (_referencedFromManaged == false && _referencedFromNative == false)
+ {
+ _destroyed = true;
+ Destroyed();
+ }
+ }
+
+ public MicroComShadow Shadow { get; set; }
+ public void OnReferencedFromNative()
+ {
+ lock (_lock)
+ _referencedFromNative = true;
+ }
+
+ public void OnUnreferencedFromNative()
+ {
+ lock (_lock)
+ {
+ _referencedFromNative = false;
+ DestroyIfNeeded();
+ }
+ }
+ }
+}
diff --git a/src/Avalonia.MicroCom/MicroComRuntime.cs b/src/Avalonia.MicroCom/MicroComRuntime.cs
index e0f524146a..b9a56a69ba 100644
--- a/src/Avalonia.MicroCom/MicroComRuntime.cs
+++ b/src/Avalonia.MicroCom/MicroComRuntime.cs
@@ -12,6 +12,8 @@ namespace Avalonia.MicroCom
new ConcurrentDictionary>();
private static ConcurrentDictionary _guids = new ConcurrentDictionary();
private static ConcurrentDictionary _guidsToTypes = new ConcurrentDictionary();
+
+ internal static readonly Guid ManagedObjectInterfaceGuid = Guid.Parse("cd7687c0-a9c2-4563-b08e-a399df50c633");
static MicroComRuntime()
{
@@ -82,6 +84,19 @@ namespace Avalonia.MicroCom
return shadow.Target;
}
+ public static bool IsComWrapper(IUnknown obj) => obj is MicroComProxyBase;
+
+ public static object TryUnwrapManagedObject(IUnknown obj)
+ {
+ if (obj is not MicroComProxyBase proxy)
+ return null;
+ if (proxy.QueryInterface(ManagedObjectInterfaceGuid, out _) != 0)
+ return null;
+ // Successful QueryInterface always increments ref counter
+ proxy.Release();
+ return GetObjectFromCcw(proxy.NativePointer);
+ }
+
public static bool TryGetTypeForGuid(Guid guid, out Type t) => _guidsToTypes.TryGetValue(guid, out t);
public static bool GetVtableFor(Type type, out IntPtr ptr) => _vtables.TryGetValue(type, out ptr);
diff --git a/src/Avalonia.MicroCom/MicroComShadow.cs b/src/Avalonia.MicroCom/MicroComShadow.cs
index a6a0fd519e..765ff3b9ad 100644
--- a/src/Avalonia.MicroCom/MicroComShadow.cs
+++ b/src/Avalonia.MicroCom/MicroComShadow.cs
@@ -23,6 +23,12 @@ namespace Avalonia.MicroCom
{
if (MicroComRuntime.TryGetTypeForGuid(*guid, out var type))
return QueryInterface(type, ppv);
+ else if (*guid == MicroComRuntime.ManagedObjectInterfaceGuid)
+ {
+ ccw->RefCount++;
+ *ppv = ccw;
+ return 0;
+ }
else
return unchecked((int)0x80004002u);
}
diff --git a/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs b/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs
index 8084e06d28..ae24d06bad 100644
--- a/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs
@@ -6,7 +6,7 @@ using Avalonia.Platform;
namespace Avalonia.Native
{
- internal class AvaloniaNativeApplicationPlatform : CallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl
+ internal class AvaloniaNativeApplicationPlatform : NativeCallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl
{
public event EventHandler ShutdownRequested;
diff --git a/src/Avalonia.Native/AvaloniaNativeDragSource.cs b/src/Avalonia.Native/AvaloniaNativeDragSource.cs
index 80d54d8a10..f91a299b3b 100644
--- a/src/Avalonia.Native/AvaloniaNativeDragSource.cs
+++ b/src/Avalonia.Native/AvaloniaNativeDragSource.cs
@@ -30,7 +30,7 @@ namespace Avalonia.Native
return visual.VisualRoot as TopLevel;
}
- class DndCallback : CallbackBase, IAvnDndResultCallback
+ class DndCallback : NativeCallbackBase, IAvnDndResultCallback
{
private TaskCompletionSource _tcs;
diff --git a/src/Avalonia.Native/AvaloniaNativePlatform.cs b/src/Avalonia.Native/AvaloniaNativePlatform.cs
index 1eadf70b13..4802fed554 100644
--- a/src/Avalonia.Native/AvaloniaNativePlatform.cs
+++ b/src/Avalonia.Native/AvaloniaNativePlatform.cs
@@ -79,7 +79,7 @@ namespace Avalonia.Native
_factory = factory;
}
- class GCHandleDeallocator : CallbackBase, IAvnGCHandleDeallocatorCallback
+ class GCHandleDeallocator : NativeCallbackBase, IAvnGCHandleDeallocatorCallback
{
public void FreeGCHandle(IntPtr handle)
{
diff --git a/src/Avalonia.Native/CallbackBase.cs b/src/Avalonia.Native/CallbackBase.cs
index 455ed4b159..56f9505cb4 100644
--- a/src/Avalonia.Native/CallbackBase.cs
+++ b/src/Avalonia.Native/CallbackBase.cs
@@ -5,19 +5,8 @@ using Avalonia.Platform;
namespace Avalonia.Native
{
- public class CallbackBase : IUnknown, IMicroComShadowContainer, IMicroComExceptionCallback
+ public abstract class NativeCallbackBase : CallbackBase, IMicroComExceptionCallback
{
- private readonly object _lock = new object();
- private bool _referencedFromManaged = true;
- private bool _referencedFromNative = false;
- private bool _destroyed;
-
-
- protected virtual void Destroyed()
- {
-
- }
-
public void RaiseException(Exception e)
{
if (AvaloniaLocator.Current.GetService() is PlatformThreadingInterface threadingInterface)
@@ -27,41 +16,5 @@ namespace Avalonia.Native
threadingInterface.DispatchException(ExceptionDispatchInfo.Capture(e));
}
}
-
- public void Dispose()
- {
- lock (_lock)
- {
- _referencedFromManaged = false;
- DestroyIfNeeded();
- }
- }
-
- void DestroyIfNeeded()
- {
- if(_destroyed)
- return;
- if (_referencedFromManaged == false && _referencedFromNative == false)
- {
- _destroyed = true;
- Destroyed();
- }
- }
-
- public MicroComShadow Shadow { get; set; }
- public void OnReferencedFromNative()
- {
- lock (_lock)
- _referencedFromNative = true;
- }
-
- public void OnUnreferencedFromNative()
- {
- lock (_lock)
- {
- _referencedFromNative = false;
- DestroyIfNeeded();
- }
- }
}
}
diff --git a/src/Avalonia.Native/DeferredFramebuffer.cs b/src/Avalonia.Native/DeferredFramebuffer.cs
index 950b6a3197..4e0c037154 100644
--- a/src/Avalonia.Native/DeferredFramebuffer.cs
+++ b/src/Avalonia.Native/DeferredFramebuffer.cs
@@ -27,7 +27,7 @@ namespace Avalonia.Native
public Vector Dpi { get; set; }
public PixelFormat Format { get; set; }
- class Disposer : CallbackBase
+ class Disposer : NativeCallbackBase
{
private IntPtr _ptr;
diff --git a/src/Avalonia.Native/IAvnMenu.cs b/src/Avalonia.Native/IAvnMenu.cs
index 0e6fdd2df0..e413023f6d 100644
--- a/src/Avalonia.Native/IAvnMenu.cs
+++ b/src/Avalonia.Native/IAvnMenu.cs
@@ -7,7 +7,7 @@ using Avalonia.Platform.Interop;
namespace Avalonia.Native.Interop
{
- class MenuEvents : CallbackBase, IAvnMenuEvents
+ class MenuEvents : NativeCallbackBase, IAvnMenuEvents
{
private IAvnMenu _parent;
diff --git a/src/Avalonia.Native/MenuActionCallback.cs b/src/Avalonia.Native/MenuActionCallback.cs
index 5318195f30..3acbb9c19c 100644
--- a/src/Avalonia.Native/MenuActionCallback.cs
+++ b/src/Avalonia.Native/MenuActionCallback.cs
@@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native
{
- public class MenuActionCallback : CallbackBase, IAvnActionCallback
+ public class MenuActionCallback : NativeCallbackBase, IAvnActionCallback
{
private Action _action;
diff --git a/src/Avalonia.Native/PlatformThreadingInterface.cs b/src/Avalonia.Native/PlatformThreadingInterface.cs
index 882d5a2ed3..99aa8a5850 100644
--- a/src/Avalonia.Native/PlatformThreadingInterface.cs
+++ b/src/Avalonia.Native/PlatformThreadingInterface.cs
@@ -9,7 +9,7 @@ namespace Avalonia.Native
{
internal class PlatformThreadingInterface : IPlatformThreadingInterface
{
- class TimerCallback : CallbackBase, IAvnActionCallback
+ class TimerCallback : NativeCallbackBase, IAvnActionCallback
{
readonly Action _tick;
@@ -24,7 +24,7 @@ namespace Avalonia.Native
}
}
- class SignaledCallback : CallbackBase, IAvnSignaledCallback
+ class SignaledCallback : NativeCallbackBase, IAvnSignaledCallback
{
readonly PlatformThreadingInterface _parent;
diff --git a/src/Avalonia.Native/PredicateCallback.cs b/src/Avalonia.Native/PredicateCallback.cs
index 19c470bcb3..dbb65791f0 100644
--- a/src/Avalonia.Native/PredicateCallback.cs
+++ b/src/Avalonia.Native/PredicateCallback.cs
@@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native
{
- public class PredicateCallback : CallbackBase, IAvnPredicateCallback
+ public class PredicateCallback : NativeCallbackBase, IAvnPredicateCallback
{
private Func _predicate;
diff --git a/src/Avalonia.Native/SystemDialogs.cs b/src/Avalonia.Native/SystemDialogs.cs
index 8012813644..4372829df1 100644
--- a/src/Avalonia.Native/SystemDialogs.cs
+++ b/src/Avalonia.Native/SystemDialogs.cs
@@ -62,7 +62,7 @@ namespace Avalonia.Native
}
}
- internal unsafe class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents
+ internal unsafe class SystemDialogEvents : NativeCallbackBase, IAvnSystemDialogEvents
{
private TaskCompletionSource _tcs;
diff --git a/src/Avalonia.Native/WindowImplBase.cs b/src/Avalonia.Native/WindowImplBase.cs
index 1917b1575d..eba58c2f85 100644
--- a/src/Avalonia.Native/WindowImplBase.cs
+++ b/src/Avalonia.Native/WindowImplBase.cs
@@ -151,7 +151,7 @@ namespace Avalonia.Native
public IMouseDevice MouseDevice => _mouse;
public abstract IPopupImpl CreatePopup();
- protected unsafe class WindowBaseEvents : CallbackBase, IAvnWindowBaseEvents
+ protected unsafe class WindowBaseEvents : NativeCallbackBase, IAvnWindowBaseEvents
{
private readonly WindowBaseImpl _parent;
diff --git a/src/Windows/Avalonia.Win32/ClipboardFormats.cs b/src/Windows/Avalonia.Win32/ClipboardFormats.cs
index a18116aade..7538dedfca 100644
--- a/src/Windows/Avalonia.Win32/ClipboardFormats.cs
+++ b/src/Windows/Avalonia.Win32/ClipboardFormats.cs
@@ -14,11 +14,11 @@ namespace Avalonia.Win32
class ClipboardFormat
{
- public short Format { get; private set; }
+ public ushort Format { get; private set; }
public string Name { get; private set; }
- public short[] Synthesized { get; private set; }
+ public ushort[] Synthesized { get; private set; }
- public ClipboardFormat(string name, short format, params short[] synthesized)
+ public ClipboardFormat(string name, ushort format, params ushort[] synthesized)
{
Format = format;
Name = name;
@@ -28,12 +28,12 @@ namespace Avalonia.Win32
private static readonly List FormatList = new List()
{
- new ClipboardFormat(DataFormats.Text, (short)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (short)UnmanagedMethods.ClipboardFormat.CF_TEXT),
- new ClipboardFormat(DataFormats.FileNames, (short)UnmanagedMethods.ClipboardFormat.CF_HDROP),
+ new ClipboardFormat(DataFormats.Text, (ushort)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (ushort)UnmanagedMethods.ClipboardFormat.CF_TEXT),
+ new ClipboardFormat(DataFormats.FileNames, (ushort)UnmanagedMethods.ClipboardFormat.CF_HDROP),
};
- private static string QueryFormatName(short format)
+ private static string QueryFormatName(ushort format)
{
StringBuilder sb = new StringBuilder(MAX_FORMAT_NAME_LENGTH);
if (UnmanagedMethods.GetClipboardFormatName(format, sb, sb.Capacity) > 0)
@@ -41,7 +41,7 @@ namespace Avalonia.Win32
return null;
}
- public static string GetFormat(short format)
+ public static string GetFormat(ushort format)
{
lock (FormatList)
{
@@ -58,7 +58,7 @@ namespace Avalonia.Win32
}
}
- public static short GetFormat(string format)
+ public static ushort GetFormat(string format)
{
lock (FormatList)
{
@@ -68,7 +68,7 @@ namespace Avalonia.Win32
int id = UnmanagedMethods.RegisterClipboardFormat(format);
if (id == 0)
throw new Win32Exception();
- pd = new ClipboardFormat(format, (short)id);
+ pd = new ClipboardFormat(format, (ushort)id);
FormatList.Add(pd);
}
return pd.Format;
diff --git a/src/Windows/Avalonia.Win32/ClipboardImpl.cs b/src/Windows/Avalonia.Win32/ClipboardImpl.cs
index 047b7c361f..7cf8b14bed 100644
--- a/src/Windows/Avalonia.Win32/ClipboardImpl.cs
+++ b/src/Windows/Avalonia.Win32/ClipboardImpl.cs
@@ -78,12 +78,13 @@ namespace Avalonia.Win32
public async Task SetDataObjectAsync(IDataObject data)
{
Dispatcher.UIThread.VerifyAccess();
- var wrapper = new DataObject(data);
+ using var wrapper = new DataObject(data);
var i = OleRetryCount;
while (true)
{
- var hr = UnmanagedMethods.OleSetClipboard(wrapper);
+ var ptr = MicroCom.MicroComRuntime.GetNativeIntPtr(wrapper);
+ var hr = UnmanagedMethods.OleSetClipboard(ptr);
if (hr == 0)
break;
@@ -106,9 +107,9 @@ namespace Avalonia.Win32
if (hr == 0)
{
- var wrapper = new OleDataObject(dataObject);
+ using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, true);
+ using var wrapper = new OleDataObject(proxy);
var formats = wrapper.GetDataFormats().ToArray();
- Marshal.ReleaseComObject(dataObject);
return formats;
}
@@ -130,9 +131,9 @@ namespace Avalonia.Win32
if (hr == 0)
{
- var wrapper = new OleDataObject(dataObject);
+ using var proxy = MicroCom.MicroComRuntime.CreateProxyFor(dataObject, true);
+ using var wrapper = new OleDataObject(proxy);
var rv = wrapper.Get(format);
- Marshal.ReleaseComObject(dataObject);
return rv;
}
diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs
index 3066403186..7a96ca9ee0 100644
--- a/src/Windows/Avalonia.Win32/DataObject.cs
+++ b/src/Windows/Avalonia.Win32/DataObject.cs
@@ -8,22 +8,25 @@ using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
using Avalonia.Input;
+using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
+
+using FORMATETC = Avalonia.Win32.Interop.FORMATETC;
using IDataObject = Avalonia.Input.IDataObject;
namespace Avalonia.Win32
{
- class DataObject : IDataObject, IOleDataObject
+ internal sealed class DataObject : CallbackBase, IDataObject, Win32Com.IDataObject
{
// Compatibility with WinForms + WPF...
internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray();
- class FormatEnumerator : IEnumFORMATETC
+ class FormatEnumerator : CallbackBase, Win32Com.IEnumFORMATETC
{
private FORMATETC[] _formats;
- private int _current;
+ private uint _current;
- private FormatEnumerator(FORMATETC[] formats, int current)
+ private FormatEnumerator(FORMATETC[] formats, uint current)
{
_formats = formats;
_current = current;
@@ -46,59 +49,70 @@ namespace Avalonia.Win32
return result;
}
- public void Clone(out IEnumFORMATETC newEnum)
- {
- newEnum = new FormatEnumerator(_formats, _current);
- }
-
- public int Next(int celt, FORMATETC[] rgelt, int[] pceltFetched)
+ public unsafe uint Next(uint celt, FORMATETC* rgelt, uint* results)
{
if (rgelt == null)
- return unchecked((int)UnmanagedMethods.HRESULT.E_INVALIDARG);
+ return (uint)UnmanagedMethods.HRESULT.E_INVALIDARG;
- int i = 0;
+ uint i = 0;
while (i < celt && _current < _formats.Length)
{
rgelt[i] = _formats[_current];
_current++;
i++;
}
- if (pceltFetched != null)
- pceltFetched[0] = i;
if (i != celt)
- return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
- return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
+ return (uint)UnmanagedMethods.HRESULT.S_FALSE;
+
+ // "results" parameter can be NULL if celt is 1.
+ if (celt != 1 || results != default)
+ *results = i;
+ return 0;
+ }
+
+ public uint Skip(uint celt)
+ {
+ _current += Math.Min(celt, int.MaxValue - _current);
+ if (_current >= _formats.Length)
+ return (uint)UnmanagedMethods.HRESULT.S_FALSE;
+ return 0;
}
- public int Reset()
+ public void Reset()
{
_current = 0;
- return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
- public int Skip(int celt)
+ public Win32Com.IEnumFORMATETC Clone()
{
- _current += Math.Min(celt, int.MaxValue - _current);
- if (_current >= _formats.Length)
- return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
- return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
+ return new FormatEnumerator(_formats, _current);
}
}
- private const int DV_E_TYMED = unchecked((int)0x80040069);
- private const int DV_E_DVASPECT = unchecked((int)0x8004006B);
- private const int DV_E_FORMATETC = unchecked((int)0x80040064);
- private const int OLE_E_ADVISENOTSUPPORTED = unchecked((int)0x80040003);
- private const int STG_E_MEDIUMFULL = unchecked((int)0x80030070);
+ private const uint DV_E_TYMED = 0x80040069;
+ private const uint DV_E_DVASPECT = 0x8004006B;
+ private const uint DV_E_FORMATETC = 0x80040064;
+ private const uint OLE_E_ADVISENOTSUPPORTED = 0x80040003;
+ private const uint STG_E_MEDIUMFULL = 0x80030070;
+
private const int GMEM_ZEROINIT = 0x0040;
private const int GMEM_MOVEABLE = 0x0002;
- IDataObject _wrapped;
-
+ private IDataObject _wrapped;
+
public DataObject(IDataObject wrapped)
{
+ if (wrapped == null)
+ {
+ throw new ArgumentNullException(nameof(wrapped));
+ }
+ if (_wrapped is DataObject || _wrapped is OleDataObject)
+ {
+ throw new InvalidOperationException();
+ }
+
_wrapped = wrapped;
}
@@ -131,123 +145,123 @@ namespace Avalonia.Win32
#region IOleDataObject
- int IOleDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
+ unsafe int Win32Com.IDataObject.DAdvise(FORMATETC* pFormatetc, int advf, void* adviseSink)
{
- if (_wrapped is IOleDataObject ole)
- return ole.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
- connection = 0;
- return OLE_E_ADVISENOTSUPPORTED;
+ if (_wrapped is Win32Com.IDataObject ole)
+ return ole.DAdvise(pFormatetc, advf, adviseSink);
+ return 0;
}
- void IOleDataObject.DUnadvise(int connection)
+ void Win32Com.IDataObject.DUnadvise(int connection)
{
- if (_wrapped is IOleDataObject ole)
+ if (_wrapped is Win32Com.IDataObject ole)
ole.DUnadvise(connection);
- Marshal.ThrowExceptionForHR(OLE_E_ADVISENOTSUPPORTED);
+ throw new COMException(nameof(OLE_E_ADVISENOTSUPPORTED), unchecked((int)OLE_E_ADVISENOTSUPPORTED));
}
- int IOleDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise)
+ unsafe void* Win32Com.IDataObject.EnumDAdvise()
{
- if (_wrapped is IOleDataObject ole)
- return ole.EnumDAdvise(out enumAdvise);
+ if (_wrapped is Win32Com.IDataObject ole)
+ return ole.EnumDAdvise();
- enumAdvise = null;
- return OLE_E_ADVISENOTSUPPORTED;
+ return null;
}
- IEnumFORMATETC IOleDataObject.EnumFormatEtc(DATADIR direction)
+ Win32Com.IEnumFORMATETC Win32Com.IDataObject.EnumFormatEtc(int direction)
{
- if (_wrapped is IOleDataObject ole)
+ if (_wrapped is Win32Com.IDataObject ole)
return ole.EnumFormatEtc(direction);
- if (direction == DATADIR.DATADIR_GET)
+ if ((DATADIR)direction == DATADIR.DATADIR_GET)
return new FormatEnumerator(_wrapped);
- throw new NotSupportedException();
+ throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
}
- int IOleDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
+ unsafe FORMATETC Win32Com.IDataObject.GetCanonicalFormatEtc(FORMATETC* formatIn)
{
- if (_wrapped is IOleDataObject ole)
- return ole.GetCanonicalFormatEtc(ref formatIn, out formatOut);
+ if (_wrapped is Win32Com.IDataObject ole)
+ return ole.GetCanonicalFormatEtc(formatIn);
- formatOut = new FORMATETC();
+ var formatOut = new FORMATETC();
formatOut.ptd = IntPtr.Zero;
- return unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL);
+
+ throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
}
- void IOleDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
+ unsafe uint Win32Com.IDataObject.GetData(FORMATETC* format, Interop.STGMEDIUM* medium)
{
- if (_wrapped is IOleDataObject ole)
+ if (_wrapped is Win32Com.IDataObject ole)
{
- ole.GetData(ref format, out medium);
- return;
+ return ole.GetData(format, medium);
}
- if(!format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
- Marshal.ThrowExceptionForHR(DV_E_TYMED);
- if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
- Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
+ if (!format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
+ return DV_E_TYMED;
+
+ if (format->dwAspect != DVASPECT.DVASPECT_CONTENT)
+ return DV_E_DVASPECT;
- string fmt = ClipboardFormats.GetFormat(format.cfFormat);
+ string fmt = ClipboardFormats.GetFormat(format->cfFormat);
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
- Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
+ return DV_E_FORMATETC;
- medium = default(STGMEDIUM);
- medium.tymed = TYMED.TYMED_HGLOBAL;
- int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
- Marshal.ThrowExceptionForHR(result);
+ * medium = default(Interop.STGMEDIUM);
+ medium->tymed = TYMED.TYMED_HGLOBAL;
+ return WriteDataToHGlobal(fmt, ref medium->unionmember);
}
- void IOleDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
+ unsafe uint Win32Com.IDataObject.GetDataHere(FORMATETC* format, Interop.STGMEDIUM* medium)
{
- if (_wrapped is IOleDataObject ole)
+ if (_wrapped is Win32Com.IDataObject ole)
{
- ole.GetDataHere(ref format, ref medium);
- return;
+ return ole.GetDataHere(format, medium);
}
- if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
- Marshal.ThrowExceptionForHR(DV_E_TYMED);
+ if (medium->tymed != TYMED.TYMED_HGLOBAL || !format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
+ return DV_E_TYMED;
- if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
- Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
+ if (format->dwAspect != DVASPECT.DVASPECT_CONTENT)
+ return DV_E_DVASPECT;
- string fmt = ClipboardFormats.GetFormat(format.cfFormat);
+ string fmt = ClipboardFormats.GetFormat(format->cfFormat);
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
- Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
+ return DV_E_FORMATETC;
- if (medium.unionmember == IntPtr.Zero)
- Marshal.ThrowExceptionForHR(STG_E_MEDIUMFULL);
+ if (medium->unionmember == IntPtr.Zero)
+ return STG_E_MEDIUMFULL;
- int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
- Marshal.ThrowExceptionForHR(result);
+ return WriteDataToHGlobal(fmt, ref medium->unionmember);
}
- int IOleDataObject.QueryGetData(ref FORMATETC format)
+ unsafe uint Win32Com.IDataObject.QueryGetData(FORMATETC* format)
{
- if (_wrapped is IOleDataObject ole)
- return ole.QueryGetData(ref format);
- if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
+ if (_wrapped is Win32Com.IDataObject ole)
+ {
+ return ole.QueryGetData(format);
+ }
+
+ if (format->dwAspect != DVASPECT.DVASPECT_CONTENT)
return DV_E_DVASPECT;
- if (!format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
+ if (!format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
return DV_E_TYMED;
- string dataFormat = ClipboardFormats.GetFormat(format.cfFormat);
- if (!string.IsNullOrEmpty(dataFormat) && _wrapped.Contains(dataFormat))
- return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
- return DV_E_FORMATETC;
+ var dataFormat = ClipboardFormats.GetFormat(format->cfFormat);
+
+ if (string.IsNullOrEmpty(dataFormat) || !_wrapped.Contains(dataFormat))
+ return DV_E_FORMATETC;
+
+ return 0;
}
- void IOleDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
+ unsafe uint Win32Com.IDataObject.SetData(FORMATETC* pformatetc, Interop.STGMEDIUM* pmedium, int fRelease)
{
- if (_wrapped is IOleDataObject ole)
+ if (_wrapped is Win32Com.IDataObject ole)
{
- ole.SetData(ref formatIn, ref medium, release);
- return;
+ return ole.SetData(pformatetc, pmedium, fRelease);
}
- Marshal.ThrowExceptionForHR(unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
+ return (uint)UnmanagedMethods.HRESULT.E_NOTIMPL;
}
- private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal)
+ private uint WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal)
{
object data = _wrapped.Get(dataFormat);
if (dataFormat == DataFormats.Text || data is string)
@@ -288,7 +302,7 @@ namespace Avalonia.Win32
}
}
- private unsafe int WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan data)
+ private unsafe uint WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan data)
{
int required = data.Length;
if (hGlobal == IntPtr.Zero)
@@ -312,7 +326,7 @@ namespace Avalonia.Win32
}
}
- private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable files)
+ private uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable files)
{
if (!files?.Any() ?? false)
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
@@ -344,7 +358,7 @@ namespace Avalonia.Win32
}
}
- private int WriteStringToHGlobal(ref IntPtr hGlobal, string data)
+ private uint WriteStringToHGlobal(ref IntPtr hGlobal, string data)
{
int required = (data.Length + 1) * sizeof(char);
if (hGlobal == IntPtr.Zero)
@@ -367,6 +381,15 @@ namespace Avalonia.Win32
}
}
+ protected override void Destroyed()
+ {
+ ReleaseWrapped();
+ }
+
+ public void ReleaseWrapped()
+ {
+ _wrapped = null;
+ }
#endregion
}
}
diff --git a/src/Windows/Avalonia.Win32/DragSource.cs b/src/Windows/Avalonia.Win32/DragSource.cs
index f74e59a8f8..1159c5bfc9 100644
--- a/src/Windows/Avalonia.Win32/DragSource.cs
+++ b/src/Windows/Avalonia.Win32/DragSource.cs
@@ -8,17 +8,26 @@ namespace Avalonia.Win32
{
class DragSource : IPlatformDragSource
{
- public Task DoDragDrop(PointerEventArgs triggerEvent,
+ public unsafe Task DoDragDrop(PointerEventArgs triggerEvent,
IDataObject data, DragDropEffects allowedEffects)
{
Dispatcher.UIThread.VerifyAccess();
+
triggerEvent.Pointer.Capture(null);
- OleDragSource src = new OleDragSource();
- DataObject dataObject = new DataObject(data);
- int allowed = (int)OleDropTarget.ConvertDropEffect(allowedEffects);
+
+ using var dataObject = new DataObject(data);
+ using var src = new OleDragSource();
+ var allowed = OleDropTarget.ConvertDropEffect(allowedEffects);
+
+ var objPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(dataObject);
+ var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(src);
+
+ UnmanagedMethods.DoDragDrop(objPtr, srcPtr, (int)allowed, out var finalEffect);
+
+ // Force releasing of internal wrapper to avoid memory leak, if drop target keeps com reference.
+ dataObject.ReleaseWrapped();
- UnmanagedMethods.DoDragDrop(dataObject, src, allowed, out var finalEffect);
- return Task.FromResult(OleDropTarget.ConvertDropEffect((DropEffect)finalEffect));
+ return Task.FromResult(OleDropTarget.ConvertDropEffect((Win32Com.DropEffect)finalEffect));
}
}
}
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index 1809fcf98b..f7838c9f46 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -1277,10 +1277,10 @@ namespace Avalonia.Win32.Interop
public static extern IntPtr SetClipboardData(ClipboardFormat uFormat, IntPtr hMem);
[DllImport("ole32.dll", PreserveSig = false)]
- public static extern int OleGetClipboard(out IOleDataObject dataObject);
+ public static extern int OleGetClipboard(out IntPtr dataObject);
[DllImport("ole32.dll", PreserveSig = true)]
- public static extern int OleSetClipboard(IOleDataObject dataObject);
+ public static extern int OleSetClipboard(IntPtr dataObject);
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GlobalLock(IntPtr handle);
@@ -1424,7 +1424,7 @@ namespace Avalonia.Win32.Interop
public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
- public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IDropTarget target);
+ public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IntPtr target);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern HRESULT RevokeDragDrop(IntPtr hwnd);
@@ -1448,7 +1448,7 @@ namespace Avalonia.Win32.Interop
public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
- internal static extern void DoDragDrop(IOleDataObject dataObject, IDropSource dropSource, int allowedEffects, out int finalEffect);
+ internal static extern void DoDragDrop(IntPtr dataObject, IntPtr dropSource, int allowedEffects, [Out] out int finalEffect);
[DllImport("dwmapi.dll")]
public static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
@@ -2064,64 +2064,6 @@ namespace Avalonia.Win32.Interop
}
}
- [Flags]
- internal enum DropEffect : int
- {
- None = 0,
- Copy = 1,
- Move = 2,
- Link = 4,
- Scroll = -2147483648,
- }
-
- [ComImport]
- [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- [Guid("00000122-0000-0000-C000-000000000046")]
- internal interface IDropTarget
- {
- [PreserveSig]
- UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
- [PreserveSig]
- UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
- [PreserveSig]
- UnmanagedMethods.HRESULT DragLeave();
- [PreserveSig]
- UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
- }
-
- [ComImport]
- [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- [Guid("00000121-0000-0000-C000-000000000046")]
- internal interface IDropSource
- {
- [PreserveSig]
- int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState);
- [PreserveSig]
- int GiveFeedback([MarshalAs(UnmanagedType.U4)] [In] int dwEffect);
- }
-
-
- [ComImport]
- [Guid("0000010E-0000-0000-C000-000000000046")]
- [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
- internal interface IOleDataObject
- {
- void GetData([In] ref FORMATETC format, out STGMEDIUM medium);
- void GetDataHere([In] ref FORMATETC format, ref STGMEDIUM medium);
- [PreserveSig]
- int QueryGetData([In] ref FORMATETC format);
- [PreserveSig]
- int GetCanonicalFormatEtc([In] ref FORMATETC formatIn, out FORMATETC formatOut);
- void SetData([In] ref FORMATETC formatIn, [In] ref STGMEDIUM medium, [MarshalAs(UnmanagedType.Bool)] bool release);
- IEnumFORMATETC EnumFormatEtc(DATADIR direction);
- [PreserveSig]
- int DAdvise([In] ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection);
- void DUnadvise(int connection);
- [PreserveSig]
- int EnumDAdvise(out IEnumSTATDATA enumAdvise);
- }
-
-
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct _DROPFILES
{
@@ -2132,6 +2074,24 @@ namespace Avalonia.Win32.Interop
public bool fWide;
}
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct STGMEDIUM
+ {
+ public TYMED tymed;
+ public IntPtr unionmember;
+ public IntPtr pUnkForRelease;
+ }
+
+ [StructLayoutAttribute(LayoutKind.Sequential)]
+ internal struct FORMATETC
+ {
+ public ushort cfFormat;
+ public IntPtr ptd;
+ public DVASPECT dwAspect;
+ public int lindex;
+ public TYMED tymed;
+ }
+
[Flags]
internal enum PixelFormatDescriptorFlags : uint
{
diff --git a/src/Windows/Avalonia.Win32/OleContext.cs b/src/Windows/Avalonia.Win32/OleContext.cs
index c6e04a29b4..c025d06fe7 100644
--- a/src/Windows/Avalonia.Win32/OleContext.cs
+++ b/src/Windows/Avalonia.Win32/OleContext.cs
@@ -4,6 +4,7 @@ using System.Threading;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
+using Avalonia.Win32.Win32Com;
namespace Avalonia.Win32
{
@@ -46,7 +47,8 @@ namespace Avalonia.Win32
return false;
}
- return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, target) == UnmanagedMethods.HRESULT.S_OK;
+ var trgPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(target);
+ return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, trgPtr) == UnmanagedMethods.HRESULT.S_OK;
}
internal bool UnregisterDragDrop(IPlatformHandle hwnd)
diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs
index f111fe0a6b..ba17177473 100644
--- a/src/Windows/Avalonia.Win32/OleDataObject.cs
+++ b/src/Windows/Avalonia.Win32/OleDataObject.cs
@@ -1,7 +1,6 @@
using System;
using System.Buffers;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -9,17 +8,20 @@ using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using Avalonia.Input;
+using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
+using IDataObject = Avalonia.Input.IDataObject;
+
namespace Avalonia.Win32
{
- class OleDataObject : Avalonia.Input.IDataObject
+ internal class OleDataObject : IDataObject, IDisposable
{
- private IOleDataObject _wrapped;
+ private readonly Win32Com.IDataObject _wrapped;
- public OleDataObject(IOleDataObject wrapped)
+ public OleDataObject(Win32Com.IDataObject wrapped)
{
- _wrapped = wrapped;
+ _wrapped = wrapped.CloneReference();
}
public bool Contains(string dataFormat)
@@ -34,12 +36,12 @@ namespace Avalonia.Win32
public string GetText()
{
- return GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT) as string;
+ return (string)GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT);
}
public IEnumerable GetFileNames()
{
- return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable;
+ return (IEnumerable)GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT);
}
public object Get(string dataFormat)
@@ -47,16 +49,17 @@ namespace Avalonia.Win32
return GetDataFromOleHGLOBAL(dataFormat, DVASPECT.DVASPECT_CONTENT);
}
- private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect)
+ private unsafe object GetDataFromOleHGLOBAL(string format, DVASPECT aspect)
{
- FORMATETC formatEtc = new FORMATETC();
+ var formatEtc = new Interop.FORMATETC();
formatEtc.cfFormat = ClipboardFormats.GetFormat(format);
formatEtc.dwAspect = aspect;
formatEtc.lindex = -1;
formatEtc.tymed = TYMED.TYMED_HGLOBAL;
- if (_wrapped.QueryGetData(ref formatEtc) == 0)
+ if (_wrapped.QueryGetData(&formatEtc) == 0)
{
- _wrapped.GetData(ref formatEtc, out STGMEDIUM medium);
+ Interop.STGMEDIUM medium = default;
+ _ = _wrapped.GetData(&formatEtc, &medium);
try
{
if (medium.unionmember != IntPtr.Zero && medium.tymed == TYMED.TYMED_HGLOBAL)
@@ -140,38 +143,53 @@ namespace Avalonia.Win32
}
}
- private IEnumerable GetDataFormatsCore()
+ private unsafe IEnumerable GetDataFormatsCore()
{
- var enumFormat = _wrapped.EnumFormatEtc(DATADIR.DATADIR_GET);
+ var formatsList = new List();
+ var enumFormat = _wrapped.EnumFormatEtc((int)DATADIR.DATADIR_GET);
if (enumFormat != null)
{
enumFormat.Reset();
- var formats = ArrayPool.Shared.Rent(1);
- var fetched = ArrayPool.Shared.Rent(1);
+ var formats = ArrayPool.Shared.Rent(1);
try
{
+ uint fetched = 0;
do
{
- fetched[0] = 0;
- if (enumFormat.Next(1, formats, fetched) == 0 && fetched[0] > 0)
+ fixed (Interop.FORMATETC* formatsPtr = formats)
+ {
+ // Read one item at a time.
+ // When "celt" parameter is 1, "pceltFetched" is ignored.
+ var res = enumFormat.Next(1, formatsPtr, &fetched);
+ if (res != 0)
+ {
+ break;
+ }
+ }
+ if (fetched > 0)
{
if (formats[0].ptd != IntPtr.Zero)
Marshal.FreeCoTaskMem(formats[0].ptd);
- yield return ClipboardFormats.GetFormat(formats[0].cfFormat);
+ formatsList.Add(ClipboardFormats.GetFormat(formats[0].cfFormat));
}
}
- while (fetched[0] > 0);
+ while (fetched > 0);
}
finally
{
- ArrayPool.Shared.Return(formats);
- ArrayPool.Shared.Return(fetched);
+ ArrayPool.Shared.Return(formats);
}
}
+ return formatsList;
+ }
+
+ public void Dispose()
+ {
+ _wrapped.Dispose();
}
}
}
diff --git a/src/Windows/Avalonia.Win32/OleDragSource.cs b/src/Windows/Avalonia.Win32/OleDragSource.cs
index c903a9ca57..30503339da 100644
--- a/src/Windows/Avalonia.Win32/OleDragSource.cs
+++ b/src/Windows/Avalonia.Win32/OleDragSource.cs
@@ -1,9 +1,12 @@
using System.Linq;
+
+using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
+using Avalonia.Win32.Win32Com;
namespace Avalonia.Win32
{
- class OleDragSource : IDropSource
+ internal class OleDragSource : CallbackBase, IDropSource
{
private const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102;
private const int DRAGDROP_S_DROP = 0x00040100;
@@ -30,7 +33,7 @@ namespace Avalonia.Win32
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
- public int GiveFeedback(int dwEffect)
+ public int GiveFeedback(DropEffect dwEffect)
{
return DRAGDROP_S_USEDEFAULTCURSORS;
}
diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs
index d8cb52a914..3d0d35228c 100644
--- a/src/Windows/Avalonia.Win32/OleDropTarget.cs
+++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs
@@ -1,12 +1,15 @@
-using Avalonia.Input;
+using System;
+
+using Avalonia.Input;
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
{
- internal class OleDropTarget : IDropTarget
+ internal class OleDropTarget : CallbackBase, Win32Com.IDropTarget
{
private readonly IInputRoot _target;
private readonly ITopLevelImpl _tl;
@@ -65,58 +68,51 @@ namespace Avalonia.Win32
return modifiers;
}
- UnmanagedMethods.HRESULT IDropTarget.DragEnter(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect)
+ unsafe void Win32Com.IDropTarget.DragEnter(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
- pdwEffect = DropEffect.None;
- return UnmanagedMethods.HRESULT.S_OK;
+ *pdwEffect= (int)DropEffect.None;
}
- _currentDrag = pDataObj as IDataObject;
- if (_currentDrag == null)
- _currentDrag = new OleDataObject(pDataObj);
+
+ SetDataObject(pDataObj);
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragEnter,
- _target,
- GetDragLocation(pt),
+ _target,
+ GetDragLocation(pt),
_currentDrag,
- ConvertDropEffect(pdwEffect),
+ ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
- pdwEffect = ConvertDropEffect(args.Effects);
-
- return UnmanagedMethods.HRESULT.S_OK;
+ *pdwEffect = ConvertDropEffect(args.Effects);
}
- UnmanagedMethods.HRESULT IDropTarget.DragOver(int grfKeyState, long pt, ref DropEffect pdwEffect)
+ unsafe void Win32Com.IDropTarget.DragOver(int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
- pdwEffect = DropEffect.None;
- return UnmanagedMethods.HRESULT.S_OK;
+ *pdwEffect = (int)DropEffect.None;
}
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragOver,
- _target,
- GetDragLocation(pt),
- _currentDrag,
- ConvertDropEffect(pdwEffect),
+ _target,
+ GetDragLocation(pt),
+ _currentDrag,
+ ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
- pdwEffect = ConvertDropEffect(args.Effects);
-
- return UnmanagedMethods.HRESULT.S_OK;
+ *pdwEffect = ConvertDropEffect(args.Effects);
}
- UnmanagedMethods.HRESULT IDropTarget.DragLeave()
+ void Win32Com.IDropTarget.DragLeave()
{
try
{
@@ -129,56 +125,91 @@ namespace Avalonia.Win32
DragDropEffects.None,
RawInputModifiers.None
));
- return UnmanagedMethods.HRESULT.S_OK;
}
finally
{
- _currentDrag = null;
+ ReleaseDataObject();
}
}
- UnmanagedMethods.HRESULT IDropTarget.Drop(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect)
+ unsafe void Win32Com.IDropTarget.Drop(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
try
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
- pdwEffect = DropEffect.None;
- return UnmanagedMethods.HRESULT.S_OK;
+ *pdwEffect = (int)DropEffect.None;
}
- _currentDrag = pDataObj as IDataObject;
- if (_currentDrag == null)
- _currentDrag= new OleDataObject(pDataObj);
-
+ SetDataObject(pDataObj);
+
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.Drop,
- _target,
- GetDragLocation(pt),
- _currentDrag,
- ConvertDropEffect(pdwEffect),
+ _target,
+ GetDragLocation(pt),
+ _currentDrag,
+ ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
- pdwEffect = ConvertDropEffect(args.Effects);
-
- return UnmanagedMethods.HRESULT.S_OK;
+ *pdwEffect = ConvertDropEffect(args.Effects);
}
finally
{
- _currentDrag = null;
+ ReleaseDataObject();
}
}
- private Point GetDragLocation(long dragPoint)
+ private void SetDataObject(Win32Com.IDataObject pDataObj)
{
- int x = (int)dragPoint;
- int y = (int)(dragPoint >> 32);
+ var newDrag = GetAvaloniaObjectFromCOM(pDataObj);
+ if (_currentDrag != newDrag)
+ {
+ ReleaseDataObject();
+ _currentDrag = newDrag;
+ }
+ }
- var screenPt = new PixelPoint(x, y);
+ private void ReleaseDataObject()
+ {
+ // OleDataObject keeps COM reference, so it should be disposed.
+ if (_currentDrag is OleDataObject oleDragSource)
+ {
+ oleDragSource?.Dispose();
+ _currentDrag = null;
+ }
+ }
+
+ private Point GetDragLocation(UnmanagedMethods.POINT dragPoint)
+ {
+ var screenPt = new PixelPoint(dragPoint.X, dragPoint.Y);
return _target.PointToClient(screenPt);
}
+
+ protected override void Destroyed()
+ {
+ ReleaseDataObject();
+ }
+
+ public static unsafe IDataObject GetAvaloniaObjectFromCOM(Win32Com.IDataObject pDataObj)
+ {
+ if (pDataObj is null)
+ {
+ throw new ArgumentNullException(nameof(pDataObj));
+ }
+ if (pDataObj is IDataObject disposableDataObject)
+ {
+ return disposableDataObject;
+ }
+
+ var dataObject = MicroComRuntime.TryUnwrapManagedObject(pDataObj) as DataObject;
+ if (dataObject is not null)
+ {
+ return dataObject;
+ }
+ return new OleDataObject(pDataObj);
+ }
}
}
diff --git a/src/Windows/Avalonia.Win32/Win32Com/win32.idl b/src/Windows/Avalonia.Win32/Win32Com/win32.idl
index 54e9aa583f..689f5b7a2e 100644
--- a/src/Windows/Avalonia.Win32/Win32Com/win32.idl
+++ b/src/Windows/Avalonia.Win32/Win32Com/win32.idl
@@ -27,6 +27,8 @@
@clr-map REFGUID System.Guid*
@clr-map REFIID System.Guid*
@clr-map WCHAR System.Char
+@clr-map STGMEDIUM Avalonia.Win32.Interop.STGMEDIUM
+@clr-map FORMATETC Avalonia.Win32.Interop.FORMATETC
[flags]
enum FILEOPENDIALOGOPTIONS
@@ -53,6 +55,16 @@ enum FILEOPENDIALOGOPTIONS
FOS_DEFAULTNOMINIMODE = 0x20000000
}
+[flags]
+enum DropEffect
+{
+ None = 0,
+ Copy = 1,
+ Move = 2,
+ Link = 4,
+ Scroll = -2147483648,
+}
+
[
object,
uuid(43826d1e-e718-42ee-bc55-a1e261c37bfe),
@@ -189,3 +201,114 @@ interface IFileOpenDialog : IFileDialog
HRESULT GetSelectedItems(
[out] IShellItemArray** ppsai);
}
+
+[
+ object,
+ uuid(00000103-0000-0000-C000-000000000046),
+ pointer_default(unique)
+]
+interface IEnumFORMATETC : IUnknown
+{
+ UINT32 Next(
+ [in] ULONG celt,
+ [out] FORMATETC* rgelt,
+ ULONG* pceltFetched);
+
+ UINT32 Skip(
+ [in] ULONG celt);
+
+ HRESULT Reset();
+
+ HRESULT Clone(
+ [out] IEnumFORMATETC** ppenum);
+}
+
+[
+ object,
+ uuid(0000010e-0000-0000-C000-000000000046),
+ pointer_default(unique)
+]
+interface IDataObject : IUnknown
+{
+ UINT32 GetData(
+ [in, unique] FORMATETC* pformatetcIn,
+ [out] STGMEDIUM* pmedium);
+
+ UINT32 GetDataHere(
+ [in, unique] FORMATETC* pformatetc,
+ [in] STGMEDIUM* pmedium);
+
+ UINT32 QueryGetData(
+ [in, unique] FORMATETC* pformatetc);
+
+ HRESULT GetCanonicalFormatEtc(
+ [in, unique] FORMATETC* pformatectIn,
+ [out] FORMATETC* pformatetcOut);
+
+ UINT32 SetData(
+ [in, unique] FORMATETC* pformatetc,
+ [in, unique] STGMEDIUM* pmedium,
+ [in] BOOL fRelease);
+
+ HRESULT EnumFormatEtc(
+ [in] DWORD dwDirection,
+ [out] IEnumFORMATETC** ppenumFormatEtc);
+
+ HRESULT DAdvise(
+ [in] FORMATETC* pformatetc,
+ [in] DWORD advf,
+ [in, unique] void* pAdvSink,
+ [out] DWORD* pdwConnection);
+
+ HRESULT DUnadvise(
+ [in] DWORD dwConnection);
+
+ HRESULT EnumDAdvise(
+ [out] void** ppenumAdvise);
+}
+
+[
+ local,
+ object,
+ uuid(00000121-0000-0000-C000-000000000046)
+]
+
+interface IDropSource : IUnknown
+{
+ INT32 QueryContinueDrag([in] BOOL fEscapePressed, [in] DWORD grfKeyState);
+
+ INT32 GiveFeedback([in] DropEffect dwEffect);
+}
+
+[
+ object,
+ uuid(00000122-0000-0000-C000-000000000046),
+ pointer_default(unique)
+]
+interface IDropTarget : IUnknown
+{
+ HRESULT DragEnter
+ (
+ [in, unique] IDataObject* pDataObj,
+ [in] DWORD grfKeyState,
+ [in] POINT pt,
+ [in] DropEffect* pdwEffect
+ );
+
+ HRESULT DragOver
+ (
+ [in] DWORD grfKeyState,
+ [in] POINT pt,
+ [in] DropEffect* pdwEffect
+ );
+
+ HRESULT DragLeave();
+
+ HRESULT Drop
+ (
+ [in, unique] IDataObject* pDataObj,
+ [in] DWORD grfKeyState,
+ [in] POINT pt,
+ [in] DropEffect* pdwEffect
+ );
+}
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index d1945e6c85..e8a149c918 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;
}