拖动和从C#大型虚拟文件拖放到Windows资源管理器 [英] Drag and drop large virtual files from C# to Windows Explorer
问题描述
我有在作为FTP客户端,在远程服务器上列出文件,并允许用户下载他们一个部分一个C#WPF应用程序。我希望用户能够拖动并从文件列表到自己的机器(即到Windows资源管理器外壳)文件拖放。
I have a C# WPF application with a section that serves as an FTP client, listing files on a remote server and allowing the user to download them. I want the user to be able to drag and drop files from the file listing onto their own machine (i.e. into a Windows Explorer shell).
要做到这一点,我用的的从延迟的博客 VirtualFileDataObject代码,使用动作<流>
超载的SetData code>。这对小文件的伟大工程
To accomplish this, I used the VirtualFileDataObject code from Delay's blog, using the Action<Stream>
overload of SetData
. This works great on smaller files.
我的问题是:一些我处理的文件非常大(2+ GB),和路 VirtualFileDataObject
类处理流包括读取整个事情到内存中,从而可以最终抛出一个没有足够的存储错误对于那些非常大的文件。
My problem is: some of the files I'm dealing with are very large (2+ GB), and the way the VirtualFileDataObject
class handles the stream involves reading the entire thing into memory, which can end up throwing a "not enough storage" error for those very large files.
VirtualFileDataObject
代码的相关章节如下。 如何重写这个代码不要求整个流是在内存中?
The relevant section of the VirtualFileDataObject
code is below. How can I rewrite this code to not require the entire stream to be in memory?
public void SetData(short dataFormat, int index, Action<Stream> streamData) {
_dataObjects.Add(
new DataObject {
FORMATETC = new FORMATETC {
cfFormat = dataFormat,
ptd = IntPtr.Zero,
dwAspect = DVASPECT.DVASPECT_CONTENT,
lindex = index,
tymed = TYMED.TYMED_ISTREAM
},
GetData = () => {
// Create IStream for data
var ptr = IntPtr.Zero;
var iStream = NativeMethods.CreateStreamOnHGlobal(IntPtr.Zero, true);
if (streamData != null) {
// Wrap in a .NET-friendly Stream and call provided code to fill it
using (var stream = new IStreamWrapper(iStream)) {
streamData(stream);
}
}
// Return an IntPtr for the IStream
ptr = Marshal.GetComInterfaceForObject(iStream, typeof(IStream));
Marshal.ReleaseComObject(iStream);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
},
});
}
在特别的GetData本节
是罪魁祸首:
// Wrap in a .NET-friendly Stream and call provided code to fill it
using (var stream = new IStreamWrapper(iStream)) {
streamData(stream);
}
streamData code>是
动作<流>
我提供哪些实际的文件数据写入流。我代表刚刚打开一个文件并读取字节到提供的数据流。
streamData
is the Action<stream>
I provide which writes the actual file data to the stream. My delegate is just opening a file and reading the bytes into the provided stream.
有没有办法避免这最后一步,或许在某种程度上直接传递文件流是从资源管理器外壳看?我想是这样的指针在.NET FILESTREAM我有替换的IStream
...但我不知道有足够的了解COM互操作,甚至知道语法这样做。 !任何提示/方向,将不胜感激。
Is there a way to avoid this last step, perhaps somehow passing the file stream directly to be read from by the Explorer shell? I'm thinking something like replacing iStream
with a pointer to the .NET filestream I've got...but I don't know enough about COM interop to even know the syntax for doing that. Any tips/direction would be appreciated!
推荐答案
我得到了同样的问题,很容易解决,虽然;)
I got the same issue, easy to fix though ;)
的问题是,我们正在创造一个新的内存流,而并不需要它,因为我们已经有我们的。您可以在C#创建一个实现IStream的一个流包装:
The problem is that we are creating a new memory stream whereas it is not needed since we already have ours. You can create in c# an Stream wrapper that implements IStream:
/// <summary>
/// Simple class that exposes a read-only Stream as a IStream.
/// </summary>
private class StreamWrapper : IStream
{
private Stream _stream;
public StreamWrapper(Stream stream)
{
_stream = stream;
}
public void Read(byte[] pv, int cb, System.IntPtr pcbRead)
{
Marshal.WriteInt32(pcbRead, _stream.Read(pv, 0, cb));
}
public void Seek(long dlibMove, int dwOrigin, System.IntPtr plibNewPosition)
{
Marshal.WriteInt32(plibNewPosition, (int)_stream.Seek(dlibMove, (SeekOrigin)dwOrigin));
}
public void Clone(out IStream ppstm)
{
throw new NotImplementedException();
}
public void Commit(int grfCommitFlags)
{
throw new NotImplementedException();
}
public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
{
throw new NotImplementedException();
}
public void LockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Revert()
{
throw new NotImplementedException();
}
public void SetSize(long libNewSize)
{
throw new NotImplementedException();
}
public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag)
{
throw new NotImplementedException();
}
public void UnlockRegion(long libOffset, long cb, int dwLockType)
{
throw new NotImplementedException();
}
public void Write(byte[] pv, int cb, IntPtr pcbWritten)
{
throw new NotImplementedException();
}
}
然后在VirtualFileDataObject类,更改签名SetData方法,让你现在通过一个Stream:
And then in the VirtualFileDataObject class, change the signature of the SetData method so that you pass now a Stream:
public void SetData(short dataFormat, int index, Stream stream)
{
...
var iStream = new StreamWrapper(stream);
...
// Ensure the following line is commented out:
//Marshal.ReleaseComObject(iStream);
return new Tuple<IntPtr, int>(ptr, NativeMethods.S_OK);
...
}
现在,没有新的内存流将被创建
Now, no new memory stream will be created.
有关更多信息,请访问的 http://blogs.msdn.com/b/delay/archive /2009/11/04/creating-something-from-nothing-asynchronously-developer-friendly-virtual-file-implementation-for-net-improved.aspx#10496772 和阅读我的意见
For further info, go to http://blogs.msdn.com/b/delay/archive/2009/11/04/creating-something-from-nothing-asynchronously-developer-friendly-virtual-file-implementation-for-net-improved.aspx#10496772 and read my comments
这篇关于拖动和从C#大型虚拟文件拖放到Windows资源管理器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!