DocumentUtils.cs 32 KB

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