DocumentUtils.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. using Syncfusion.Pdf;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Reflection;
  7. using System.Runtime.InteropServices;
  8. using System.Runtime.InteropServices.ComTypes;
  9. using System.Text;
  10. using System.Threading.Tasks;
  11. using System.Windows;
  12. namespace InABox.Wpf;
  13. public static class DocumentUtils
  14. {
  15. public static IEnumerable<Tuple<string, Stream?>>? HandleFileDrop(DragEventArgs e)
  16. {
  17. return HandleFileDrop(e.Data);
  18. }
  19. public static IEnumerable<Tuple<string, Stream?>>? HandleFileDrop(System.Windows.IDataObject obj)
  20. {
  21. var dataObject = new OutlookDataObject(obj);
  22. string? desc = null;
  23. if (dataObject.GetDataPresent("FileGroupDescriptor")) desc = "FileGroupDescriptor";
  24. else if (dataObject.GetDataPresent("FileGroupDescriptorW")) desc = "FileGroupDescriptorW";
  25. if (desc is not null)
  26. {
  27. var result = new List<Tuple<string, Stream?>>();
  28. var filenames = (string[])dataObject.GetData(desc);
  29. var filestreams = (MemoryStream[])dataObject.GetData("FileContents");
  30. for (var i = 0; i < filenames.Length; i++)
  31. {
  32. var filename = filenames[i];
  33. var filestream = filestreams[i];
  34. result.Add(new Tuple<string, Stream?>(filename, filestream));
  35. }
  36. return result;
  37. }
  38. else if (dataObject.GetDataPresent(DataFormats.FileDrop))
  39. {
  40. var result = new List<Tuple<string, Stream?>>();
  41. foreach (var filename in (string[])dataObject.GetData(DataFormats.FileDrop))
  42. {
  43. if (File.Exists(filename))
  44. {
  45. result.Add(new Tuple<string, Stream?>(filename, null));
  46. }
  47. }
  48. return result;
  49. }
  50. return null;
  51. }
  52. // https://gist.github.com/MattyBoy4444/521547
  53. public class OutlookDataObject : System.Windows.IDataObject
  54. {
  55. #region NativeMethods
  56. private class NativeMethods
  57. {
  58. [DllImport("kernel32.dll")]
  59. static extern IntPtr GlobalLock(IntPtr hMem);
  60. [DllImport("ole32.dll", PreserveSig = false)]
  61. public static extern ILockBytes CreateILockBytesOnHGlobal(IntPtr hGlobal, bool fDeleteOnRelease);
  62. [DllImport("OLE32.DLL", CharSet = CharSet.Auto, PreserveSig = false)]
  63. public static extern IntPtr GetHGlobalFromILockBytes(ILockBytes pLockBytes);
  64. [DllImport("OLE32.DLL", CharSet = CharSet.Unicode, PreserveSig = false)]
  65. public static extern IStorage StgCreateDocfileOnILockBytes(ILockBytes plkbyt, uint grfMode, uint reserved);
  66. [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("0000000B-0000-0000-C000-000000000046")]
  67. public interface IStorage
  68. {
  69. [return: MarshalAs(UnmanagedType.Interface)]
  70. IStream CreateStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
  71. [return: MarshalAs(UnmanagedType.Interface)]
  72. IStream OpenStream([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr reserved1, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
  73. [return: MarshalAs(UnmanagedType.Interface)]
  74. IStorage CreateStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.U4)] int grfMode, [In, MarshalAs(UnmanagedType.U4)] int reserved1, [In, MarshalAs(UnmanagedType.U4)] int reserved2);
  75. [return: MarshalAs(UnmanagedType.Interface)]
  76. IStorage OpenStorage([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, IntPtr pstgPriority, [In, MarshalAs(UnmanagedType.U4)] int grfMode, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.U4)] int reserved);
  77. void CopyTo(int ciidExclude, [In, MarshalAs(UnmanagedType.LPArray)] Guid[] pIIDExclude, IntPtr snbExclude, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest);
  78. void MoveElementTo([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In, MarshalAs(UnmanagedType.Interface)] IStorage stgDest, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName, [In, MarshalAs(UnmanagedType.U4)] int grfFlags);
  79. void Commit(int grfCommitFlags);
  80. void Revert();
  81. void EnumElements([In, MarshalAs(UnmanagedType.U4)] int reserved1, IntPtr reserved2, [In, MarshalAs(UnmanagedType.U4)] int reserved3, [MarshalAs(UnmanagedType.Interface)] out object ppVal);
  82. void DestroyElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsName);
  83. void RenameElement([In, MarshalAs(UnmanagedType.BStr)] string pwcsOldName, [In, MarshalAs(UnmanagedType.BStr)] string pwcsNewName);
  84. void SetElementTimes([In, MarshalAs(UnmanagedType.BStr)] string pwcsName, [In] System.Runtime.InteropServices.ComTypes.FILETIME pctime, [In] System.Runtime.InteropServices.ComTypes.FILETIME patime, [In] System.Runtime.InteropServices.ComTypes.FILETIME pmtime);
  85. void SetClass([In] ref Guid clsid);
  86. void SetStateBits(int grfStateBits, int grfMask);
  87. void Stat([Out] out System.Runtime.InteropServices.ComTypes.STATSTG pStatStg, int grfStatFlag);
  88. }
  89. [ComImport, Guid("0000000A-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  90. public interface ILockBytes
  91. {
  92. void ReadAt([In, MarshalAs(UnmanagedType.U8)] long ulOffset, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] byte[] pv, [In, MarshalAs(UnmanagedType.U4)] int cb, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pcbRead);
  93. void WriteAt([In, MarshalAs(UnmanagedType.U8)] long ulOffset, IntPtr pv, [In, MarshalAs(UnmanagedType.U4)] int cb, [Out, MarshalAs(UnmanagedType.LPArray)] int[] pcbWritten);
  94. void Flush();
  95. void SetSize([In, MarshalAs(UnmanagedType.U8)] long cb);
  96. void LockRegion([In, MarshalAs(UnmanagedType.U8)] long libOffset, [In, MarshalAs(UnmanagedType.U8)] long cb, [In, MarshalAs(UnmanagedType.U4)] int dwLockType);
  97. void UnlockRegion([In, MarshalAs(UnmanagedType.U8)] long libOffset, [In, MarshalAs(UnmanagedType.U8)] long cb, [In, MarshalAs(UnmanagedType.U4)] int dwLockType);
  98. void Stat([Out] out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, [In, MarshalAs(UnmanagedType.U4)] int grfStatFlag);
  99. }
  100. [StructLayout(LayoutKind.Sequential)]
  101. public sealed class POINTL
  102. {
  103. public int x;
  104. public int y;
  105. }
  106. [StructLayout(LayoutKind.Sequential)]
  107. public sealed class SIZEL
  108. {
  109. public int cx;
  110. public int cy;
  111. }
  112. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
  113. public sealed class FILEGROUPDESCRIPTORA
  114. {
  115. public uint cItems;
  116. public FILEDESCRIPTORA fgd;
  117. }
  118. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
  119. public sealed class FILEDESCRIPTORA
  120. {
  121. public uint dwFlags;
  122. public Guid clsid;
  123. public SIZEL sizel;
  124. public POINTL pointl;
  125. public uint dwFileAttributes;
  126. public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
  127. public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
  128. public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
  129. public uint nFileSizeHigh;
  130. public uint nFileSizeLow;
  131. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
  132. public string cFileName;
  133. }
  134. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  135. public sealed class FILEGROUPDESCRIPTORW
  136. {
  137. public uint cItems;
  138. public FILEDESCRIPTORW fgd;
  139. }
  140. [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
  141. public sealed class FILEDESCRIPTORW
  142. {
  143. public uint dwFlags;
  144. public Guid clsid;
  145. public SIZEL sizel;
  146. public POINTL pointl;
  147. public uint dwFileAttributes;
  148. public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
  149. public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
  150. public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
  151. public uint nFileSizeHigh;
  152. public uint nFileSizeLow;
  153. [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
  154. public string cFileName;
  155. }
  156. }
  157. #endregion
  158. #region Property(s)
  159. /// <summary>
  160. /// Holds the <see cref="System.Windows.IDataObject"/> that this class is wrapping
  161. /// </summary>
  162. private System.Windows.IDataObject underlyingDataObject;
  163. /// <summary>
  164. /// Holds the <see cref="System.Runtime.InteropServices.ComTypes.IDataObject"/> interface to the <see cref="System.Windows.IDataObject"/> that this class is wrapping.
  165. /// </summary>
  166. private System.Runtime.InteropServices.ComTypes.IDataObject comUnderlyingDataObject;
  167. /// <summary>
  168. /// Holds the internal ole <see cref="System.Windows.IDataObject"/> to the <see cref="System.Windows.IDataObject"/> that this class is wrapping.
  169. /// </summary>
  170. private System.Windows.IDataObject oleUnderlyingDataObject;
  171. /// <summary>
  172. /// Holds the <see cref="MethodInfo"/> of the "GetDataFromHGLOBAL" method of the internal ole <see cref="System.Windows.IDataObject"/>.
  173. /// </summary>
  174. private MethodInfo getDataFromHGLOBALMethod;
  175. #endregion
  176. #region Constructor(s)
  177. /// <summary>
  178. /// Initializes a new instance of the <see cref="OutlookDataObject"/> class.
  179. /// </summary>
  180. /// <param name="underlyingDataObject">The underlying data object to wrap.</param>
  181. public OutlookDataObject(System.Windows.IDataObject underlyingDataObject)
  182. {
  183. //get the underlying dataobject and its ComType IDataObject interface to it
  184. this.underlyingDataObject = underlyingDataObject;
  185. this.comUnderlyingDataObject = (System.Runtime.InteropServices.ComTypes.IDataObject)this.underlyingDataObject;
  186. //get the internal ole dataobject and its GetDataFromHGLOBAL so it can be called later
  187. FieldInfo innerDataField = this.underlyingDataObject.GetType().GetField("_innerData", BindingFlags.NonPublic | BindingFlags.Instance);
  188. this.oleUnderlyingDataObject = (System.Windows.IDataObject)innerDataField.GetValue(this.underlyingDataObject);
  189. this.getDataFromHGLOBALMethod = this.oleUnderlyingDataObject.GetType().GetMethod("GetDataFromHGLOBAL", BindingFlags.NonPublic | BindingFlags.Instance);
  190. }
  191. #endregion
  192. #region IDataObject Members
  193. /// <summary>
  194. /// Retrieves the data associated with the specified class type format.
  195. /// </summary>
  196. /// <param name="format">A <see cref="T:System.Type"></see> representing the format of the data to retrieve. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  197. /// <returns>
  198. /// The data associated with the specified format, or null.
  199. /// </returns>
  200. public object GetData(Type format)
  201. {
  202. return this.GetData(format.FullName);
  203. }
  204. /// <summary>
  205. /// Retrieves the data associated with the specified data format.
  206. /// </summary>
  207. /// <param name="format">The format of the data to retrieve. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  208. /// <returns>
  209. /// The data associated with the specified format, or null.
  210. /// </returns>
  211. public object GetData(string format)
  212. {
  213. return this.GetData(format, true);
  214. }
  215. /// <summary>
  216. /// Retrieves the data associated with the specified data format, using a Boolean to determine whether to convert the data to the format.
  217. /// </summary>
  218. /// <param name="format">The format of the data to retrieve. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  219. /// <param name="autoConvert">true to convert the data to the specified format; otherwise, false.</param>
  220. /// <returns>
  221. /// The data associated with the specified format, or null.
  222. /// </returns>
  223. public object GetData(string format, bool autoConvert)
  224. {
  225. //handle the "FileGroupDescriptor" and "FileContents" format request in this class otherwise pass through to underlying IDataObject
  226. switch (format)
  227. {
  228. case "FileGroupDescriptor":
  229. //override the default handling of FileGroupDescriptor which returns a
  230. //MemoryStream and instead return a string array of file names
  231. IntPtr fileGroupDescriptorAPointer = IntPtr.Zero;
  232. try
  233. {
  234. //use the underlying IDataObject to get the FileGroupDescriptor as a MemoryStream
  235. MemoryStream fileGroupDescriptorStream = (MemoryStream)this.underlyingDataObject.GetData("FileGroupDescriptor", autoConvert);
  236. byte[] fileGroupDescriptorBytes = new byte[fileGroupDescriptorStream.Length];
  237. fileGroupDescriptorStream.Read(fileGroupDescriptorBytes, 0, fileGroupDescriptorBytes.Length);
  238. fileGroupDescriptorStream.Close();
  239. //copy the file group descriptor into unmanaged memory
  240. fileGroupDescriptorAPointer = Marshal.AllocHGlobal(fileGroupDescriptorBytes.Length);
  241. Marshal.Copy(fileGroupDescriptorBytes, 0, fileGroupDescriptorAPointer, fileGroupDescriptorBytes.Length);
  242. ////marshal the unmanaged memory to to FILEGROUPDESCRIPTORA struct
  243. //FIX FROM - https://stackoverflow.com/questions/27173844/accessviolationexception-after-copying-a-file-from-inside-a-zip-archive-to-the-c
  244. int ITEMCOUNT = Marshal.ReadInt32(fileGroupDescriptorAPointer);
  245. //create a new array to store file names in of the number of items in the file group descriptor
  246. string[] fileNames = new string[ITEMCOUNT];
  247. //get the pointer to the first file descriptor
  248. IntPtr fileDescriptorPointer = (IntPtr)((long)fileGroupDescriptorAPointer + Marshal.SizeOf(ITEMCOUNT));
  249. //loop for the number of files acording to the file group descriptor
  250. for (int fileDescriptorIndex = 0; fileDescriptorIndex < ITEMCOUNT; fileDescriptorIndex++)
  251. {
  252. //marshal the pointer top the file descriptor as a FILEDESCRIPTORA struct and get the file name
  253. NativeMethods.FILEDESCRIPTORA fileDescriptor = (NativeMethods.FILEDESCRIPTORA)Marshal.PtrToStructure(fileDescriptorPointer, typeof(NativeMethods.FILEDESCRIPTORA));
  254. fileNames[fileDescriptorIndex] = fileDescriptor.cFileName;
  255. //move the file descriptor pointer to the next file descriptor
  256. fileDescriptorPointer = (IntPtr)((long)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
  257. }
  258. //return the array of filenames
  259. return fileNames;
  260. }
  261. finally
  262. {
  263. //free unmanaged memory pointer
  264. Marshal.FreeHGlobal(fileGroupDescriptorAPointer);
  265. }
  266. case "FileGroupDescriptorW":
  267. //override the default handling of FileGroupDescriptorW which returns a
  268. //MemoryStream and instead return a string array of file names
  269. IntPtr fileGroupDescriptorWPointer = IntPtr.Zero;
  270. try
  271. {
  272. //use the underlying IDataObject to get the FileGroupDescriptorW as a MemoryStream
  273. MemoryStream fileGroupDescriptorStream = (MemoryStream)this.underlyingDataObject.GetData("FileGroupDescriptorW");
  274. byte[] fileGroupDescriptorBytes = new byte[fileGroupDescriptorStream.Length];
  275. fileGroupDescriptorStream.Read(fileGroupDescriptorBytes, 0, fileGroupDescriptorBytes.Length);
  276. fileGroupDescriptorStream.Close();
  277. //copy the file group descriptor into unmanaged memory
  278. fileGroupDescriptorWPointer = Marshal.AllocHGlobal(fileGroupDescriptorBytes.Length);
  279. Marshal.Copy(fileGroupDescriptorBytes, 0, fileGroupDescriptorWPointer, fileGroupDescriptorBytes.Length);
  280. //marshal the unmanaged memory to to FILEGROUPDESCRIPTORW struct
  281. //FIX FROM - https://stackoverflow.com/questions/27173844/accessviolationexception-after-copying-a-file-from-inside-a-zip-archive-to-the-c
  282. int ITEMCOUNT = Marshal.ReadInt32(fileGroupDescriptorWPointer);
  283. //create a new array to store file names in of the number of items in the file group descriptor
  284. string[] fileNames = new string[ITEMCOUNT];
  285. //get the pointer to the first file descriptor
  286. IntPtr fileDescriptorPointer = (IntPtr)((long)fileGroupDescriptorWPointer + Marshal.SizeOf(ITEMCOUNT));
  287. //loop for the number of files acording to the file group descriptor
  288. for (int fileDescriptorIndex = 0; fileDescriptorIndex < ITEMCOUNT; fileDescriptorIndex++)
  289. {
  290. //marshal the pointer top the file descriptor as a FILEDESCRIPTORW struct and get the file name
  291. NativeMethods.FILEDESCRIPTORW fileDescriptor = (NativeMethods.FILEDESCRIPTORW)Marshal.PtrToStructure(fileDescriptorPointer, typeof(NativeMethods.FILEDESCRIPTORW));
  292. fileNames[fileDescriptorIndex] = fileDescriptor.cFileName;
  293. //move the file descriptor pointer to the next file descriptor
  294. fileDescriptorPointer = (IntPtr)((long)fileDescriptorPointer + Marshal.SizeOf(fileDescriptor));
  295. }
  296. //return the array of filenames
  297. return fileNames;
  298. }
  299. finally
  300. {
  301. //free unmanaged memory pointer
  302. Marshal.FreeHGlobal(fileGroupDescriptorWPointer);
  303. }
  304. case "FileContents":
  305. //override the default handling of FileContents which returns the
  306. //contents of the first file as a memory stream and instead return
  307. //a array of MemoryStreams containing the data to each file dropped
  308. //
  309. // FILECONTENTS requires a companion FILEGROUPDESCRIPTOR to be
  310. // available so we bail out if we don't find one in the data object.
  311. string fgdFormatName;
  312. if (GetDataPresent("FileGroupDescriptorW"))
  313. fgdFormatName = "FileGroupDescriptorW";
  314. else if (GetDataPresent("FileGroupDescriptor"))
  315. fgdFormatName = "FileGroupDescriptor";
  316. else
  317. return null;
  318. //get the array of filenames which lets us know how many file contents exist
  319. string[] fileContentNames = (string[])this.GetData(fgdFormatName);
  320. //create a MemoryStream array to store the file contents
  321. MemoryStream[] fileContents = new MemoryStream[fileContentNames.Length];
  322. //loop for the number of files acording to the file names
  323. for (int fileIndex = 0; fileIndex < fileContentNames.Length; fileIndex++)
  324. {
  325. //get the data at the file index and store in array
  326. fileContents[fileIndex] = this.GetData(format, fileIndex);
  327. }
  328. //return array of MemoryStreams containing file contents
  329. return fileContents;
  330. }
  331. //use underlying IDataObject to handle getting of data
  332. return this.underlyingDataObject.GetData(format, autoConvert);
  333. }
  334. /// <summary>
  335. /// Retrieves the data associated with the specified data format at the specified index.
  336. /// </summary>
  337. /// <param name="format">The format of the data to retrieve. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  338. /// <param name="index">The index of the data to retrieve.</param>
  339. /// <returns>
  340. /// A <see cref="MemoryStream"/> containing the raw data for the specified data format at the specified index.
  341. /// </returns>
  342. public MemoryStream GetData(string format, int index)
  343. {
  344. //create a FORMATETC struct to request the data with
  345. FORMATETC formatetc = new FORMATETC();
  346. formatetc.cfFormat = (short)DataFormats.GetDataFormat(format).Id;
  347. formatetc.dwAspect = DVASPECT.DVASPECT_CONTENT;
  348. formatetc.lindex = index;
  349. formatetc.ptd = new IntPtr(0);
  350. formatetc.tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_ISTORAGE | TYMED.TYMED_HGLOBAL;
  351. //create STGMEDIUM to output request results into
  352. STGMEDIUM medium = new STGMEDIUM();
  353. //using the Com IDataObject interface get the data using the defined FORMATETC
  354. this.comUnderlyingDataObject.GetData(ref formatetc, out medium);
  355. //retrieve the data depending on the returned store type
  356. switch (medium.tymed)
  357. {
  358. case TYMED.TYMED_ISTORAGE:
  359. //to handle a IStorage it needs to be written into a second unmanaged
  360. //memory mapped storage and then the data can be read from memory into
  361. //a managed byte and returned as a MemoryStream
  362. NativeMethods.IStorage iStorage = null;
  363. NativeMethods.IStorage iStorage2 = null;
  364. NativeMethods.ILockBytes iLockBytes = null;
  365. System.Runtime.InteropServices.ComTypes.STATSTG iLockBytesStat;
  366. try
  367. {
  368. //marshal the returned pointer to a IStorage object
  369. iStorage = (NativeMethods.IStorage)Marshal.GetObjectForIUnknown(medium.unionmember);
  370. Marshal.Release(medium.unionmember);
  371. //create a ILockBytes (unmanaged byte array) and then create a IStorage using the byte array as a backing store
  372. iLockBytes = NativeMethods.CreateILockBytesOnHGlobal(IntPtr.Zero, true);
  373. iStorage2 = NativeMethods.StgCreateDocfileOnILockBytes(iLockBytes, 0x00001012, 0);
  374. //copy the returned IStorage into the new IStorage
  375. iStorage.CopyTo(0, null, IntPtr.Zero, iStorage2);
  376. iLockBytes.Flush();
  377. iStorage2.Commit(0);
  378. //get the STATSTG of the ILockBytes to determine how many bytes were written to it
  379. iLockBytesStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
  380. iLockBytes.Stat(out iLockBytesStat, 1);
  381. int iLockBytesSize = (int)iLockBytesStat.cbSize;
  382. //read the data from the ILockBytes (unmanaged byte array) into a managed byte array
  383. byte[] iLockBytesContent = new byte[iLockBytesSize];
  384. iLockBytes.ReadAt(0, iLockBytesContent, iLockBytesContent.Length, null);
  385. //wrapped the managed byte array into a memory stream and return it
  386. return new MemoryStream(iLockBytesContent);
  387. }
  388. finally
  389. {
  390. //release all unmanaged objects
  391. Marshal.ReleaseComObject(iStorage2);
  392. Marshal.ReleaseComObject(iLockBytes);
  393. Marshal.ReleaseComObject(iStorage);
  394. }
  395. case TYMED.TYMED_ISTREAM:
  396. //to handle a IStream it needs to be read into a managed byte and
  397. //returned as a MemoryStream
  398. IStream iStream = null;
  399. System.Runtime.InteropServices.ComTypes.STATSTG iStreamStat;
  400. try
  401. {
  402. //marshal the returned pointer to a IStream object
  403. iStream = (IStream)Marshal.GetObjectForIUnknown(medium.unionmember);
  404. Marshal.Release(medium.unionmember);
  405. //get the STATSTG of the IStream to determine how many bytes are in it
  406. iStreamStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
  407. iStream.Stat(out iStreamStat, 0);
  408. int iStreamSize = (int)iStreamStat.cbSize;
  409. //read the data from the IStream into a managed byte array
  410. byte[] iStreamContent = new byte[iStreamSize];
  411. iStream.Read(iStreamContent, iStreamContent.Length, IntPtr.Zero);
  412. //wrapped the managed byte array into a memory stream and return it
  413. return new MemoryStream(iStreamContent);
  414. }
  415. finally
  416. {
  417. //release all unmanaged objects
  418. Marshal.ReleaseComObject(iStream);
  419. }
  420. case TYMED.TYMED_HGLOBAL:
  421. //to handle a HGlobal the exisitng "GetDataFromHGLOBAL" method is invoked via
  422. //reflection
  423. return (MemoryStream)this.getDataFromHGLOBALMethod.Invoke(this.oleUnderlyingDataObject, new object[] { DataFormats.GetDataFormat((short)formatetc.cfFormat).Name, medium.unionmember });
  424. }
  425. return null;
  426. }
  427. /// <summary>
  428. /// Determines whether data stored in this instance is associated with, or can be converted to, the specified format.
  429. /// </summary>
  430. /// <param name="format">A <see cref="T:System.Type"></see> representing the format for which to check. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  431. /// <returns>
  432. /// true if data stored in this instance is associated with, or can be converted to, the specified format; otherwise, false.
  433. /// </returns>
  434. public bool GetDataPresent(Type format)
  435. {
  436. return this.underlyingDataObject.GetDataPresent(format);
  437. }
  438. /// <summary>
  439. /// Determines whether data stored in this instance is associated with, or can be converted to, the specified format.
  440. /// </summary>
  441. /// <param name="format">The format for which to check. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  442. /// <returns>
  443. /// true if data stored in this instance is associated with, or can be converted to, the specified format; otherwise false.
  444. /// </returns>
  445. public bool GetDataPresent(string format)
  446. {
  447. return this.underlyingDataObject.GetDataPresent(format);
  448. }
  449. /// <summary>
  450. /// Determines whether data stored in this instance is associated with the specified format, using a Boolean value to determine whether to convert the data to the format.
  451. /// </summary>
  452. /// <param name="format">The format for which to check. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  453. /// <param name="autoConvert">true to determine whether data stored in this instance can be converted to the specified format; false to check whether the data is in the specified format.</param>
  454. /// <returns>
  455. /// true if the data is in, or can be converted to, the specified format; otherwise, false.
  456. /// </returns>
  457. public bool GetDataPresent(string format, bool autoConvert)
  458. {
  459. return this.underlyingDataObject.GetDataPresent(format, autoConvert);
  460. }
  461. /// <summary>
  462. /// Returns a list of all formats that data stored in this instance is associated with or can be converted to.
  463. /// </summary>
  464. /// <returns>
  465. /// An array of the names that represents a list of all formats that are supported by the data stored in this object.
  466. /// </returns>
  467. public string[] GetFormats()
  468. {
  469. return this.underlyingDataObject.GetFormats();
  470. }
  471. /// <summary>
  472. /// Gets a list of all formats that data stored in this instance is associated with or can be converted to, using a Boolean value to determine whether to retrieve all formats that the data can be converted to or only native data formats.
  473. /// </summary>
  474. /// <param name="autoConvert">true to retrieve all formats that data stored in this instance is associated with or can be converted to; false to retrieve only native data formats.</param>
  475. /// <returns>
  476. /// An array of the names that represents a list of all formats that are supported by the data stored in this object.
  477. /// </returns>
  478. public string[] GetFormats(bool autoConvert)
  479. {
  480. return this.underlyingDataObject.GetFormats(autoConvert);
  481. }
  482. /// <summary>
  483. /// Stores the specified data in this instance, using the class of the data for the format.
  484. /// </summary>
  485. /// <param name="data">The data to store.</param>
  486. public void SetData(object data)
  487. {
  488. this.underlyingDataObject.SetData(data);
  489. }
  490. /// <summary>
  491. /// Stores the specified data and its associated class type in this instance.
  492. /// </summary>
  493. /// <param name="format">A <see cref="T:System.Type"></see> representing the format associated with the data. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  494. /// <param name="data">The data to store.</param>
  495. public void SetData(Type format, object data)
  496. {
  497. this.underlyingDataObject.SetData(format, data);
  498. }
  499. /// <summary>
  500. /// Stores the specified data and its associated format in this instance.
  501. /// </summary>
  502. /// <param name="format">The format associated with the data. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  503. /// <param name="data">The data to store.</param>
  504. public void SetData(string format, object data)
  505. {
  506. this.underlyingDataObject.SetData(format, data);
  507. }
  508. /// <summary>
  509. /// Stores the specified data and its associated format in this instance, using a Boolean value to specify whether the data can be converted to another format.
  510. /// </summary>
  511. /// <param name="format">The format associated with the data. See <see cref="T:System.Windows.DataFormats"></see> for predefined formats.</param>
  512. /// <param name="autoConvert">true to allow the data to be converted to another format; otherwise, false.</param>
  513. /// <param name="data">The data to store.</param>
  514. public void SetData(string format, object data, bool autoConvert)
  515. {
  516. this.underlyingDataObject.SetData(format, data, autoConvert);
  517. }
  518. #endregion
  519. }
  520. }