8 changed files with 437 additions and 20 deletions
@ -0,0 +1,312 @@ |
|||
using System; |
|||
using System.Linq; |
|||
using System.Collections.Generic; |
|||
using System.Runtime.InteropServices; |
|||
using System.Runtime.InteropServices.ComTypes; |
|||
using System.Text; |
|||
using Avalonia.Controls.DragDrop; |
|||
using Avalonia.Win32.Interop; |
|||
using IDataObject = Avalonia.Controls.DragDrop.IDataObject; |
|||
using IOleDataObject = System.Runtime.InteropServices.ComTypes.IDataObject; |
|||
|
|||
namespace Avalonia.Win32 |
|||
{ |
|||
class DataObject : IDataObject, IOleDataObject |
|||
{ |
|||
class FormatEnumerator : IEnumFORMATETC |
|||
{ |
|||
private FORMATETC[] _formats; |
|||
private int _current; |
|||
|
|||
private FormatEnumerator(FORMATETC[] formats, int current) |
|||
{ |
|||
_formats = formats; |
|||
_current = current; |
|||
} |
|||
|
|||
public FormatEnumerator(IDataObject dataobj) |
|||
{ |
|||
_formats = dataobj.GetDataFormats().Select(ConvertToFormatEtc).ToArray(); |
|||
_current = 0; |
|||
} |
|||
|
|||
private FORMATETC ConvertToFormatEtc(string aFormatName) |
|||
{ |
|||
FORMATETC result = default(FORMATETC); |
|||
result.cfFormat = ClipboardFormats.GetFormat(aFormatName); |
|||
result.dwAspect = DVASPECT.DVASPECT_CONTENT; |
|||
result.ptd = IntPtr.Zero; |
|||
result.lindex = -1; |
|||
result.tymed = TYMED.TYMED_HGLOBAL; |
|||
return result; |
|||
} |
|||
|
|||
public void Clone(out IEnumFORMATETC newEnum) |
|||
{ |
|||
newEnum = new FormatEnumerator(_formats, _current); |
|||
} |
|||
|
|||
public int Next(int celt, FORMATETC[] rgelt, int[] pceltFetched) |
|||
{ |
|||
if (rgelt == null) |
|||
return unchecked((int)UnmanagedMethods.HRESULT.E_INVALIDARG); |
|||
|
|||
int 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); |
|||
} |
|||
|
|||
public int Reset() |
|||
{ |
|||
_current = 0; |
|||
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|||
} |
|||
|
|||
public int Skip(int celt) |
|||
{ |
|||
_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 int GMEM_ZEROINIT = 0x0040; |
|||
private const int GMEM_MOVEABLE = 0x0002; |
|||
|
|||
|
|||
IDataObject _wrapped; |
|||
|
|||
public DataObject(IDataObject wrapped) |
|||
{ |
|||
_wrapped = wrapped; |
|||
} |
|||
|
|||
#region IDataObject
|
|||
bool IDataObject.Contains(string dataFormat) |
|||
{ |
|||
return _wrapped.Contains(dataFormat); |
|||
} |
|||
|
|||
IEnumerable<string> IDataObject.GetDataFormats() |
|||
{ |
|||
return _wrapped.GetDataFormats(); |
|||
} |
|||
|
|||
IEnumerable<string> IDataObject.GetFileNames() |
|||
{ |
|||
return _wrapped.GetFileNames(); |
|||
} |
|||
|
|||
string IDataObject.GetText() |
|||
{ |
|||
return _wrapped.GetText(); |
|||
} |
|||
|
|||
object IDataObject.Get(string dataFormat) |
|||
{ |
|||
return _wrapped.Get(dataFormat); |
|||
} |
|||
#endregion
|
|||
|
|||
#region IOleDataObject
|
|||
|
|||
int IOleDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
return ole.DAdvise(ref pFormatetc, advf, adviseSink, out connection); |
|||
connection = 0; |
|||
return OLE_E_ADVISENOTSUPPORTED; |
|||
} |
|||
|
|||
void IOleDataObject.DUnadvise(int connection) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
ole.DUnadvise(connection); |
|||
Marshal.ThrowExceptionForHR(OLE_E_ADVISENOTSUPPORTED); |
|||
} |
|||
|
|||
int IOleDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
return ole.EnumDAdvise(out enumAdvise); |
|||
|
|||
enumAdvise = null; |
|||
return OLE_E_ADVISENOTSUPPORTED; |
|||
} |
|||
|
|||
IEnumFORMATETC IOleDataObject.EnumFormatEtc(DATADIR direction) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
return ole.EnumFormatEtc(direction); |
|||
if (direction == DATADIR.DATADIR_GET) |
|||
return new FormatEnumerator(_wrapped); |
|||
throw new NotSupportedException(); |
|||
} |
|||
|
|||
int IOleDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
return ole.GetCanonicalFormatEtc(ref formatIn, out formatOut); |
|||
|
|||
formatOut = new FORMATETC(); |
|||
formatOut.ptd = IntPtr.Zero; |
|||
return unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL); |
|||
} |
|||
|
|||
void IOleDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
{ |
|||
ole.GetData(ref format, out medium); |
|||
return; |
|||
} |
|||
if(!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL)) |
|||
Marshal.ThrowExceptionForHR(DV_E_TYMED); |
|||
|
|||
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) |
|||
Marshal.ThrowExceptionForHR(DV_E_DVASPECT); |
|||
|
|||
string fmt = ClipboardFormats.GetFormat(format.cfFormat); |
|||
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt)) |
|||
Marshal.ThrowExceptionForHR(DV_E_FORMATETC); |
|||
|
|||
medium = default(STGMEDIUM); |
|||
medium.tymed = TYMED.TYMED_HGLOBAL; |
|||
int result = WriteDataToHGlobal(fmt, ref medium.unionmember); |
|||
Marshal.ThrowExceptionForHR(result); |
|||
} |
|||
|
|||
void IOleDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
{ |
|||
ole.GetDataHere(ref format, ref medium); |
|||
return; |
|||
} |
|||
|
|||
if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasFlag(TYMED.TYMED_HGLOBAL)) |
|||
Marshal.ThrowExceptionForHR(DV_E_TYMED); |
|||
|
|||
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) |
|||
Marshal.ThrowExceptionForHR(DV_E_DVASPECT); |
|||
|
|||
string fmt = ClipboardFormats.GetFormat(format.cfFormat); |
|||
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt)) |
|||
Marshal.ThrowExceptionForHR(DV_E_FORMATETC); |
|||
|
|||
if (medium.unionmember == IntPtr.Zero) |
|||
Marshal.ThrowExceptionForHR(STG_E_MEDIUMFULL); |
|||
|
|||
int result = WriteDataToHGlobal(fmt, ref medium.unionmember); |
|||
Marshal.ThrowExceptionForHR(result); |
|||
} |
|||
|
|||
int IOleDataObject.QueryGetData(ref FORMATETC format) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
return ole.QueryGetData(ref format); |
|||
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) |
|||
return DV_E_DVASPECT; |
|||
if (!format.tymed.HasFlag(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; |
|||
} |
|||
|
|||
void IOleDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) |
|||
{ |
|||
if (_wrapped is IOleDataObject ole) |
|||
{ |
|||
ole.SetData(ref formatIn, ref medium, release); |
|||
return; |
|||
} |
|||
Marshal.ThrowExceptionForHR(unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL)); |
|||
} |
|||
|
|||
private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal) |
|||
{ |
|||
object data = _wrapped.Get(dataFormat); |
|||
if (dataFormat == DataFormats.Text || data is string) |
|||
return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data)); |
|||
if (dataFormat == DataFormats.FileNames && data is IEnumerable<string> files) |
|||
return WriteFileListToHGlobal(ref hGlobal, files); |
|||
return DV_E_TYMED; |
|||
} |
|||
|
|||
private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files) |
|||
{ |
|||
if (!files?.Any() ?? false) |
|||
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|||
|
|||
char[] filesStr = (string.Join("\0", files) + "\0\0").ToCharArray(); |
|||
_DROPFILES df = new _DROPFILES(); |
|||
df.pFiles = Marshal.SizeOf<_DROPFILES>(); |
|||
df.fWide = true; |
|||
|
|||
int required = (filesStr.Length * sizeof(char)) + Marshal.SizeOf<_DROPFILES>(); |
|||
if (hGlobal == IntPtr.Zero) |
|||
hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, required); |
|||
|
|||
long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64(); |
|||
if (required > available) |
|||
return STG_E_MEDIUMFULL; |
|||
|
|||
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); |
|||
try |
|||
{ |
|||
Marshal.StructureToPtr(df, ptr, false); |
|||
|
|||
Marshal.Copy(filesStr, 0, ptr + Marshal.SizeOf<_DROPFILES>(), filesStr.Length); |
|||
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|||
} |
|||
finally |
|||
{ |
|||
UnmanagedMethods.GlobalUnlock(hGlobal); |
|||
} |
|||
} |
|||
|
|||
private int WriteStringToHGlobal(ref IntPtr hGlobal, string data) |
|||
{ |
|||
int required = (data.Length + 1) * sizeof(char); |
|||
if (hGlobal == IntPtr.Zero) |
|||
hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, required); |
|||
|
|||
long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64(); |
|||
if (required > available) |
|||
return STG_E_MEDIUMFULL; |
|||
|
|||
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal); |
|||
try |
|||
{ |
|||
char[] chars = (data + '\0').ToCharArray(); |
|||
Marshal.Copy(chars, 0, ptr, chars.Length); |
|||
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|||
} |
|||
finally |
|||
{ |
|||
UnmanagedMethods.GlobalUnlock(hGlobal); |
|||
} |
|||
} |
|||
|
|||
#endregion
|
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using Avalonia.Win32.Interop; |
|||
|
|||
namespace Avalonia.Win32 |
|||
{ |
|||
class OleDragSource : IDropSource |
|||
{ |
|||
private const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102; |
|||
private const int DRAGDROP_S_DROP = 0x00040100; |
|||
private const int DRAGDROP_S_CANCEL = 0x00040101; |
|||
|
|||
private const int KEYSTATE_LEFTMB = 1; |
|||
private const int KEYSTATE_MIDDLEMB = 16; |
|||
private const int KEYSTATE_RIGHTMB = 2; |
|||
private static readonly int[] MOUSE_BUTTONS = new int[] { KEYSTATE_LEFTMB, KEYSTATE_MIDDLEMB, KEYSTATE_RIGHTMB }; |
|||
|
|||
public int QueryContinueDrag(int fEscapePressed, int grfKeyState) |
|||
{ |
|||
if (fEscapePressed != 0) |
|||
return DRAGDROP_S_CANCEL; |
|||
|
|||
int pressedMouseButtons = MOUSE_BUTTONS.Where(mb => (grfKeyState & mb) == mb).Count(); |
|||
|
|||
if (pressedMouseButtons >= 2) |
|||
return DRAGDROP_S_CANCEL; |
|||
if (pressedMouseButtons == 0) |
|||
return DRAGDROP_S_DROP; |
|||
|
|||
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|||
} |
|||
|
|||
public int GiveFeedback(int dwEffect) |
|||
{ |
|||
if (dwEffect != 0) |
|||
return DRAGDROP_S_USEDEFAULTCURSORS; |
|||
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue