Browse Source

MicroCOM Drag'n'Drop and Clipboard

pull/7506/head
Max Katz 4 years ago
parent
commit
ecd6192806
  1. 16
      src/Windows/Avalonia.Win32/ClipboardFormats.cs
  2. 13
      src/Windows/Avalonia.Win32/ClipboardImpl.cs
  3. 257
      src/Windows/Avalonia.Win32/DataObject.cs
  4. 17
      src/Windows/Avalonia.Win32/DragSource.cs
  5. 84
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  6. 4
      src/Windows/Avalonia.Win32/OleContext.cs
  7. 50
      src/Windows/Avalonia.Win32/OleDataObject.cs
  8. 21
      src/Windows/Avalonia.Win32/OleDragSource.cs
  9. 88
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  10. 128
      src/Windows/Avalonia.Win32/Win32Com/win32.idl

16
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<ClipboardFormat> FormatList = new List<ClipboardFormat>()
{
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;

13
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<Win32Com.IDataObject>(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<Win32Com.IDataObject>(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<Win32Com.IDataObject>(dataObject, false);
var wrapper = new OleDataObject(proxy);
var rv = wrapper.Get(format);
Marshal.ReleaseComObject(dataObject);
return rv;
}

257
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<IntPtr, IDataObject> _avnDataObject = new Dictionary<IntPtr, IDataObject>();
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<byte> data)
private unsafe uint WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan<byte> data)
{
int required = data.Length;
if (hGlobal == IntPtr.Zero)
@ -312,7 +361,7 @@ namespace Avalonia.Win32
}
}
private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files)
private uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> 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
}
}

17
src/Windows/Avalonia.Win32/DragSource.cs

@ -8,17 +8,22 @@ namespace Avalonia.Win32
{
class DragSource : IPlatformDragSource
{
public Task<DragDropEffects> DoDragDrop(PointerEventArgs triggerEvent,
public unsafe Task<DragDropEffects> 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<Win32Com.IDataObject>(dataObject, true);
var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr<Win32Com.IDropSource>(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));
}
}
}

84
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
{

4
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)

50
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<string> GetFileNames()
{
return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable<string>;
return (IEnumerable<string>)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<string> GetDataFormatsCore()
private unsafe IEnumerable<string> GetDataFormatsCore()
{
var enumFormat = _wrapped.EnumFormatEtc(DATADIR.DATADIR_GET);
var formatsList = new List<string>();
var enumFormat = _wrapped.EnumFormatEtc((int)DATADIR.DATADIR_GET);
if (enumFormat != null)
{
enumFormat.Reset();
var formats = ArrayPool<FORMATETC>.Shared.Rent(1);
var fetched = ArrayPool<int>.Shared.Rent(1);
var formats = ArrayPool<Interop.FORMATETC>.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<FORMATETC>.Shared.Return(formats);
ArrayPool<int>.Shared.Return(fetched);
ArrayPool<Interop.FORMATETC>.Shared.Return(formats);
}
}
return formatsList;
}
}
}

21
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()
{
}
}
}

88
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<IDragDropDevice>();
@ -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()
{
}
}
}

128
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
);
}

Loading…
Cancel
Save