|
Hi There
Sorry to keep messaging you but I know I'm so close to getting this working. I've managed to get the dragged file structure totally recreated in my temp dir. The data object correctly represents all the dragged items (even if they are folders) and windows tries to copy them from the temp directory when the dragdrop operation completes (dragging a folder out of my app into windows explorer), but it fails with an access denied error on the temp path. I've tried giving full permissions to everyone and it makes no difference, so I'm almost thinking the folder is still locked by my app in some way.
Any thoughts on this?
Thanks for your time
Mike
|
|
|
|
|
Update:
I've got this working with folders. I hope you don't mind me posting it here, but it'll be useful if anyone else has the same problems I did. I created a new subclass of DataObject called IFDDataObject. It's constructor is passed the full recursive list of files and folders contained in the dragged selection. All of these files and folders are downloaded to the temp directory (in their original hierarchy) the first time the 'getData' method is called, and the data itself is just set to a filedrop with the paths of the top level items in the temp directory.
public class IFDDataObject : DataObject, System.Runtime.InteropServices.ComTypes.IDataObject
{
bool filesHaveBeenDownloaded = false;
List<String> recursiveList = new List<String>();
public IFDDataObject(List<ItemToTransfer> recursiveList, string[] topLevelItems): base(DataFormats.FileDrop,topLevelItems)
{
this.recursiveList = recursiveList;
}
public override object GetData(string format, bool autoConvert)
{
if (!filesHaveBeenDownloaded && Control.MouseButtons == MouseButtons.None) {
}
return base.GetData(format, autoConvert);
}
}
I hope this helps someone in the future. Thanks for your code in the first place! Otherwise this wouldn't have been possible (at least for me )
|
|
|
|
|
Hi, can you help me out drop ftp files to explorer. but tried with local files 1st in the example in this article. i had used the DataObjectEx class in my application and i have added the below code in listview_Item Drag.
DataObjectEx.SelectedItem[] SelectedItems = new DataObjectEx.SelectedItem[3];
SelectedItems[0].FileName = "My Virtual File";
SelectedItems[0].SourceFileName = @"C:\Windows\System32\accwiz.exe";
SelectedItems[0].WriteTime = System.IO.File.GetLastWriteTime(SelectedItems[0].SourceFileName);
SelectedItems[0].FileSize = new System.IO.FileInfo(SelectedItems[0].SourceFileName).Length;
SelectedItems[1].FileName = "My Virtual File";
SelectedItems[1].SourceFileName = @"C:\Windows\System32\eventvwr.exe";
SelectedItems[1].WriteTime = System.IO.File.GetLastWriteTime(SelectedItems[1].SourceFileName);
SelectedItems[1].FileSize = new System.IO.FileInfo(SelectedItems[1].SourceFileName).Length;
SelectedItems[2].FileName = "My Virtual File";
SelectedItems[2].SourceFileName = @"C:\Windows\System32\ipconfig.exe";
SelectedItems[2].WriteTime = System.IO.File.GetLastWriteTime(SelectedItems[2].SourceFileName);
SelectedItems[2].FileSize = new System.IO.FileInfo(SelectedItems[2].SourceFileName).Length;
DataObjectEx dataObject = new DataObjectEx(SelectedItems);
dataObject.SetData(NativeMethods.CFSTR_FILEDESCRIPTORW, null);
dataObject.SetData(NativeMethods.CFSTR_FILECONTENTS, null);
dataObject.SetData(NativeMethods.CFSTR_PERFORMEDDROPEFFECT, null);
Clipboard.SetDataObject(dataObject);
DoDragDrop(new DataObject(DataFormats.FileDrop, SelectedItems), DragDropEffects.Copy | DragDropEffects.Move /* | DragDropEffects.Link */);
This code doesnot work for me!!! please help out
|
|
|
|
|
Hello,
I see it was a long time since the question, but i hope to help at least someone else that run into the same problem.
In order to create a virtual folder in the clipboard you have to set in FILEDESCRIPTOR folowing flags:
public const Int32 FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
Add if/else to the original method
private MemoryStream GetFileDescriptor(SelectedItem[] SelectedItems)
{
MemoryStream FileDescriptorMemoryStream = new MemoryStream();
FileDescriptorMemoryStream.Write
(BitConverter.GetBytes(SelectedItems.Length), 0, sizeof(UInt32));
FILEDESCRIPTOR FileDescriptor = new FILEDESCRIPTOR();
foreach (SelectedItem si in SelectedItems)
{
....
if(si.IsFolder)
{
FileDescriptor.dwFileAttributes = NativeMethods.FILE_ATTRIBUTE_DIRECTORY
| NativeMethods.FILE_ATTRIBUTE_NORMAL
| NativeMethods.FILE_ATTRIBUTE_ARCHIVE;
FileDescriptor.dwFlags = NativeMethods.FD_ATTRIBUTES
| NativeMethods.FD_PROGRESSUI;
}
else
{
FileDescriptor.dwFlags = NativeMethods.FD_FILESIZE
| NativeMethods.FD_PROGRESSUI;
FileDescriptor.dwFileAttributes = NativeMethods.FILE_ATTRIBUTE_NORMAL
| NativeMethods.FILE_ATTRIBUTE_ARCHIVE;
}
....
}
if you have files in those virtual folders you even don't need to put folders into DataObjectEx.
just set relative path in SourceFileName property of selecteditems.
something like:
selectedItem.FileName = "MyFolder\\myFile.txt"
windows will take care of this itself by creating folder "MyFodler" and pasting/droping there "myFile.txt"
modified on Tuesday, July 19, 2011 1:28 AM
|
|
|
|
|
thank you
my.hegemon wrote: public const Int32 FILE_ATTRIBUTE_DIRECTORY = 0x00000010;private MemoryStream GetFileContents(SelectedItem[] SelectedItems, Int32 FileNumber){... FileDescriptor.dwFlags = NativeMethods.FD_ATTRIBUTES; FileDescriptor.dwFileAttributes = NativeMethods.FILE_ATTRIBUTE_DIRECTORY; ....}
|
|
|
|
|
According to msdn (http://msdn.microsoft.com/en-us/library/ff362447.aspx[^]), the attribute FILE_ATTRIBUTE_NORMAL should be used only when there is no other attributes set.
Btw, was anyone able to change the creation date of a dragged&droped folder? I can't make it work. Files got their right creation and modification date but folders don't. Is there something else I should do/set to change the creation/modification dates for folders?
|
|
|
|
|
How would I save a file to the virtual drive ?
|
|
|
|
|
Hello,
Thank you for an interesting article.
As I saw in comments there are some problems with large files. Has the problem been solved? Secondly, is it possible to get the directory name where the file has been dropped? I could then write files to the directory because in case of large files (or many files) holding their contents in memory would harm performance.
Thanks.
|
|
|
|
|
This is a great article and one of the only ones I have seen for this topic. I have an app that is the visual representation of an FTP share. I want the user to be able to drag from the app to the desktop but the files are usually large and need to be FTP'ed down after the user drops them on the desktop. I can't create the files in memory first to be added to the FileContents because it would take too long.
Is there anyway to add a delayed rendering to this so that Windows will give a call back to my app and I can start the rendering of the file into a stream? I haven't seen any info on that either related to .Net.
Thanks!
|
|
|
|
|
Greetings Robert,
First off, great article!
I have a compression utility, where users can drag items from the archive (ListView) to the Windows file system. (Like WinZip..etc)
As in your situation, the files should only be extracted when needed. The problem is, I can't seem to get the drop target when the user releases the files, as my intention was to perform the extraction on the MouseUp event. Thus, my eternal searching has led me to your artical.
Would I be correct in assuming that I need to modify the private MemoryStream GetFileContents() method? For instance, I see your using the BinaryReader 'File.OpenRead()' object to pass/write the data to the MemorySream object. In my case, I think I would have to perform some sort of archival extraction, returning a similar object. Also, would there be a noticable delay creating FileContents for a considerably large file?
Cheers,
Greg
modified on Wednesday, April 9, 2008 7:14 PM
|
|
|
|
|
I just encountered that the solution is not working with every application that usually supports drag and drop from let's say Windows Explorer. At least in Lotus Notes you can open a new memo and drag and drop files from the Explorer to attach them to the email but it doesn't seem to work with the solution you described. Lotus Notes will display a "plus" sign indicating that drag and drop should work but when releasing the mouse nothing happens. In the code GetData is called with formats FileGroupDescriptorW and FileContents so everything looks fine there, no errors even when running in Debugger.
Have you ever noticed something similar, maybe with other applications?
|
|
|
|
|
First, I have to thank you for getting me closer to my goal than any article I've yet found. Now for the question (you knew it was coming, right?) The class you've generously provided works well for smallish files--up to about 12MB based on my admittedly limited testing. Unfortunately, if I try to drag and drop a larger virtual file (the one I tried was about 64MB) the system chokes and can't allocate the memory to create the HGLOBAL. From what I can gather, in instances like this you're supposed to use TYMED_ISTREAM, but I'm not quite sure how to fill the medium's unionmember when using IStreams. Any ideas?
|
|
|
|
|
Thanks for the comments. I tested on my end using a 64MB virtual file and it gives up for me too. I did some debugging and I see that when Windows Explorer is ready to accept the actual data it passes a TYMED of "TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE". My routine, based on the .NET Framework implementation of IDataObject.GetData favors HGLOBAL transfers over ISTREAM through its statement:
if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_NULL)
As a test, I came up with the following change to my code, checking for ISTREAM requests before HGLOBAL requests:
if (GetTymedUseable(formatetc.tymed))
{
if ((formatetc.tymed & TYMED.TYMED_ISTREAM) != TYMED.TYMED_NULL)
{
medium.tymed = TYMED.TYMED_ISTREAM;
((IComDataObject)this).GetDataHere(ref formatetc, ref medium);
}
else if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_NULL)
This did not solve the problem but appears to have shifted the problem to the file contents memory stream generation. I will have to experiment with a different transfer medium, perhaps going to an intermediate disk file.
In the meantime, hopefully there is a spark in the code above to give you a gem of inspiration!
|
|
|
|
|
That's about as far as I had gotten--apparantly, from what I can gather, after that point you need to set medium.unionmember to a pointer to the wrapped IStream you intend to pass the data with. There's an implementation of a wrapped IStream that seems to fit the bill at: http://www.koders.com/csharp/fid23BB694F4AFD5991BF05B935F90EB53D92087082.aspx?s=imageanimator[^]...but I'm still unsure of how to make that final linkage between the medium and the returned stream.
|
|
|
|
|
Well, I tried swapping the GlobalAlloc/GlobalFree out for VirtualAlloc/VirtualFree, using the IStream wrapper I linked earlier as the return type for GetFileContents()…the file descriptors are still being passed correctly, but I left those using GlobalAlloc/GlobalFree/MemoryStream, so that's not surprising. The file contents, however, don't seem to be making their way to the shell despite the VirtualAlloc returning what seems to be a valid memory address. Any idea how to attach any sort of debugger to this so I can at least see where things are going pearshaped? Or is this one of those "keeping trying different things until one of them works" situations?
The inserted snippet in GetData()
if (formatetc.cfFormat == (Int16)DataFormats.GetFormat(NativeMethods.CFSTR_FILECONTENTS).Id) {
if ((formatetc.tymed & TYMED.TYMED_ISTREAM) != TYMED.TYMED_NULL) {
medium.tymed = TYMED.TYMED_ISTREAM;
medium.unionmember = NativeMethods.VirtualAlloc(IntPtr.Zero, new UIntPtr(1), NativeMethods.MEM_COMMIT, NativeMethods.PAGE_READWRITE);
if (medium.unionmember == IntPtr.Zero) {
throw new OutOfMemoryException();
}
try {
((System.Runtime.InteropServices.ComTypes.IDataObject)this).GetDataHere(ref formatetc, ref medium);
return;
} catch {
NativeMethods.VirtualFree(medium.unionmember, new UIntPtr(1), NativeMethods.MEM_DECOMMIT);
medium.unionmember = IntPtr.Zero;
throw;
}
}
}
Any suggestions?
|
|
|
|
|
If you are using Visual Studio 2008, you can enable .NET Framework source code debugging and step into the GetDataHere() method. The instructions for configuring VS 2008 to access the source code can be found at [^]. Alternately, the source to the GetDataHere method could be extracted and added to the DataObjectEx class so that it can be debugged. When I get a chance, I'll try adding your code and see if I can step into the routines and see why it is failing.
|
|
|
|
|
Tried the debugging thing, but it didn't seem to recognize the recast call as an external call—it treated it as a symbol from my source, and didn't give any useful information that I could decode. It did, however, help me notice that I'd stupidly had the code checking for:
((formatetc.tymed & TYMED.TYMED_ISTREAM) != TYMED.TYMED_ISTREAM)
instead of what I should have been checking for:
((formatetc.tymed & TYMED.TYMED_ISTREAM) != TYMED.TYMED_NULL)
Unfortunately, that wasn't the problem. Back to the drawing board.
|
|
|
|
|
|
I would like to apologize to everyone who has emailed me over the months with questions. Due to employee resignations and layoffs my workload has been crazy and I have not had a chance to go back and revisit many of the issues brought up. The large files problem has been particularly tricky. Our quick "hack" until a resolution is found was to disallow the transfer of files beyond a certain size. This was not too bad for us because the limit is still quite high (several MB).
At this time, I am hoping to revisit the code in the new year. My firm is hoping for a few more sales by the end of the year and support for completing those has taken over a lot of my time. Heck, in this economy, I am glad to still have a job!
|
|
|
|
|
Nice to see that you have nit given up. Hope you will be able to solve it
|
|
|
|
|
Did someone have any luck with this issue ?
|
|
|
|
|
You have to properly implement the IStream interface (Google for IMemoryStream, for example), then before comparing to TYMED_HGLOBAL do
if (GetTymedUseable(formatetc.tymed))
{
if ((formatetc.tymed & TYMED.TYMED_ISTREAM) != TYMED.TYMED_NULL)
{
try
{
medium.tymed = TYMED.TYMED_ISTREAM;
IStream o = (IStream)GetData("FileContents", false);
medium.unionmember = Marshal.GetComInterfaceForObjectInContext(o, typeof(IStream));
return;
}
catch (Exception)
{
throw;
}
}
else
if ((formatetc.tymed & TYMED.TYMED_HGLOBAL) != TYMED.TYMED_NULL)
...
Easy... when you know
|
|
|
|
|
[DllImport("ole32.dll", PreserveSig = false)]
public static extern IStream CreateStreamOnHGlobal(IntPtr hGlobal, [MarshalAs(UnmanagedType.Bool)] bool fDeleteOnRelease);
must do it in
System.Runtime.InteropServices.ComTypes.IDataObject.SetData
not go to in .net objectdata
var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true);
use iStream.write to fill data
ptr = Marshal.GetComInterfaceForObject(iStream, typeof(IStream));
|
|
|
|
|
If you were able to shift the issue to the creating of the memory stream, could you just return a read only non-locking FileStream?
private FileStream GetFileContents(SelectedItem[] SelectedItems, Int32 FileNumber)
{
FileStream FileContentMemoryStream = null;
if (SelectedItems != null && FileNumber < SelectedItems.Length)
{
FileContentMemoryStream = new FileStream(SelectedItems[FileNumber].SourceFileName,FileMode.Open,FileAccess.Read);
}
return FileContentMemoryStream;
}
This could create an issue since it is not closed, but the fs could be made more global and cleaned up on the PERFORMEDDROPEFFECT.
Thanks,
Kevin
modified on Saturday, September 26, 2009 9:00 PM
|
|
|
|
|
I found one example on late-rendering on the web (don't know on which page) and was able to modify it that way, to support large-Files (I tested it with a 250 MB file) and IStream.
This example is written in for .NET 2.0 and far from being perfect but hopefully it will give some hints.
I will post the code of this two files
File 1 (Example.cs is 63 lines of code), File 2(VirtualFileDataObject.cs is 1125 lines of code)
// Example.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Delay;
using System.IO;
using System.Collections.Specialized;
using System.Runtime.InteropServices;
namespace DOExample
{
public partial class Example : Form
{
public Example()
{
InitializeComponent();
}
private void button1_MouseDown(object sender, MouseEventArgs e)
{
VirtualFileDataObject vfdo = new VirtualFileDataObject();
vfdo.IsAsynchronous = true;
vfdo.PreferredDropEffect = DragDropEffects.Copy;
VirtualFileDataObject.FileDescriptor fileDecriptor1 = new VirtualFileDataObject.FileDescriptor();
List<VirtualFileDataObject.FileDescriptor> fileDescriptors = new List<VirtualFileDataObject.FileDescriptor>();
string filePath = @"C:\Program Files (x86)\VMware\VMware Workstation\pkg\Tools.cab";
FileInfo fi = new FileInfo(filePath);
fileDecriptor1.Name = fi.Name;
fileDecriptor1.Length = fi.Length;
fileDecriptor1.ChangeTimeUtc = fi.LastWriteTimeUtc;
fileDecriptor1.ExtraInfo = fi.FullName;
fileDecriptor1.StreamContents = new VirtualFileDataObject.MyAction<Delay.VirtualFileDataObject.Tuple<Stream,string>>(GetFileData);
fileDescriptors.Add(fileDecriptor1);
vfdo.SetData(fileDescriptors);
vfdo.PreferredDropEffect = DragDropEffects.Copy;
button1.DoDragDrop(vfdo, DragDropEffects.Copy);
}
public void GetFileData(Delay.VirtualFileDataObject.Tuple<Stream, string> tuple)
{
using (FileStream sourceStream = new FileStream(tuple.Item2, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[1024 * 1024];
while (true)
{
int read = sourceStream.Read(buffer, 0, buffer.Length);
if (read <= 0)
break;
tuple.Item1.Write(buffer, 0, read);
}
}
}
}
}
------------------------------------------------------------------------------------------
// VirtualFileDataObject.cs
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Windows.Forms;
using System.Collections.Specialized;
namespace Delay
{
public class VirtualFileDataObject : System.Runtime.InteropServices.ComTypes.IDataObject, IAsyncOperation
{
public bool IsAsynchronous { get; set; }
private static short FILECONTENTS = (short)(DataFormats.GetFormat(NativeMethods.CFSTR_FILECONTENTS).Id);
private static short FILEDESCRIPTORW = (short)(DataFormats.GetFormat(NativeMethods.CFSTR_FILEDESCRIPTORW).Id);
private static short PASTESUCCEEDED = (short)(DataFormats.GetFormat(NativeMethods.CFSTR_PASTESUCCEEDED).Id);
private static short PERFORMEDDROPEFFECT = (short)(DataFormats.GetFormat(NativeMethods.CFSTR_PERFORMEDDROPEFFECT).Id);
private static short PREFERREDDROPEFFECT = (short)(DataFormats.GetFormat(NativeMethods.CFSTR_PREFERREDDROPEFFECT).Id);
private List<DataObject> _dataObjects = new List<DataObject>();
private bool _inOperation;
private Action<VirtualFileDataObject> _startAction;
private Action<VirtualFileDataObject> _endAction;
public VirtualFileDataObject()
{
IsAsynchronous = true;
}
public VirtualFileDataObject(Action<VirtualFileDataObject> startAction, Action<VirtualFileDataObject> endAction)
: this()
{
_startAction = startAction;
_endAction = endAction;
}
#region IDataObject Members
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
int System.Runtime.InteropServices.ComTypes.IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
{
Marshal.ThrowExceptionForHR(NativeMethods.OLE_E_ADVISENOTSUPPORTED);
throw new NotImplementedException();
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
void System.Runtime.InteropServices.ComTypes.IDataObject.DUnadvise(int connection)
{
Marshal.ThrowExceptionForHR(NativeMethods.OLE_E_ADVISENOTSUPPORTED);
throw new NotImplementedException();
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
int System.Runtime.InteropServices.ComTypes.IDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise)
{
Marshal.ThrowExceptionForHR(NativeMethods.OLE_E_ADVISENOTSUPPORTED);
throw new NotImplementedException();
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
IEnumFORMATETC System.Runtime.InteropServices.ComTypes.IDataObject.EnumFormatEtc(DATADIR direction)
{
if (direction == DATADIR.DATADIR_GET)
{
if (0 == _dataObjects.Count)
{
throw new InvalidOperationException("VirtualFileDataObject requires at least one data object to enumerate.");
}
IEnumFORMATETC enumerator;
List<FORMATETC> formatetcs = new List<FORMATETC>(_dataObjects.Count);
foreach (DataObject d in _dataObjects)
{
formatetcs.Add(d.FORMATETC);
}
if (NativeMethods.SUCCEEDED(NativeMethods.SHCreateStdEnumFmtEtc((uint)(_dataObjects.Count), formatetcs.ToArray(), out enumerator)))
{
return enumerator;
}
Marshal.ThrowExceptionForHR(NativeMethods.E_FAIL);
}
throw new NotImplementedException();
}
int System.Runtime.InteropServices.ComTypes.IDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
{
throw new NotImplementedException();
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
void System.Runtime.InteropServices.ComTypes.IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
{
medium = new STGMEDIUM();
var hr = ((System.Runtime.InteropServices.ComTypes.IDataObject)this).QueryGetData(ref format);
if (NativeMethods.SUCCEEDED(hr))
{
DataObject dataObject = null;
foreach (DataObject d in _dataObjects)
{
if ((d.FORMATETC.cfFormat == format.cfFormat) &&
(d.FORMATETC.dwAspect == format.dwAspect) &&
(0 != (d.FORMATETC.tymed & format.tymed) &&
(d.FORMATETC.lindex == format.lindex)))
{
dataObject = d;
break;
}
}
if (dataObject != null)
{
if (!IsAsynchronous && (FILEDESCRIPTORW == dataObject.FORMATETC.cfFormat) && !_inOperation)
{
_inOperation = true;
if (null != _startAction)
{
_startAction(this);
}
}
medium.tymed = dataObject.FORMATETC.tymed;
var result = dataObject.GetData();
hr = result.Item2;
if (NativeMethods.SUCCEEDED(hr))
{
medium.unionmember = result.Item1;
}
}
else
{
hr = NativeMethods.DV_E_FORMATETC;
}
}
if (!NativeMethods.SUCCEEDED(hr))
{
Marshal.ThrowExceptionForHR(hr);
}
}
void System.Runtime.InteropServices.ComTypes.IDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
{
throw new NotImplementedException();
}
int System.Runtime.InteropServices.ComTypes.IDataObject.QueryGetData(ref FORMATETC format)
{
List<DataObject> formatMatches = new List<DataObject>();
foreach (DataObject d in _dataObjects)
{
if (d.FORMATETC.cfFormat == format.cfFormat)
{
formatMatches.Add(d);
}
}
if (formatMatches.Count == 0)
{
return NativeMethods.DV_E_FORMATETC;
}
List<DataObject> tymedMatches = new List<DataObject>();
foreach (DataObject d in formatMatches)
{
if ((d.FORMATETC.tymed & format.tymed) != 0)
{
tymedMatches.Add(d);
}
}
if (tymedMatches.Count == 0)
{
return NativeMethods.DV_E_TYMED;
}
List<DataObject> aspectMatches = new List<DataObject>();
foreach (DataObject d in tymedMatches)
{
if (d.FORMATETC.dwAspect == format.dwAspect)
{
aspectMatches.Add(d);
}
}
if (aspectMatches.Count == 0)
{
return NativeMethods.DV_E_DVASPECT;
}
return NativeMethods.S_OK;
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
void System.Runtime.InteropServices.ComTypes.IDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
{
var handled = false;
if ((formatIn.dwAspect == DVASPECT.DVASPECT_CONTENT) &&
(formatIn.tymed == TYMED.TYMED_HGLOBAL) &&
(medium.tymed == formatIn.tymed))
{
var ptr = NativeMethods.GlobalLock(medium.unionmember);
if (IntPtr.Zero != ptr)
{
try
{
var length = NativeMethods.GlobalSize(ptr).ToInt32();
byte[] data = new byte[length];
Marshal.Copy(ptr, data, 0, length);
SetData(formatIn.cfFormat, data);
handled = true;
}
finally
{
NativeMethods.GlobalUnlock(medium.unionmember);
}
}
if (release)
{
Marshal.FreeHGlobal(medium.unionmember);
}
}
if (!IsAsynchronous && (PERFORMEDDROPEFFECT == formatIn.cfFormat) && _inOperation)
{
if (null != _endAction)
{
_endAction(this);
}
_inOperation = false;
}
if (!handled)
{
throw new NotImplementedException();
}
}
#endregion
public void SetData(StringCollection fileDropList)
{
DataObject dataObj = new DataObject();
}
public void SetData(short dataFormat, byte[] data)
{
_dataObjects.Add(
new DataObject
{
FORMATETC = new FORMATETC
{
cfFormat = dataFormat,
ptd = IntPtr.Zero,
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = -1,
tymed = TYMED.TYMED_HGLOBAL
},
GetData = () =>
{
var dataArray = data;
var ptr = Marshal.AllocHGlobal(dataArray.Length);
Marshal.Copy(dataArray, 0, ptr, dataArray.Length);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
});
}
public void SetData(short dataFormat, int index, MyAction<Tuple<Stream,string>> streamAndId, FileDescriptor fileDescriptor)
{
_dataObjects.Add(
new DataObject
{
FORMATETC = new FORMATETC
{
cfFormat = dataFormat,
ptd = IntPtr.Zero,
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = index,
tymed = TYMED.TYMED_ISTREAM
},
GetData = () =>
{
var ptr = IntPtr.Zero;
var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true);
if (streamAndId != null)
{
using (var stream = new IStreamWrapper(iStream))
{
streamAndId.Invoke(new Tuple<Stream,string>(stream, fileDescriptor.ExtraInfo));
}
}
ptr = Marshal.GetComInterfaceForObject(iStream, typeof(IStream));
Marshal.ReleaseComObject(iStream);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
});
}
public void SetData(List<FileDescriptor> fileDescriptors)
{
var bytes = new List<byte>();
bytes.AddRange(StructureBytes(new NativeMethods.FILEGROUPDESCRIPTOR { cItems = (uint)(fileDescriptors.Count) }));
foreach (var fileDescriptor in fileDescriptors)
{
var FILEDESCRIPTOR = new NativeMethods.FILEDESCRIPTOR
{
cFileName = fileDescriptor.Name,
};
if (fileDescriptor.ChangeTimeUtc.HasValue)
{
FILEDESCRIPTOR.dwFlags |= NativeMethods.FD_CREATETIME | NativeMethods.FD_WRITESTIME;
var changeTime = fileDescriptor.ChangeTimeUtc.Value.ToLocalTime().ToFileTime();
var changeTimeFileTime = new System.Runtime.InteropServices.ComTypes.FILETIME
{
dwLowDateTime = (int)(changeTime & 0xffffffff),
dwHighDateTime = (int)(changeTime >> 32),
};
FILEDESCRIPTOR.ftLastWriteTime = changeTimeFileTime;
FILEDESCRIPTOR.ftCreationTime = changeTimeFileTime;
}
if (fileDescriptor.Length.HasValue)
{
FILEDESCRIPTOR.dwFlags |= NativeMethods.FD_FILESIZE;
FILEDESCRIPTOR.nFileSizeLow = (uint)(fileDescriptor.Length & 0xffffffff);
FILEDESCRIPTOR.nFileSizeHigh = (uint)(fileDescriptor.Length >> 32);
}
bytes.AddRange(StructureBytes(FILEDESCRIPTOR));
}
SetData(FILEDESCRIPTORW, bytes.ToArray());
var index = 0;
foreach (var fileDescriptor in fileDescriptors)
{
SetData(FILECONTENTS, index, fileDescriptor.StreamContents, fileDescriptor);
index++;
}
}
public DragDropEffects? PasteSucceeded
{
get { return GetDropEffect(PASTESUCCEEDED); }
set
{
List<byte> data = new List<byte>(BitConverter.GetBytes((UInt32)value));
SetData(PASTESUCCEEDED, data.ToArray());
}
}
public DragDropEffects? PerformedDropEffect
{
get { return GetDropEffect(PERFORMEDDROPEFFECT); }
set
{
List<byte> data = new List<byte>(BitConverter.GetBytes((UInt32)value));
SetData(PERFORMEDDROPEFFECT, data.ToArray());
}
}
public DragDropEffects? PreferredDropEffect
{
get { return GetDropEffect(PREFERREDDROPEFFECT); }
set
{
List<byte> data = new List<byte>(BitConverter.GetBytes((UInt32)value));
SetData(PREFERREDDROPEFFECT, data.ToArray());
}
}
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
private DragDropEffects? GetDropEffect(short format)
{
DataObject dataObject = null;
foreach (DataObject d in _dataObjects)
{
if ((format == d.FORMATETC.cfFormat) &&
(DVASPECT.DVASPECT_CONTENT == d.FORMATETC.dwAspect) &&
(TYMED.TYMED_HGLOBAL == d.FORMATETC.tymed))
{
dataObject = d;
}
}
if (null != dataObject)
{
var result = dataObject.GetData();
if (NativeMethods.SUCCEEDED(result.Item2))
{
var ptr = NativeMethods.GlobalLock(result.Item1);
if (IntPtr.Zero != ptr)
{
try
{
var length = NativeMethods.GlobalSize(ptr).ToInt32();
if (4 == length)
{
var data = new byte[length];
Marshal.Copy(ptr, data, 0, length);
return (DragDropEffects)(BitConverter.ToUInt32(data, 0));
}
}
finally
{
NativeMethods.GlobalUnlock(result.Item1);
}
}
}
}
return null;
}
#region IAsyncOperation Members
void IAsyncOperation.SetAsyncMode(int fDoOpAsync)
{
IsAsynchronous = !(NativeMethods.VARIANT_FALSE == fDoOpAsync);
}
void IAsyncOperation.GetAsyncMode(out int pfIsOpAsync)
{
pfIsOpAsync = IsAsynchronous ? NativeMethods.VARIANT_TRUE : NativeMethods.VARIANT_FALSE;
}
void IAsyncOperation.StartOperation(IBindCtx pbcReserved)
{
_inOperation = true;
if (null != _startAction)
{
_startAction(this);
}
}
void IAsyncOperation.InOperation(out int pfInAsyncOp)
{
pfInAsyncOp = _inOperation ? NativeMethods.VARIANT_TRUE : NativeMethods.VARIANT_FALSE;
}
void IAsyncOperation.EndOperation(int hResult, IBindCtx pbcReserved, uint dwEffects)
{
if (null != _endAction)
{
_endAction(this);
}
_inOperation = false;
}
#endregion
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Method doesn't decrease security.")]
private static IEnumerable<byte> StructureBytes(object source)
{
var size = Marshal.SizeOf(source.GetType());
var ptr = Marshal.AllocHGlobal(size);
var bytes = new byte[size];
try
{
Marshal.StructureToPtr(source, ptr, false);
Marshal.Copy(ptr, bytes, 0, size);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return bytes;
}
public delegate void MyAction<T>(T obj);
[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Justification = "Deliberate to provide obvious coupling.")]
public class FileDescriptor
{
public string Name { get; set; }
public Int64? Length { get; set; }
public DateTime? ChangeTimeUtc { get; set; }
public MyAction<Tuple<Stream, string>> StreamContents { get; set; }
public string ExtraInfo { get; set; }
}
private class DataObject
{
public FORMATETC FORMATETC { get; set; }
public Func<Tuple<IntPtr, int>> GetData { get; set; }
}
public class Tuple<T1, T2>
{
public T1 Item1 { get; private set; }
public T2 Item2 { get; private set; }
public Tuple(T1 item1, T2 item2)
{
Item1 = item1;
Item2 = item2;
}
}
public delegate TResult Func<TResult>();
private class IStreamWrapper : Stream
{
private IStream _iStream;
public IStreamWrapper(IStream iStream)
{
_iStream = iStream;
}
public override bool CanRead
{
get { return false; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
throw new NotImplementedException();
}
public override long Length
{
get { throw new NotImplementedException(); }
}
public override long Position
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
if (offset == 0)
{
_iStream.Write(buffer, count, IntPtr.Zero);
}
else
{
ArraySegment<byte> buf = new ArraySegment<byte>(buffer, offset, count);
_iStream.Write(buf.Array, count, IntPtr.Zero);
}
}
}
[SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "dragSource", Justification = "Parameter is present so the signature matches that of System.Windows.DragDrop.DoDragDrop.")]
public static DragDropEffects DoDragDrop(object dragSource, System.Runtime.InteropServices.ComTypes.IDataObject dataObject, DragDropEffects allowedEffects)
{
int[] finalEffect = new int[1];
try
{
NativeMethods.DoDragDrop(dataObject, new DropSource(), (int)allowedEffects, finalEffect);
}
finally
{
var virtualFileDataObject = dataObject as VirtualFileDataObject;
if ((null != virtualFileDataObject) && !virtualFileDataObject.IsAsynchronous && virtualFileDataObject._inOperation)
{
if (null != virtualFileDataObject._endAction)
{
virtualFileDataObject._endAction(virtualFileDataObject);
}
virtualFileDataObject._inOperation = false;
}
}
return (DragDropEffects)(finalEffect[0]);
}
private class DropSource : NativeMethods.IDropSource
{
public int QueryContinueDrag(int fEscapePressed, uint grfKeyState)
{
var escapePressed = (0 != fEscapePressed);
var keyStates = (DragDropKeyStates)grfKeyState;
if (escapePressed)
{
return NativeMethods.DRAGDROP_S_CANCEL;
}
else if (DragDropKeyStates.None == (keyStates & DragDropKeyStates.LeftMouseButton))
{
return NativeMethods.DRAGDROP_S_DROP;
}
return NativeMethods.S_OK;
}
public int GiveFeedback(uint dwEffect)
{
return NativeMethods.DRAGDROP_S_USEDEFAULTCURSORS;
}
}
public static class NativeMethods
{
public const int DRAGDROP_S_DROP = 0x00040100;
public const int DRAGDROP_S_CANCEL = 0x00040101;
public const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102;
public const int DV_E_DVASPECT = -2147221397;
public const int DV_E_FORMATETC = -2147221404;
public const int DV_E_TYMED = -2147221399;
public const int E_FAIL = -2147467259;
public const uint FD_CREATETIME = 0x00000008;
public const uint FD_WRITESTIME = 0x00000020;
public const uint FD_FILESIZE = 0x00000040;
public const int OLE_E_ADVISENOTSUPPORTED = -2147221501;
public const int S_OK = 0;
public const int S_FALSE = 1;
public const int VARIANT_FALSE = 0;
public const int VARIANT_TRUE = -1;
public const string CFSTR_FILECONTENTS = "FileContents";
public const string CFSTR_FILEDESCRIPTORW = "FileGroupDescriptorW";
public const string CFSTR_PASTESUCCEEDED = "Paste Succeeded";
public const string CFSTR_PERFORMEDDROPEFFECT = "Performed DropEffect";
public const string CFSTR_PREFERREDDROPEFFECT = "Preferred DropEffect";
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "Structure exists for interop.")]
[StructLayout(LayoutKind.Sequential)]
public struct FILEGROUPDESCRIPTOR
{
public UInt32 cItems;
}
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "Structure exists for interop.")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct FILEDESCRIPTOR
{
public UInt32 dwFlags;
public Guid clsid;
public Int32 sizelcx;
public Int32 sizelcy;
public Int32 pointlx;
public Int32 pointly;
public UInt32 dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public UInt32 nFileSizeHigh;
public UInt32 nFileSizeLow;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
}
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "Structure exists for interop.")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct POINT
{
public Int32 X;
public Int32 Y;
public POINT(int x, int y)
{
this.X = x;
this.Y = y;
}
}
[SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "Structure exists for interop.")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DROPFILES
{
public int pFiles;
public POINT point;
public bool fNC;
public bool fWide;
}
[ComImport]
[Guid("00000121-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDropSource
{
[PreserveSig]
int QueryContinueDrag(int fEscapePressed, uint grfKeyState);
[PreserveSig]
int GiveFeedback(uint dwEffect);
}
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Justification = "Win32 API.")]
[DllImport("shell32.dll")]
public static extern int SHCreateStdEnumFmtEtc(uint cfmt, FORMATETC[] afmt, out IEnumFORMATETC ppenumFormatEtc);
[return: MarshalAs(UnmanagedType.Interface)]
[DllImport("ole32.dll", PreserveSig = false)]
public static extern IStream CreateStreamOnHGlobal(IntPtr hGlobal, [MarshalAs(UnmanagedType.Bool)] bool fDeleteOnRelease);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
public static extern void DoDragDrop(System.Runtime.InteropServices.ComTypes.IDataObject dataObject, IDropSource dropSource, int allowedEffects, int[] finalEffect);
[DllImport("kernel32.dll")]
public static extern IntPtr GlobalLock(IntPtr hMem);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll")]
public static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll")]
public static extern IntPtr GlobalSize(IntPtr handle);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam,
IntPtr lParam);
public const uint WM_DROPFILES = 0x0233;
public static bool SUCCEEDED(int hr)
{
return (0 <= hr);
}
}
}
[ComImport]
[Guid("3D8B0590-F691-11d2-8EA9-006097DF5BD4")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IAsyncOperation
{
void SetAsyncMode([In] Int32 fDoOpAsync);
void GetAsyncMode([Out] out Int32 pfIsOpAsync);
void StartOperation([In] IBindCtx pbcReserved);
void InOperation([Out] out Int32 pfInAsyncOp);
void EndOperation([In] Int32 hResult, [In] IBindCtx pbcReserved, [In] UInt32 dwEffects);
}
[Flags]
public enum DragDropKeyStates
{
None = 0,
LeftMouseButton = 1,
RightMouseButton = 2,
ShiftKey = 4,
ControlKey = 8,
MiddleMouseButton = 16,
AltKey = 32,
}
}
|
|
|
|
|