Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p><strong>Getting the filenames</strong></p> <p>Getting the filenames is easy. Just call:</p> <pre><code>dragEventArgs.Data.GetData("FileGroupDescriptorW") </code></pre> <p>this will return a <code>MemoryStream</code> that contains a <code>FILEGROUPDESCRIPTORA</code> structure. This can be parsed to get the filenames. <a href="http://www.codeproject.com/KB/office/outlook_drag_drop_in_cs.aspx" rel="noreferrer">Here</a> and <a href="http://www.codeproject.com/KB/cs/testemaildragdrop.aspx?df=100&amp;forumid=51309&amp;exp=0&amp;select=1553102" rel="noreferrer">here</a> are links to projects on CodeProject that show you two different ways to parse <code>FILEGROUPDESCRIPTORA</code> in C#, so I won't go into detail here. I would probably use the technique described in the first project.</p> <p><strong>Getting the actual data</strong></p> <p>To get the actual data, you use the <code>FileContents</code> format. Unfortunately you either have to use reflection to access private methods, or write some COM interop yourself. The problem is that to get the data you must call the <code>System.Runtime.InteropServices.ComTypes.IDataObject</code> interface with a FORMATETC structure that has lindex set to the item index. Unfortunately System.Windows.DataObject's implementation always calls it with lindex=-1.</p> <p>The easiest solution is probably to use reflection to call private members of WPF's <code>DataObject</code>. Be forewarned, however, that this may break your code in future NET Framework versions. If that is completely unacceptable, your other option is to call the <code>RegisterDragDrop</code> function in <code>ole32.dll</code> to register a custom <code>IOleDropTarget</code>, then talk directly to the COM <code>IDataObject</code> that is passed in. This is not terribly difficult, but the reflection solution is much easier and will probably work for many versions of NET Framework, so that is what I will focus on.</p> <p>Here is what to do to retrieve a FileContent for a particular index:</p> <ol> <li>Reflect on the actual class of the data object to find a method called "GetData" that takes four arguments</li> <li>If the method was not found, reflect again to find a field of type <code>System.Windows.IDataObject</code>, get its value, and go back to step 1 (recursion is safe here)</li> <li>Use <code>MethodInfo.Invoke</code> to call the "GetData" you found with arguments: "FileContents", false, ComTypes.DVASPECT, lindex</li> <li>Read the file data from the returned <code>MemoryStream</code></li> </ol> <p>Here is the gist of the code to retrieve the file contents for a given index:</p> <pre><code>public MemoryStream GetFileContents(IDataObject dataObject, int index) { MethodInfo getData = ( from method in dataObject.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) where method.Name=="GetData" &amp;&amp; method.GetParameters().Length==4 select method ).FirstOrDefault(); if(getData==null) { FieldInfo innerField = ( from field in dataObject.GetType().GetFields() where field.FieldType == typeof(IDataObject) select field ).FirstOrDefault(); if(innerField==null) throw new Exception("Cannot get FileContents from DataObject of type" + dataObject.GetType()); return GetFileContents((IDataObject)innerField.GetValue(dataObject), index); } return (MemoryStream)getData.Invoke(dataObject, new object[] { "FileContents", false, System.Runtime.InteropServices.ComTypes.DVASPECT.DVASPECT_CONTENT, index }); } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload