PageRenderTime 51ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Windows/Avalonia.Win32/DataObject.cs

https://gitlab.com/kush/Avalonia
C# | 359 lines | 302 code | 56 blank | 1 comment | 57 complexity | f7428fc14a928be46932061b86874b07 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Runtime.InteropServices;
  6. using System.Runtime.InteropServices.ComTypes;
  7. using System.Runtime.Serialization.Formatters.Binary;
  8. using Avalonia.Input;
  9. using Avalonia.Win32.Interop;
  10. using IDataObject = Avalonia.Input.IDataObject;
  11. namespace Avalonia.Win32
  12. {
  13. class DataObject : IDataObject, IOleDataObject
  14. {
  15. // Compatibility with WinForms + WPF...
  16. internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray();
  17. class FormatEnumerator : IEnumFORMATETC
  18. {
  19. private FORMATETC[] _formats;
  20. private int _current;
  21. private FormatEnumerator(FORMATETC[] formats, int current)
  22. {
  23. _formats = formats;
  24. _current = current;
  25. }
  26. public FormatEnumerator(IDataObject dataobj)
  27. {
  28. _formats = dataobj.GetDataFormats().Select(ConvertToFormatEtc).ToArray();
  29. _current = 0;
  30. }
  31. private FORMATETC ConvertToFormatEtc(string aFormatName)
  32. {
  33. FORMATETC result = default(FORMATETC);
  34. result.cfFormat = ClipboardFormats.GetFormat(aFormatName);
  35. result.dwAspect = DVASPECT.DVASPECT_CONTENT;
  36. result.ptd = IntPtr.Zero;
  37. result.lindex = -1;
  38. result.tymed = TYMED.TYMED_HGLOBAL;
  39. return result;
  40. }
  41. public void Clone(out IEnumFORMATETC newEnum)
  42. {
  43. newEnum = new FormatEnumerator(_formats, _current);
  44. }
  45. public int Next(int celt, FORMATETC[] rgelt, int[] pceltFetched)
  46. {
  47. if (rgelt == null)
  48. return unchecked((int)UnmanagedMethods.HRESULT.E_INVALIDARG);
  49. int i = 0;
  50. while (i < celt && _current < _formats.Length)
  51. {
  52. rgelt[i] = _formats[_current];
  53. _current++;
  54. i++;
  55. }
  56. if (pceltFetched != null)
  57. pceltFetched[0] = i;
  58. if (i != celt)
  59. return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
  60. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  61. }
  62. public int Reset()
  63. {
  64. _current = 0;
  65. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  66. }
  67. public int Skip(int celt)
  68. {
  69. _current += Math.Min(celt, int.MaxValue - _current);
  70. if (_current >= _formats.Length)
  71. return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
  72. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  73. }
  74. }
  75. private const int DV_E_TYMED = unchecked((int)0x80040069);
  76. private const int DV_E_DVASPECT = unchecked((int)0x8004006B);
  77. private const int DV_E_FORMATETC = unchecked((int)0x80040064);
  78. private const int OLE_E_ADVISENOTSUPPORTED = unchecked((int)0x80040003);
  79. private const int STG_E_MEDIUMFULL = unchecked((int)0x80030070);
  80. private const int GMEM_ZEROINIT = 0x0040;
  81. private const int GMEM_MOVEABLE = 0x0002;
  82. IDataObject _wrapped;
  83. public DataObject(IDataObject wrapped)
  84. {
  85. _wrapped = wrapped;
  86. }
  87. #region IDataObject
  88. bool IDataObject.Contains(string dataFormat)
  89. {
  90. return _wrapped.Contains(dataFormat);
  91. }
  92. IEnumerable<string> IDataObject.GetDataFormats()
  93. {
  94. return _wrapped.GetDataFormats();
  95. }
  96. IEnumerable<string> IDataObject.GetFileNames()
  97. {
  98. return _wrapped.GetFileNames();
  99. }
  100. string IDataObject.GetText()
  101. {
  102. return _wrapped.GetText();
  103. }
  104. object IDataObject.Get(string dataFormat)
  105. {
  106. return _wrapped.Get(dataFormat);
  107. }
  108. #endregion
  109. #region IOleDataObject
  110. int IOleDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
  111. {
  112. if (_wrapped is IOleDataObject ole)
  113. return ole.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
  114. connection = 0;
  115. return OLE_E_ADVISENOTSUPPORTED;
  116. }
  117. void IOleDataObject.DUnadvise(int connection)
  118. {
  119. if (_wrapped is IOleDataObject ole)
  120. ole.DUnadvise(connection);
  121. Marshal.ThrowExceptionForHR(OLE_E_ADVISENOTSUPPORTED);
  122. }
  123. int IOleDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise)
  124. {
  125. if (_wrapped is IOleDataObject ole)
  126. return ole.EnumDAdvise(out enumAdvise);
  127. enumAdvise = null;
  128. return OLE_E_ADVISENOTSUPPORTED;
  129. }
  130. IEnumFORMATETC IOleDataObject.EnumFormatEtc(DATADIR direction)
  131. {
  132. if (_wrapped is IOleDataObject ole)
  133. return ole.EnumFormatEtc(direction);
  134. if (direction == DATADIR.DATADIR_GET)
  135. return new FormatEnumerator(_wrapped);
  136. throw new NotSupportedException();
  137. }
  138. int IOleDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
  139. {
  140. if (_wrapped is IOleDataObject ole)
  141. return ole.GetCanonicalFormatEtc(ref formatIn, out formatOut);
  142. formatOut = new FORMATETC();
  143. formatOut.ptd = IntPtr.Zero;
  144. return unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL);
  145. }
  146. void IOleDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
  147. {
  148. if (_wrapped is IOleDataObject ole)
  149. {
  150. ole.GetData(ref format, out medium);
  151. return;
  152. }
  153. if(!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
  154. Marshal.ThrowExceptionForHR(DV_E_TYMED);
  155. if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
  156. Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
  157. string fmt = ClipboardFormats.GetFormat(format.cfFormat);
  158. if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
  159. Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
  160. medium = default(STGMEDIUM);
  161. medium.tymed = TYMED.TYMED_HGLOBAL;
  162. int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
  163. Marshal.ThrowExceptionForHR(result);
  164. }
  165. void IOleDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
  166. {
  167. if (_wrapped is IOleDataObject ole)
  168. {
  169. ole.GetDataHere(ref format, ref medium);
  170. return;
  171. }
  172. if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
  173. Marshal.ThrowExceptionForHR(DV_E_TYMED);
  174. if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
  175. Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
  176. string fmt = ClipboardFormats.GetFormat(format.cfFormat);
  177. if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
  178. Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
  179. if (medium.unionmember == IntPtr.Zero)
  180. Marshal.ThrowExceptionForHR(STG_E_MEDIUMFULL);
  181. int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
  182. Marshal.ThrowExceptionForHR(result);
  183. }
  184. int IOleDataObject.QueryGetData(ref FORMATETC format)
  185. {
  186. if (_wrapped is IOleDataObject ole)
  187. return ole.QueryGetData(ref format);
  188. if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
  189. return DV_E_DVASPECT;
  190. if (!format.tymed.HasFlag(TYMED.TYMED_HGLOBAL))
  191. return DV_E_TYMED;
  192. string dataFormat = ClipboardFormats.GetFormat(format.cfFormat);
  193. if (!string.IsNullOrEmpty(dataFormat) && _wrapped.Contains(dataFormat))
  194. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  195. return DV_E_FORMATETC;
  196. }
  197. void IOleDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
  198. {
  199. if (_wrapped is IOleDataObject ole)
  200. {
  201. ole.SetData(ref formatIn, ref medium, release);
  202. return;
  203. }
  204. Marshal.ThrowExceptionForHR(unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
  205. }
  206. private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal)
  207. {
  208. object data = _wrapped.Get(dataFormat);
  209. if (dataFormat == DataFormats.Text || data is string)
  210. return WriteStringToHGlobal(ref hGlobal, Convert.ToString(data));
  211. if (dataFormat == DataFormats.FileNames && data is IEnumerable<string> files)
  212. return WriteFileListToHGlobal(ref hGlobal, files);
  213. if (data is Stream stream)
  214. {
  215. byte[] buffer = new byte[stream.Length - stream.Position];
  216. stream.Read(buffer, 0, buffer.Length);
  217. return WriteBytesToHGlobal(ref hGlobal, buffer);
  218. }
  219. if (data is IEnumerable<byte> bytes)
  220. {
  221. var byteArr = bytes is byte[] ? (byte[])bytes : bytes.ToArray();
  222. return WriteBytesToHGlobal(ref hGlobal, byteArr);
  223. }
  224. return WriteBytesToHGlobal(ref hGlobal, SerializeObject(data));
  225. }
  226. private byte[] SerializeObject(object data)
  227. {
  228. using (var ms = new MemoryStream())
  229. {
  230. ms.Write(SerializedObjectGUID, 0, SerializedObjectGUID.Length);
  231. BinaryFormatter binaryFormatter = new BinaryFormatter();
  232. binaryFormatter.Serialize(ms, data);
  233. return ms.ToArray();
  234. }
  235. }
  236. private int WriteBytesToHGlobal(ref IntPtr hGlobal, byte[] data)
  237. {
  238. int required = data.Length;
  239. if (hGlobal == IntPtr.Zero)
  240. hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, required);
  241. long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
  242. if (required > available)
  243. return STG_E_MEDIUMFULL;
  244. IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
  245. try
  246. {
  247. Marshal.Copy(data, 0, ptr, data.Length);
  248. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  249. }
  250. finally
  251. {
  252. UnmanagedMethods.GlobalUnlock(hGlobal);
  253. }
  254. }
  255. private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files)
  256. {
  257. if (!files?.Any() ?? false)
  258. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  259. char[] filesStr = (string.Join("\0", files) + "\0\0").ToCharArray();
  260. _DROPFILES df = new _DROPFILES();
  261. df.pFiles = Marshal.SizeOf<_DROPFILES>();
  262. df.fWide = true;
  263. int required = (filesStr.Length * sizeof(char)) + Marshal.SizeOf<_DROPFILES>();
  264. if (hGlobal == IntPtr.Zero)
  265. hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, required);
  266. long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
  267. if (required > available)
  268. return STG_E_MEDIUMFULL;
  269. IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
  270. try
  271. {
  272. Marshal.StructureToPtr(df, ptr, false);
  273. Marshal.Copy(filesStr, 0, ptr + Marshal.SizeOf<_DROPFILES>(), filesStr.Length);
  274. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  275. }
  276. finally
  277. {
  278. UnmanagedMethods.GlobalUnlock(hGlobal);
  279. }
  280. }
  281. private int WriteStringToHGlobal(ref IntPtr hGlobal, string data)
  282. {
  283. int required = (data.Length + 1) * sizeof(char);
  284. if (hGlobal == IntPtr.Zero)
  285. hGlobal = UnmanagedMethods.GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, required);
  286. long available = UnmanagedMethods.GlobalSize(hGlobal).ToInt64();
  287. if (required > available)
  288. return STG_E_MEDIUMFULL;
  289. IntPtr ptr = UnmanagedMethods.GlobalLock(hGlobal);
  290. try
  291. {
  292. char[] chars = (data + '\0').ToCharArray();
  293. Marshal.Copy(chars, 0, ptr, chars.Length);
  294. return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
  295. }
  296. finally
  297. {
  298. UnmanagedMethods.GlobalUnlock(hGlobal);
  299. }
  300. }
  301. #endregion
  302. }
  303. }