QuickImage – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.IO;
3 using System.Reflection;
4 using System.Runtime.InteropServices;
5 using System.Runtime.InteropServices.ComTypes;
6 using System.Windows.Forms;
7  
8 namespace QuickImage.Utilities.Extensions
9 {
10 /// <summary>Helper methods for getting FileContents from DragDrop data.</summary>
11 /// <see cref="!:https://www.codeproject.com/Articles/28209/Outlook-Drag-and-Drop-in-C"/>
12 /// <remarks>https://stackoverflow.com/questions/49695870/how-can-i-retrieve-the-drag-drop-data-from-chrome-like-windows-explorer-does</remarks>
13 public static class IDataObjectExtensionMethods
14 {
15 /// <summary>Gets the array of FileNames from the FileGroupDescriptors format.</summary>
16 public static string[] GetFileContentNames(this System.Windows.Forms.IDataObject data)
17 {
18 var names = new string[data.GetFileContentCount()];
19  
20 if (names.Length != 0)
21 {
22 var bytes = data.GetFileGroupDescriptor().ToArray();
23 IntPtr fgdPtr = IntPtr.Zero;
24 try
25 {
26 fgdPtr = Marshal.AllocHGlobal(bytes.Length);
27  
28 int offset = Marshal.SizeOf(typeof(UInt32));
29 int size = Marshal.SizeOf(typeof(FILEDESCRIPTORW));
30  
31 for (int i = 0; i < names.Length; i++)
32 {
33 var fd = (FILEDESCRIPTORW)Marshal.PtrToStructure(fgdPtr + offset + (i * size), typeof(FILEDESCRIPTORW));
34 names[i] = fd.cFileName;
35 }
36  
37 }
38 finally
39 {
40 if (fgdPtr != IntPtr.Zero) Marshal.FreeHGlobal(fgdPtr);
41  
42 }
43 }
44  
45 return names;
46 }
47  
48 /// <summary>Gets the number of files available in the FileGroupDescriptor format.</summary>
49 public static int GetFileContentCount(this System.Windows.Forms.IDataObject data)
50 {
51 // File count is stored as an UInt32 in the FileGroupDescriptor format
52 MemoryStream ms = data.GetFileGroupDescriptor();
53 if (ms == null) return 0;
54  
55 using (var reader = new BinaryReader(ms))
56 {
57 return (int)reader.ReadUInt32(); // Assume this won't overflow!
58 }
59 }
60  
61 /// <summary>Gets the file content for the specified FileDescriptor index.</summary>
62 /// <param name="index">The index of the file content to retrieve.</param>
63 public static MemoryStream GetFileContent(this System.Windows.Forms.IDataObject data, int index)
64 {
65 // As this is indexed, "FileContent" is most likely null, so the COM IDataObject needs to be used
66 var comData = (System.Runtime.InteropServices.ComTypes.IDataObject)data;
67  
68 var formatetc = new FORMATETC()
69 {
70 cfFormat = (short)DataFormats.GetFormat("FileContents").Id,
71 dwAspect = DVASPECT.DVASPECT_CONTENT,
72 lindex = index,
73 ptd = IntPtr.Zero,
74 tymed = TYMED.TYMED_ISTREAM | TYMED.TYMED_HGLOBAL
75 };
76  
77 var medium = new STGMEDIUM();
78 comData.GetData(ref formatetc, out medium);
79  
80 switch (medium.tymed)
81 {
82 case TYMED.TYMED_HGLOBAL:
83 return data.GetFileContentFromHGlobal(medium);
84  
85 case TYMED.TYMED_ISTREAM:
86 return data.GetFileContentFromIStream(medium);
87  
88 default:
89 throw new InvalidOperationException($"Cannot get FileContent for {medium.tymed} TYMED.");
90  
91 }
92 }
93  
94 private static MemoryStream GetFileContentFromHGlobal(this System.Windows.Forms.IDataObject data, STGMEDIUM medium)
95 {
96 var innerDataField = data.GetType().GetField("innerData", BindingFlags.NonPublic | BindingFlags.Instance);
97 var oldData = (System.Windows.Forms.IDataObject)innerDataField.GetValue(data);
98  
99 var getDataFromHGLOBLALMethod = oldData.GetType().GetMethod("GetDataFromHGLOBLAL", BindingFlags.NonPublic | BindingFlags.Instance);
100  
101 return (MemoryStream)getDataFromHGLOBLALMethod.Invoke(oldData, new object[] { "FileContents", medium.unionmember });
102 }
103  
104 private static MemoryStream GetFileContentFromIStream(this System.Windows.Forms.IDataObject data, STGMEDIUM medium)
105 {
106 var iStream = (IStream)Marshal.GetObjectForIUnknown(medium.unionmember);
107 Marshal.Release(medium.unionmember);
108  
109 var iStreamStat = new System.Runtime.InteropServices.ComTypes.STATSTG();
110 iStream.Stat(out iStreamStat, 0);
111  
112 var content = new byte[(int)iStreamStat.cbSize];
113 iStream.Read(content, content.Length, IntPtr.Zero);
114  
115 return new MemoryStream(content);
116 }
117  
118 private static MemoryStream GetFileGroupDescriptor(this System.Windows.Forms.IDataObject data)
119 {
120 MemoryStream ms = null;
121 if (data.GetDataPresent("FileGroupDescriptorW"))
122 {
123 ms = (MemoryStream)data.GetData("FileGroupDescriptorW", true);
124 }
125  
126 return ms;
127 }
128  
129 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
130 private struct FILEDESCRIPTORW
131 {
132 public UInt32 dwFlags;
133 public Guid clsid;
134 public System.Drawing.Size sizel;
135 public System.Drawing.Point pointl;
136 public UInt32 dwFileAttributes;
137 public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
138 public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
139 public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
140 public UInt32 nFileSizeHigh;
141 public UInt32 nFileSizeLow;
142 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
143 public String cFileName;
144 }
145 }
146 }