Browse Source

Allow Winforms + WPF compatible object drag

boombuler 7 years ago
parent
commit
ed4a85fef9

+ 50 - 1
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<string> 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<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)

+ 25 - 1
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<string> ReadFileNamesFromHGlobal(IntPtr hGlobal)
         {
             List<string> files = new List<string>();