A cross-platform UI framework for .NET
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

170 lines
5.7 KiB

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.Input;
using Avalonia.Win32.Interop;
namespace Avalonia.Win32
{
class OleDataObject : Avalonia.Input.IDataObject
{
private IOleDataObject _wrapped;
public OleDataObject(IOleDataObject wrapped)
{
_wrapped = wrapped;
}
public bool Contains(string dataFormat)
{
return GetDataFormatsCore().Any(df => StringComparer.OrdinalIgnoreCase.Equals(df, dataFormat));
}
public IEnumerable<string> GetDataFormats()
{
return GetDataFormatsCore().Distinct();
}
public string GetText()
{
return GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT) as string;
}
public IEnumerable<string> GetFileNames()
{
return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable<string>;
}
public object Get(string dataFormat)
{
return GetDataFromOleHGLOBAL(dataFormat, DVASPECT.DVASPECT_CONTENT);
}
private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect)
{
FORMATETC formatEtc = new FORMATETC();
formatEtc.cfFormat = ClipboardFormats.GetFormat(format);
formatEtc.dwAspect = aspect;
formatEtc.lindex = -1;
formatEtc.tymed = TYMED.TYMED_HGLOBAL;
if (_wrapped.QueryGetData(ref formatEtc) == 0)
{
_wrapped.GetData(ref formatEtc, out STGMEDIUM medium);
try
{
if (medium.unionmember != IntPtr.Zero && medium.tymed == TYMED.TYMED_HGLOBAL)
{
if (format == DataFormats.Text)
return ReadStringFromHGlobal(medium.unionmember);
if (format == DataFormats.FileNames)
return ReadFileNamesFromHGlobal(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
{
UnmanagedMethods.ReleaseStgMedium(ref medium);
}
}
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>();
int fileCount = UnmanagedMethods.DragQueryFile(hGlobal, -1, null, 0);
if (fileCount > 0)
{
for (int i = 0; i < fileCount; i++)
{
int pathLen = UnmanagedMethods.DragQueryFile(hGlobal, i, null, 0);
StringBuilder sb = new StringBuilder(pathLen+1);
if (UnmanagedMethods.DragQueryFile(hGlobal, i, sb, sb.Capacity) == pathLen)
{
files.Add(sb.ToString());
}
}
}
return files;
}
private static string ReadStringFromHGlobal(IntPtr hGlobal)
{
IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
try
{
return Marshal.PtrToStringAuto(ptr);
}
finally
{
UnmanagedMethods.GlobalUnlock(hGlobal);
}
}
private static byte[] ReadBytesFromHGlobal(IntPtr hGlobal)
{
IntPtr source = UnmanagedMethods.GlobalLock(hGlobal);
try
{
int size = (int)UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
byte[] data = new byte[size];
Marshal.Copy(source, data, 0, size);
return data;
}
finally
{
UnmanagedMethods.GlobalUnlock(hGlobal);
}
}
private IEnumerable<string> GetDataFormatsCore()
{
var enumFormat = _wrapped.EnumFormatEtc(DATADIR.DATADIR_GET);
if (enumFormat != null)
{
enumFormat.Reset();
FORMATETC[] formats = new FORMATETC[1];
int[] fetched = { 1 };
while (fetched[0] > 0)
{
fetched[0] = 0;
if (enumFormat.Next(1, formats, fetched) == 0 && fetched[0] > 0)
{
if (formats[0].ptd != IntPtr.Zero)
Marshal.FreeCoTaskMem(formats[0].ptd);
yield return ClipboardFormats.GetFormat(formats[0].cfFormat);
}
}
}
}
}
}