|
|
|
@ -8,22 +8,50 @@ using System.Runtime.InteropServices; |
|
|
|
using System.Runtime.InteropServices.ComTypes; |
|
|
|
using System.Runtime.Serialization.Formatters.Binary; |
|
|
|
using Avalonia.Input; |
|
|
|
using Avalonia.MicroCom; |
|
|
|
using Avalonia.Win32.Interop; |
|
|
|
|
|
|
|
using FORMATETC = Avalonia.Win32.Interop.FORMATETC; |
|
|
|
using IDataObject = Avalonia.Input.IDataObject; |
|
|
|
|
|
|
|
namespace Avalonia.Win32 |
|
|
|
{ |
|
|
|
class DataObject : IDataObject, IOleDataObject |
|
|
|
internal static class DataObjectEx |
|
|
|
{ |
|
|
|
public static unsafe IDataObject GetAvaloniaObject(this Win32Com.IDataObject pDataObj) |
|
|
|
{ |
|
|
|
switch (pDataObj) |
|
|
|
{ |
|
|
|
case null: |
|
|
|
throw new ArgumentNullException(nameof(pDataObj)); |
|
|
|
case DataObject avnObj: |
|
|
|
return avnObj; |
|
|
|
case OleDataObject oleAvnObj: |
|
|
|
return oleAvnObj; |
|
|
|
default: |
|
|
|
var guid = MicroComRuntime.GetGuidFor(typeof(Win32Com.IAvnDataObject)); |
|
|
|
var rv = ((MicroComProxyBase)pDataObj).QueryInterface(guid, out var ppv); |
|
|
|
return rv == 0 |
|
|
|
? MicroComRuntime.GetObjectFromCcw(ppv) as DataObject |
|
|
|
: new OleDataObject(pDataObj); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
internal class DataObject : IDataObject, Win32Com.IAvnDataObject, IMicroComShadowContainer |
|
|
|
{ |
|
|
|
// Compatibility with WinForms + WPF...
|
|
|
|
internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray(); |
|
|
|
private static Dictionary<IntPtr, IDataObject> _avnDataObject = new Dictionary<IntPtr, IDataObject>(); |
|
|
|
|
|
|
|
class FormatEnumerator : IEnumFORMATETC |
|
|
|
class FormatEnumerator : Win32Com.IEnumFORMATETC, IMicroComShadowContainer |
|
|
|
{ |
|
|
|
private FORMATETC[] _formats; |
|
|
|
private int _current; |
|
|
|
private uint _current; |
|
|
|
|
|
|
|
private FormatEnumerator(FORMATETC[] formats, int current) |
|
|
|
public MicroComShadow Shadow { get; set; } |
|
|
|
|
|
|
|
private FormatEnumerator(FORMATETC[] formats, uint current) |
|
|
|
{ |
|
|
|
_formats = formats; |
|
|
|
_current = current; |
|
|
|
@ -38,7 +66,7 @@ namespace Avalonia.Win32 |
|
|
|
private FORMATETC ConvertToFormatEtc(string aFormatName) |
|
|
|
{ |
|
|
|
FORMATETC result = default(FORMATETC); |
|
|
|
result.cfFormat = ClipboardFormats.GetFormat(aFormatName); |
|
|
|
result.cfFormat = (ushort)ClipboardFormats.GetFormat(aFormatName); |
|
|
|
result.dwAspect = DVASPECT.DVASPECT_CONTENT; |
|
|
|
result.ptd = IntPtr.Zero; |
|
|
|
result.lindex = -1; |
|
|
|
@ -46,59 +74,80 @@ namespace Avalonia.Win32 |
|
|
|
return result; |
|
|
|
} |
|
|
|
|
|
|
|
public void Clone(out IEnumFORMATETC newEnum) |
|
|
|
{ |
|
|
|
newEnum = new FormatEnumerator(_formats, _current); |
|
|
|
} |
|
|
|
|
|
|
|
public int Next(int celt, FORMATETC[] rgelt, int[] pceltFetched) |
|
|
|
public unsafe uint Next(uint celt, FORMATETC* rgelt, uint* results) |
|
|
|
{ |
|
|
|
if (rgelt == null) |
|
|
|
return unchecked((int)UnmanagedMethods.HRESULT.E_INVALIDARG); |
|
|
|
return (uint)UnmanagedMethods.HRESULT.E_INVALIDARG; |
|
|
|
|
|
|
|
int i = 0; |
|
|
|
uint 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); |
|
|
|
return (uint)UnmanagedMethods.HRESULT.S_FALSE; |
|
|
|
|
|
|
|
// "results" parameter can be NULL if celt is 1.
|
|
|
|
if (celt != 1 || results != default) |
|
|
|
*results = i; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
public int Reset() |
|
|
|
public uint Skip(uint celt) |
|
|
|
{ |
|
|
|
_current += Math.Min(celt, int.MaxValue - _current); |
|
|
|
if (_current >= _formats.Length) |
|
|
|
return (uint)UnmanagedMethods.HRESULT.S_FALSE; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
public void Reset() |
|
|
|
{ |
|
|
|
_current = 0; |
|
|
|
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|
|
|
} |
|
|
|
|
|
|
|
public int Skip(int celt) |
|
|
|
public Win32Com.IEnumFORMATETC Clone() |
|
|
|
{ |
|
|
|
return new FormatEnumerator(_formats, _current); |
|
|
|
} |
|
|
|
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
public void OnReferencedFromNative() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
public void OnUnreferencedFromNative() |
|
|
|
{ |
|
|
|
_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 uint DV_E_TYMED = 0x80040069; |
|
|
|
private const uint DV_E_DVASPECT = 0x8004006B; |
|
|
|
private const uint DV_E_FORMATETC = 0x80040064; |
|
|
|
private const uint OLE_E_ADVISENOTSUPPORTED = 0x80040003; |
|
|
|
private const uint STG_E_MEDIUMFULL = 0x80030070; |
|
|
|
|
|
|
|
private const int GMEM_ZEROINIT = 0x0040; |
|
|
|
private const int GMEM_MOVEABLE = 0x0002; |
|
|
|
|
|
|
|
|
|
|
|
IDataObject _wrapped; |
|
|
|
|
|
|
|
|
|
|
|
public MicroComShadow Shadow { get; set; } |
|
|
|
|
|
|
|
public DataObject(IDataObject wrapped) |
|
|
|
{ |
|
|
|
if (_wrapped is DataObject) |
|
|
|
{ |
|
|
|
throw new InvalidOperationException(); |
|
|
|
} |
|
|
|
|
|
|
|
_wrapped = wrapped; |
|
|
|
} |
|
|
|
|
|
|
|
@ -131,123 +180,123 @@ namespace Avalonia.Win32 |
|
|
|
|
|
|
|
#region IOleDataObject
|
|
|
|
|
|
|
|
int IOleDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) |
|
|
|
unsafe int Win32Com.IDataObject.DAdvise(FORMATETC* pFormatetc, int advf, void* adviseSink) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
return ole.DAdvise(ref pFormatetc, advf, adviseSink, out connection); |
|
|
|
connection = 0; |
|
|
|
return OLE_E_ADVISENOTSUPPORTED; |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
return ole.DAdvise(pFormatetc, advf, adviseSink); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void IOleDataObject.DUnadvise(int connection) |
|
|
|
void Win32Com.IDataObject.DUnadvise(int connection) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
ole.DUnadvise(connection); |
|
|
|
Marshal.ThrowExceptionForHR(OLE_E_ADVISENOTSUPPORTED); |
|
|
|
throw new COMException(nameof(OLE_E_ADVISENOTSUPPORTED), unchecked((int)OLE_E_ADVISENOTSUPPORTED)); |
|
|
|
} |
|
|
|
|
|
|
|
int IOleDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise) |
|
|
|
unsafe void* Win32Com.IDataObject.EnumDAdvise() |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
return ole.EnumDAdvise(out enumAdvise); |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
return ole.EnumDAdvise(); |
|
|
|
|
|
|
|
enumAdvise = null; |
|
|
|
return OLE_E_ADVISENOTSUPPORTED; |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
IEnumFORMATETC IOleDataObject.EnumFormatEtc(DATADIR direction) |
|
|
|
Win32Com.IEnumFORMATETC Win32Com.IDataObject.EnumFormatEtc(int direction) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
return ole.EnumFormatEtc(direction); |
|
|
|
if (direction == DATADIR.DATADIR_GET) |
|
|
|
if ((DATADIR)direction == DATADIR.DATADIR_GET) |
|
|
|
return new FormatEnumerator(_wrapped); |
|
|
|
throw new NotSupportedException(); |
|
|
|
throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL)); |
|
|
|
} |
|
|
|
|
|
|
|
int IOleDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) |
|
|
|
unsafe FORMATETC Win32Com.IDataObject.GetCanonicalFormatEtc(FORMATETC* formatIn) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
return ole.GetCanonicalFormatEtc(ref formatIn, out formatOut); |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
return ole.GetCanonicalFormatEtc(formatIn); |
|
|
|
|
|
|
|
formatOut = new FORMATETC(); |
|
|
|
var formatOut = new FORMATETC(); |
|
|
|
formatOut.ptd = IntPtr.Zero; |
|
|
|
return unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL); |
|
|
|
|
|
|
|
throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL)); |
|
|
|
} |
|
|
|
|
|
|
|
void IOleDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium) |
|
|
|
unsafe uint Win32Com.IDataObject.GetData(FORMATETC* format, Interop.STGMEDIUM* medium) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
{ |
|
|
|
ole.GetData(ref format, out medium); |
|
|
|
return; |
|
|
|
return ole.GetData(format, medium); |
|
|
|
} |
|
|
|
if(!format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL)) |
|
|
|
Marshal.ThrowExceptionForHR(DV_E_TYMED); |
|
|
|
|
|
|
|
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) |
|
|
|
Marshal.ThrowExceptionForHR(DV_E_DVASPECT); |
|
|
|
if (!format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL)) |
|
|
|
return DV_E_TYMED; |
|
|
|
|
|
|
|
if (format->dwAspect != DVASPECT.DVASPECT_CONTENT) |
|
|
|
return DV_E_DVASPECT; |
|
|
|
|
|
|
|
string fmt = ClipboardFormats.GetFormat(format.cfFormat); |
|
|
|
string fmt = ClipboardFormats.GetFormat(format->cfFormat); |
|
|
|
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt)) |
|
|
|
Marshal.ThrowExceptionForHR(DV_E_FORMATETC); |
|
|
|
return DV_E_FORMATETC; |
|
|
|
|
|
|
|
medium = default(STGMEDIUM); |
|
|
|
medium.tymed = TYMED.TYMED_HGLOBAL; |
|
|
|
int result = WriteDataToHGlobal(fmt, ref medium.unionmember); |
|
|
|
Marshal.ThrowExceptionForHR(result); |
|
|
|
* medium = default(Interop.STGMEDIUM); |
|
|
|
medium->tymed = TYMED.TYMED_HGLOBAL; |
|
|
|
return WriteDataToHGlobal(fmt, ref medium->unionmember); |
|
|
|
} |
|
|
|
|
|
|
|
void IOleDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) |
|
|
|
unsafe uint Win32Com.IDataObject.GetDataHere(FORMATETC* format, Interop.STGMEDIUM* medium) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
{ |
|
|
|
ole.GetDataHere(ref format, ref medium); |
|
|
|
return; |
|
|
|
return ole.GetDataHere(format, medium); |
|
|
|
} |
|
|
|
|
|
|
|
if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL)) |
|
|
|
Marshal.ThrowExceptionForHR(DV_E_TYMED); |
|
|
|
if (medium->tymed != TYMED.TYMED_HGLOBAL || !format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL)) |
|
|
|
return DV_E_TYMED; |
|
|
|
|
|
|
|
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) |
|
|
|
Marshal.ThrowExceptionForHR(DV_E_DVASPECT); |
|
|
|
if (format->dwAspect != DVASPECT.DVASPECT_CONTENT) |
|
|
|
return DV_E_DVASPECT; |
|
|
|
|
|
|
|
string fmt = ClipboardFormats.GetFormat(format.cfFormat); |
|
|
|
string fmt = ClipboardFormats.GetFormat(format->cfFormat); |
|
|
|
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt)) |
|
|
|
Marshal.ThrowExceptionForHR(DV_E_FORMATETC); |
|
|
|
return DV_E_FORMATETC; |
|
|
|
|
|
|
|
if (medium.unionmember == IntPtr.Zero) |
|
|
|
Marshal.ThrowExceptionForHR(STG_E_MEDIUMFULL); |
|
|
|
if (medium->unionmember == IntPtr.Zero) |
|
|
|
return STG_E_MEDIUMFULL; |
|
|
|
|
|
|
|
int result = WriteDataToHGlobal(fmt, ref medium.unionmember); |
|
|
|
Marshal.ThrowExceptionForHR(result); |
|
|
|
return WriteDataToHGlobal(fmt, ref medium->unionmember); |
|
|
|
} |
|
|
|
|
|
|
|
int IOleDataObject.QueryGetData(ref FORMATETC format) |
|
|
|
unsafe uint Win32Com.IDataObject.QueryGetData(FORMATETC* format) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
return ole.QueryGetData(ref format); |
|
|
|
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT) |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
{ |
|
|
|
return ole.QueryGetData(format); |
|
|
|
} |
|
|
|
|
|
|
|
if (format->dwAspect != DVASPECT.DVASPECT_CONTENT) |
|
|
|
return DV_E_DVASPECT; |
|
|
|
if (!format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL)) |
|
|
|
if (!format->tymed.HasAllFlags(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; |
|
|
|
var dataFormat = ClipboardFormats.GetFormat(format->cfFormat); |
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(dataFormat) || !_wrapped.Contains(dataFormat)) |
|
|
|
return DV_E_FORMATETC; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
void IOleDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) |
|
|
|
unsafe uint Win32Com.IDataObject.SetData(FORMATETC* pformatetc, Interop.STGMEDIUM* pmedium, int fRelease) |
|
|
|
{ |
|
|
|
if (_wrapped is IOleDataObject ole) |
|
|
|
if (_wrapped is Win32Com.IDataObject ole) |
|
|
|
{ |
|
|
|
ole.SetData(ref formatIn, ref medium, release); |
|
|
|
return; |
|
|
|
return ole.SetData(pformatetc, pmedium, fRelease); |
|
|
|
} |
|
|
|
Marshal.ThrowExceptionForHR(unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL)); |
|
|
|
return (uint)UnmanagedMethods.HRESULT.E_NOTIMPL; |
|
|
|
} |
|
|
|
|
|
|
|
private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal) |
|
|
|
private uint WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal) |
|
|
|
{ |
|
|
|
object data = _wrapped.Get(dataFormat); |
|
|
|
if (dataFormat == DataFormats.Text || data is string) |
|
|
|
@ -288,7 +337,7 @@ namespace Avalonia.Win32 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private unsafe int WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan<byte> data) |
|
|
|
private unsafe uint WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan<byte> data) |
|
|
|
{ |
|
|
|
int required = data.Length; |
|
|
|
if (hGlobal == IntPtr.Zero) |
|
|
|
@ -312,7 +361,7 @@ namespace Avalonia.Win32 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files) |
|
|
|
private uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files) |
|
|
|
{ |
|
|
|
if (!files?.Any() ?? false) |
|
|
|
return unchecked((int)UnmanagedMethods.HRESULT.S_OK); |
|
|
|
@ -344,7 +393,7 @@ namespace Avalonia.Win32 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private int WriteStringToHGlobal(ref IntPtr hGlobal, string data) |
|
|
|
private uint WriteStringToHGlobal(ref IntPtr hGlobal, string data) |
|
|
|
{ |
|
|
|
int required = (data.Length + 1) * sizeof(char); |
|
|
|
if (hGlobal == IntPtr.Zero) |
|
|
|
@ -367,6 +416,18 @@ namespace Avalonia.Win32 |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void Dispose() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
public void OnReferencedFromNative() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
public void OnUnreferencedFromNative() |
|
|
|
{ |
|
|
|
} |
|
|
|
|
|
|
|
#endregion
|
|
|
|
} |
|
|
|
} |
|
|
|
|