OutlookDataObject.cs 31 KB

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