diff --git a/src/Windows/Avalonia.Win32/DataObject.cs b/src/Windows/Avalonia.Win32/DataObject.cs index 6248afd7bb..aa3ee91ef9 100644 --- a/src/Windows/Avalonia.Win32/DataObject.cs +++ b/src/Windows/Avalonia.Win32/DataObject.cs @@ -8,11 +8,16 @@ 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; @@ -249,7 +254,51 @@ namespace Avalonia.Win32 return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data)); if (dataFormat == DataFormats.FileNames && data is IEnumerable files) return WriteFileListToHGlobal(ref hGlobal, files); - return DV_E_TYMED; + 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 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 files) diff --git a/src/Windows/Avalonia.Win32/OleDataObject.cs b/src/Windows/Avalonia.Win32/OleDataObject.cs index fe6faa162c..60527bb7d6 100644 --- a/src/Windows/Avalonia.Win32/OleDataObject.cs +++ b/src/Windows/Avalonia.Win32/OleDataObject.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; +using System.Runtime.Serialization.Formatters.Binary; using System.Text; using Avalonia.Controls.DragDrop; using Avalonia.Win32.Interop; @@ -62,7 +64,19 @@ namespace Avalonia.Win32 return ReadStringFromHGlobal(medium.unionmember); if (format == DataFormats.FileNames) return ReadFileNamesFromHGlobal(medium.unionmember); - return ReadBytesFromHGlobal(medium.unionmember); + + byte[] data = ReadBytesFromHGlobal(medium.unionmember); + + if (IsSerializedObject(data)) + { + using (var ms = new MemoryStream(data)) + { + ms.Position = DataObject.SerializedObjectGUID.Length; + BinaryFormatter binaryFormatter = new BinaryFormatter(); + return binaryFormatter.Deserialize(ms); + } + } + return data; } } finally @@ -73,6 +87,16 @@ namespace Avalonia.Win32 return null; } + private bool IsSerializedObject(byte[] data) + { + if (data.Length < DataObject.SerializedObjectGUID.Length) + return false; + for (int i = 0; i < DataObject.SerializedObjectGUID.Length; i++) + if (data[i] != DataObject.SerializedObjectGUID[i]) + return false; + return true; + } + private static IEnumerable ReadFileNamesFromHGlobal(IntPtr hGlobal) { List files = new List();