Getting the filenames
Getting the filenames is easy. Just call:
dragEventArgs.Data.GetData("FileGroupDescriptorW")
this will return a MemoryStream
that contains a FILEGROUPDESCRIPTORA
structure. This can be parsed to get the filenames. Here and here are links to projects on CodeProject that show you two different ways to parse FILEGROUPDESCRIPTORA
in C#, so I won't go into detail here. I would probably use the technique described in the first project.
Getting the actual data
To get the actual data, you use the FileContents
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 System.Runtime.InteropServices.ComTypes.IDataObject
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.
The easiest solution is probably to use reflection to call private members of WPF's DataObject
. 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 RegisterDragDrop
function in ole32.dll
to register a custom IOleDropTarget
, then talk directly to the COM IDataObject
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.
Here is what to do to retrieve a FileContent for a particular index:
- Reflect on the actual class of the data object to find a method called "GetData" that takes four arguments
- If the method was not found, reflect again to find a field of type
System.Windows.IDataObject
, get its value, and go back to step 1 (recursion is safe here)
- Use
MethodInfo.Invoke
to call the "GetData" you found with arguments: "FileContents", false, ComTypes.DVASPECT, lindex
- Read the file data from the returned
MemoryStream
Here is the gist of the code to retrieve the file contents for a given index:
public MemoryStream GetFileContents(IDataObject dataObject, int index)
{
MethodInfo getData = (
from method in dataObject.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)
where method.Name=="GetData" && 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
});
}