| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- 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;
- using System.IO;
- using System.Runtime.Serialization.Formatters.Binary;
- namespace Avalonia.Win32
- {
- class DataObject : IDataObject, IOleDataObject
- {
- // Compatibility with WinForms + WPF...
- internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray();
- 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);
- if (data is Stream stream)
- {
- byte[] buffer = new byte[stream.Length - stream.Position];
- stream.Read(buffer, 0, buffer.Length);
- return WriteBytesToHGlobal(ref hGlobal, buffer);
- }
- if (data is IEnumerable<byte> bytes)
- {
- var byteArr = bytes is byte[] ? (byte[])bytes : bytes.ToArray();
- return WriteBytesToHGlobal(ref hGlobal, byteArr);
- }
- return WriteBytesToHGlobal(ref hGlobal, SerializeObject(data));
- }
- private byte[] SerializeObject(object data)
- {
- using (var ms = new MemoryStream())
- {
- ms.Write(SerializedObjectGUID, 0, SerializedObjectGUID.Length);
- BinaryFormatter binaryFormatter = new BinaryFormatter();
- binaryFormatter.Serialize(ms, data);
- return ms.ToArray();
- }
- }
- private int WriteBytesToHGlobal(ref IntPtr hGlobal, byte[] data)
- {
- int required = data.Length;
- 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.Copy(data, 0, ptr, data.Length);
- return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
- }
- finally
- {
- UnmanagedMethods.GlobalUnlock(hGlobal);
- }
- }
- 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
- }
- }
|