diff --git a/src/Windows/Avalonia.Win32/ClipboardFormats.cs b/src/Windows/Avalonia.Win32/ClipboardFormats.cs index a18116aade..05eed32d7d 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 ClipboardFormat(string name, short format, params short[] synthesized) + public ClipboardFormat(string name, ushort format, params short[] 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, (short)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..adf0ca64bb 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, true); + 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, false); + 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, false); + 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..a62f75f5b8 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -8,22 +8,50 @@ 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 static class DataObjectEx + { + public static unsafe IDataObject GetAvaloniaObject(this Win32Com.IDataObject pDataObj) + { + switch (pDataObj) + { + 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); + } + } + } + + internal class DataObject : IDataObject, 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 : IEnumFORMATETC + class FormatEnumerator : Win32Com.IEnumFORMATETC, IMicroComShadowContainer { private FORMATETC[] _formats; - private int _current; + private uint _current; - private FormatEnumerator(FORMATETC[] formats, int current) + public MicroComShadow Shadow { get; set; } + + private FormatEnumerator(FORMATETC[] formats, uint current) { _formats = formats; _current = current; @@ -38,7 +66,7 @@ namespace Avalonia.Win32 private FORMATETC ConvertToFormatEtc(string aFormatName) { FORMATETC result = default(FORMATETC); - result.cfFormat = ClipboardFormats.GetFormat(aFormatName); + result.cfFormat = (ushort)ClipboardFormats.GetFormat(aFormatName); result.dwAspect = DVASPECT.DVASPECT_CONTENT; result.ptd = IntPtr.Zero; result.lindex = -1; @@ -46,59 +74,80 @@ 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 int Reset() + 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 void Reset() { _current = 0; - return unchecked((int)UnmanagedMethods.HRESULT.S_OK); } - public int Skip(int celt) + public Win32Com.IEnumFORMATETC Clone() + { + return new FormatEnumerator(_formats, _current); + } + + public void Dispose() + { + } + + public void OnReferencedFromNative() + { + } + + public void OnUnreferencedFromNative() { - _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); } } - 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; - + + public MicroComShadow Shadow { get; set; } + public DataObject(IDataObject wrapped) { + if (_wrapped is DataObject) + { + throw new InvalidOperationException(); + } + _wrapped = wrapped; } @@ -131,123 +180,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 +337,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 +361,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 +393,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 +416,18 @@ namespace Avalonia.Win32 } } + public void Dispose() + { + } + + public void OnReferencedFromNative() + { + } + + public void OnUnreferencedFromNative() + { + } + #endregion } } diff --git a/src/Windows/Avalonia.Win32/DragSource.cs b/src/Windows/Avalonia.Win32/DragSource.cs index f74e59a8f8..3160dfd819 100644 --- a/src/Windows/Avalonia.Win32/DragSource.cs +++ b/src/Windows/Avalonia.Win32/DragSource.cs @@ -8,17 +8,22 @@ 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); + + var dataObject = new DataObject(data); + var src = new OleDragSource(); + var allowed = OleDropTarget.ConvertDropEffect(allowedEffects); + + var objPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(dataObject, true); + var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(src, true); - UnmanagedMethods.DoDragDrop(dataObject, src, allowed, out var finalEffect); - return Task.FromResult(OleDropTarget.ConvertDropEffect((DropEffect)finalEffect)); + UnmanagedMethods.DoDragDrop(objPtr, srcPtr, allowed, out var finalEffect); + return Task.FromResult(OleDropTarget.ConvertDropEffect(finalEffect)); } } } diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index c74c5fbc01..930216e8bc 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, DropEffect allowedEffects, [Out] out DropEffect 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..a3641264ec 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, false); + 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..5626a0a675 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; @@ -13,11 +12,11 @@ using Avalonia.Win32.Interop; namespace Avalonia.Win32 { - class OleDataObject : Avalonia.Input.IDataObject + internal class OleDataObject : Avalonia.Input.IDataObject { - private IOleDataObject _wrapped; + private readonly Win32Com.IDataObject _wrapped; - public OleDataObject(IOleDataObject wrapped) + public OleDataObject(Win32Com.IDataObject wrapped) { _wrapped = wrapped; } @@ -34,12 +33,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 +46,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 +140,48 @@ 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; } } } diff --git a/src/Windows/Avalonia.Win32/OleDragSource.cs b/src/Windows/Avalonia.Win32/OleDragSource.cs index c903a9ca57..dc5c561226 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 : IDropSource, IMicroComShadowContainer { private const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102; private const int DRAGDROP_S_DROP = 0x00040100; @@ -15,6 +18,8 @@ namespace Avalonia.Win32 (int)UnmanagedMethods.ModifierKeys.MK_RBUTTON }; + public MicroComShadow Shadow { get; set; } + public int QueryContinueDrag(int fEscapePressed, int grfKeyState) { if (fEscapePressed != 0) @@ -30,9 +35,21 @@ namespace Avalonia.Win32 return unchecked((int)UnmanagedMethods.HRESULT.S_OK); } - public int GiveFeedback(int dwEffect) + public int GiveFeedback(DropEffect dwEffect) { return DRAGDROP_S_USEDEFAULTCURSORS; } + + public void Dispose() + { + } + + public void OnReferencedFromNative() + { + } + + public void OnUnreferencedFromNative() + { + } } } diff --git a/src/Windows/Avalonia.Win32/OleDropTarget.cs b/src/Windows/Avalonia.Win32/OleDropTarget.cs index d8cb52a914..3767dbd50c 100644 --- a/src/Windows/Avalonia.Win32/OleDropTarget.cs +++ b/src/Windows/Avalonia.Win32/OleDropTarget.cs @@ -1,12 +1,14 @@ 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 : Win32Com.IDropTarget, IMicroComShadowContainer { private readonly IInputRoot _target; private readonly ITopLevelImpl _tl; @@ -14,6 +16,8 @@ namespace Avalonia.Win32 private IDataObject _currentDrag = null; + public MicroComShadow Shadow { get; set; } + public OleDropTarget(ITopLevelImpl tl, IInputRoot target) { _dragDevice = AvaloniaLocator.Current.GetService(); @@ -65,58 +69,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); + + _currentDrag = pDataObj.GetAvaloniaObject(); 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,7 +126,6 @@ namespace Avalonia.Win32 DragDropEffects.None, RawInputModifiers.None )); - return UnmanagedMethods.HRESULT.S_OK; } finally { @@ -137,34 +133,29 @@ namespace Avalonia.Win32 } } - 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); - + _currentDrag = pDataObj.GetAvaloniaObject(); + 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 { @@ -172,13 +163,22 @@ namespace Avalonia.Win32 } } - private Point GetDragLocation(long dragPoint) + private Point GetDragLocation(UnmanagedMethods.POINT dragPoint) { - int x = (int)dragPoint; - int y = (int)(dragPoint >> 32); - - var screenPt = new PixelPoint(x, y); + var screenPt = new PixelPoint(dragPoint.X, dragPoint.Y); return _target.PointToClient(screenPt); } + + public void Dispose() + { + } + + public void OnReferencedFromNative() + { + } + + public void OnUnreferencedFromNative() + { + } } } diff --git a/src/Windows/Avalonia.Win32/Win32Com/win32.idl b/src/Windows/Avalonia.Win32/Win32Com/win32.idl index 904ac41ff2..8a1941b4fd 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,119 @@ 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); +} + +[uuid(3d74307e-12be-460a-b098-4dd320eec7d5)] +interface IAvnDataObject : IDataObject +{ +} + +[ + 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 + ); +}